Compare commits
104 Commits
v0.7.0
...
feature/dy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17261622c6 | ||
|
|
dff9e53658 | ||
|
|
0bf709a339 | ||
|
|
c245949ea4 | ||
|
|
c27f8baa31 | ||
|
|
f51f519c31 | ||
|
|
55a485ee67 | ||
|
|
ba6e8e1349 | ||
|
|
75d30fb56d | ||
|
|
4abcf238a0 | ||
|
|
c05b4211fa | ||
|
|
337b8bb36b | ||
|
|
97433fc7d1 | ||
|
|
fc16ed54b3 | ||
|
|
8cbde13139 | ||
|
|
646a534dfd | ||
|
|
eda816ad90 | ||
|
|
c4bb60299e | ||
|
|
3cfd0cf86b | ||
|
|
8b3fa3ed01 | ||
|
|
a0f91ed0fd | ||
|
|
77590b31a6 | ||
|
|
ce27749705 | ||
|
|
b239bb6df2 | ||
|
|
28379dc0f6 | ||
|
|
c86c0f2ae6 | ||
|
|
bbe27c6b53 | ||
|
|
21916ab869 | ||
|
|
778e766f6b | ||
|
|
5d1fdd0e81 | ||
|
|
f5208d1641 | ||
|
|
a7beb0df18 | ||
|
|
1ec46ee641 | ||
|
|
3ba0600694 | ||
|
|
a449a88395 | ||
|
|
4bf03266a9 | ||
|
|
4bceac3d56 | ||
|
|
350396d70f | ||
|
|
ad3afae315 | ||
|
|
0dc86f2a0d | ||
|
|
2b3c276f33 | ||
|
|
8bf2425c4a | ||
|
|
72530ccb15 | ||
|
|
da14fd123e | ||
|
|
f215b98f74 | ||
|
|
b5662bfd25 | ||
|
|
3b3857548c | ||
|
|
1e2e154747 | ||
|
|
2d8d4fd200 | ||
|
|
459b40ad57 | ||
|
|
90cf1e2220 | ||
|
|
692e0d8656 | ||
|
|
3ab1a6b170 | ||
|
|
069bc63d1f | ||
|
|
373121c455 | ||
|
|
936b12a977 | ||
|
|
2a4c286f2b | ||
|
|
bfab4f085e | ||
|
|
201e7191ef | ||
|
|
9fa588566f | ||
|
|
ed95574c24 | ||
|
|
0c777bc62f | ||
|
|
1d7d5e8015 | ||
|
|
95627ba43c | ||
|
|
841ac41e36 | ||
|
|
a46d8bce37 | ||
|
|
4052911ac4 | ||
|
|
3a7a18011c | ||
|
|
15e2f8abf3 | ||
|
|
6b43ddc8d7 | ||
|
|
508058c3d7 | ||
|
|
4c9ff44b1c | ||
|
|
c092e07832 | ||
|
|
abe7fe37d0 | ||
|
|
cca8e8b3ad | ||
|
|
7c194950bb | ||
|
|
723f7d0330 | ||
|
|
274891854f | ||
|
|
94b2a79f79 | ||
|
|
d2a6113fb7 | ||
|
|
55c88dd943 | ||
|
|
42db1e8899 | ||
|
|
38ca7004a6 | ||
|
|
8817766469 | ||
|
|
e250aaef30 | ||
|
|
7b29ba7d23 | ||
|
|
dc30437ce7 | ||
|
|
2c2398b549 | ||
|
|
bce01591f3 | ||
|
|
847ef1ccfe | ||
|
|
6fa9b33ac0 | ||
|
|
df6d5b3b16 | ||
|
|
4884a624d9 | ||
|
|
ea587076ed | ||
|
|
0eddb002f0 | ||
|
|
8f968f4954 | ||
|
|
094b54d728 | ||
|
|
4125175870 | ||
|
|
1cb8f1258d | ||
|
|
f05a1d3310 | ||
|
|
71069cb38b | ||
|
|
393db1e792 | ||
|
|
0a097ec7d3 | ||
|
|
ada660deeb |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -3,7 +3,7 @@
|
||||
/build*
|
||||
*.bak
|
||||
tags
|
||||
jsix.log
|
||||
*.log
|
||||
*.out
|
||||
*.o
|
||||
*.a
|
||||
@@ -13,3 +13,6 @@ sysroot
|
||||
__pycache__
|
||||
/venv
|
||||
compile_commands.json
|
||||
buddy_allocs.txt
|
||||
frame_allocs.txt
|
||||
heap_allocs.txt
|
||||
|
||||
19
.vscode/c_cpp_properties.json
vendored
Normal file
19
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/src/libraries/**",
|
||||
"${workspaceFolder}/build/**",
|
||||
"${workspaceFolder}/sysroot/include"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/clang",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "linux-clang-x64",
|
||||
"compileCommands": "${workspaceFolder}/compile_commands.json"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
32
.vscode/launch.json
vendored
Normal file
32
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "QEMU Debug Server",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/jsix.elf",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"logging": {
|
||||
//"engineLogging": true,
|
||||
"programOutput": true
|
||||
},
|
||||
"stopAtConnect": true,
|
||||
"stopAtEntry": false,
|
||||
|
||||
"setupCommands": [
|
||||
{"text": "dashboard -enabled off", "ignoreFailures": true}
|
||||
],
|
||||
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerServerAddress": "localhost:1234",
|
||||
"debugServerPath": "${workspaceFolder}/qemu.sh",
|
||||
"debugServerArgs": "--debug --no-build",
|
||||
"serverLaunchTimeout": 5000,
|
||||
}
|
||||
]
|
||||
}
|
||||
45
.vscode/tasks.json
vendored
45
.vscode/tasks.json
vendored
@@ -1,28 +1,41 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "0.1.0",
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "make",
|
||||
"command": "make.bat",
|
||||
"isBuildCommand": true
|
||||
"label": "Ninja",
|
||||
"type": "shell",
|
||||
"command": "source ${workspaceFolder}/venv/bin/activate.fish; ninja",
|
||||
"detail": "Build the project",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"taskName": "clean",
|
||||
"command": "make.bat",
|
||||
"args": [ "clean" ],
|
||||
"isShellCommand": true
|
||||
"label": "Run QEMU",
|
||||
"command": "./qemu.sh",
|
||||
"args": [ "--no-build", "--kvm" ],
|
||||
"dependsOn": ["Ninja"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
},
|
||||
{
|
||||
"taskName": "qemu (windowed)",
|
||||
"command": "qemu-win.bat",
|
||||
"showOutput": "never",
|
||||
"isTestCommand": true
|
||||
},
|
||||
{
|
||||
"taskName": "qemu",
|
||||
"command": "qemu.bat"
|
||||
"label": "clean",
|
||||
"command": "${workspaceFolder}/venv/bin/ninja",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build"
|
||||
},
|
||||
"args": [
|
||||
"-t",
|
||||
"clean"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
variables:
|
||||
cc: "${source_root}/sysroot/bin/clang"
|
||||
cxx: "${source_root}/sysroot/bin/clang++"
|
||||
ld: "${source_root}/sysroot/bin/ld.lld"
|
||||
ar: ar
|
||||
nasm: nasm
|
||||
objcopy: objcopy
|
||||
|
||||
ccflags: [
|
||||
"-I${source_root}/src/include",
|
||||
"-I${source_root}/src/include/x86_64",
|
||||
"-fcolor-diagnostics",
|
||||
"-U__STDCPP_THREADS__",
|
||||
"-D_LIBCPP_HAS_NO_THREADS",
|
||||
"-DVERSION_MAJOR=${version_major}",
|
||||
"-DVERSION_MINOR=${version_minor}",
|
||||
"-DVERSION_PATCH=${version_patch}",
|
||||
"-DVERSION_GITSHA=0x${version_sha}",
|
||||
'-DGIT_VERSION=\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
|
||||
'-DGIT_VERSION_WIDE=L\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
|
||||
|
||||
"-Wformat=2", "-Winit-self", "-Wfloat-equal", "-Winline", "-Wmissing-format-attribute",
|
||||
"-Wmissing-include-dirs", "-Wswitch", "-Wundef", "-Wdisabled-optimization",
|
||||
"-Wpointer-arith", "-Wno-attributes", "-Wno-sign-compare", "-Wno-multichar",
|
||||
"-Wno-div-by-zero", "-Wno-endif-labels", "-Wno-pragmas", "-Wno-format-extra-args",
|
||||
"-Wno-unused-result", "-Wno-deprecated-declarations", "-Wno-unused-function",
|
||||
"-Wno-address-of-packed-member", "-Wno-invalid-offsetof", "-Wno-format-nonliteral",
|
||||
"-Werror" ]
|
||||
|
||||
asflags: [
|
||||
"-DVERSION_MAJOR=${version_major}",
|
||||
"-DVERSION_MINOR=${version_minor}",
|
||||
"-DVERSION_PATCH=${version_patch}",
|
||||
"-DVERSION_GITSHA=0x${version_sha}",
|
||||
"-I${source_root}/src/include" ]
|
||||
|
||||
cflags: [ "-std=c11" ]
|
||||
cxxflags: [ "-std=c++17" ]
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
extends: base
|
||||
|
||||
variables:
|
||||
ld: clang++
|
||||
|
||||
ccflags: [
|
||||
"-nostdlib",
|
||||
"-nodefaultlibs",
|
||||
"-fno-builtin",
|
||||
|
||||
"-I${source_root}/external",
|
||||
"--target=x86_64-unknown-windows",
|
||||
"-ffreestanding",
|
||||
"-mno-red-zone",
|
||||
"-fshort-wchar",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-ggdb",
|
||||
"-g3" ]
|
||||
|
||||
cxxflags: [ "-fno-exceptions", "-fno-rtti" ]
|
||||
|
||||
ldflags: [
|
||||
"--target=x86_64-unknown-windows",
|
||||
"-nostdlib",
|
||||
"-Wl,-entry:efi_main",
|
||||
"-Wl,-subsystem:efi_application",
|
||||
"-fuse-ld=lld-link",
|
||||
"-g" ]
|
||||
|
||||
9
assets/build/config.debug.yaml
Normal file
9
assets/build/config.debug.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
ccflags: [
|
||||
"-g3",
|
||||
"-ggdb",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-g",
|
||||
]
|
||||
39
assets/build/global.yaml
Normal file
39
assets/build/global.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
cc: "${source_root}/sysroot/bin/clang"
|
||||
cxx: "${source_root}/sysroot/bin/clang++"
|
||||
ld: "${source_root}/sysroot/bin/ld.lld"
|
||||
ar: ar
|
||||
nasm: nasm
|
||||
objcopy: objcopy
|
||||
|
||||
ccflags: [
|
||||
"-I${source_root}/src/include",
|
||||
"-fcolor-diagnostics",
|
||||
"-U__STDCPP_THREADS__",
|
||||
"-D_LIBCPP_HAS_NO_THREADS",
|
||||
"-D__jsix_config=${build_config}",
|
||||
"-D__jsix_config_${build_config}",
|
||||
"-DVERSION_MAJOR=${version_major}",
|
||||
"-DVERSION_MINOR=${version_minor}",
|
||||
"-DVERSION_PATCH=${version_patch}",
|
||||
"-DVERSION_GITSHA=0x${version_sha}",
|
||||
'-DGIT_VERSION=\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
|
||||
'-DGIT_VERSION_WIDE=L\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
|
||||
|
||||
"-Wformat=2", "-Winit-self", "-Winline", "-Wmissing-format-attribute",
|
||||
"-Wmissing-include-dirs", "-Wswitch", "-Wundef", "-Wdisabled-optimization",
|
||||
"-Wpointer-arith", "-Wno-attributes", "-Wno-sign-compare", "-Wno-multichar",
|
||||
"-Wno-div-by-zero", "-Wno-endif-labels", "-Wno-pragmas", "-Wno-format-extra-args",
|
||||
"-Wno-unused-result", "-Wno-deprecated-declarations", "-Wno-unused-function",
|
||||
"-Wno-address-of-packed-member", "-Wno-invalid-offsetof", "-Wno-format-nonliteral",
|
||||
"-Werror" ]
|
||||
|
||||
asflags: [
|
||||
"-DVERSION_MAJOR=${version_major}",
|
||||
"-DVERSION_MINOR=${version_minor}",
|
||||
"-DVERSION_PATCH=${version_patch}",
|
||||
"-DVERSION_GITSHA=0x${version_sha}",
|
||||
"-I${source_root}/src/include" ]
|
||||
|
||||
cflags: [ "-std=c11" ]
|
||||
cxxflags: [ "-std=c++17" ]
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
extends: base
|
||||
|
||||
variables:
|
||||
asflags: [ "-I${source_root}/src/kernel/" ]
|
||||
|
||||
ccflags: [
|
||||
"--target=x86_64-jsix-elf",
|
||||
"-fno-stack-protector",
|
||||
|
||||
"-I${source_root}/external",
|
||||
|
||||
"-nostdinc",
|
||||
"-nostdlib",
|
||||
"-ffreestanding",
|
||||
"-nodefaultlibs",
|
||||
"-fno-builtin",
|
||||
"-fno-plt",
|
||||
|
||||
"-mno-sse",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-mno-red-zone",
|
||||
"-mcmodel=kernel",
|
||||
"-fvisibility=hidden",
|
||||
"-fvisibility-inlines-hidden",
|
||||
|
||||
"-g3",
|
||||
"-ggdb",
|
||||
|
||||
"-D__ELF__",
|
||||
"-D__jsix__",
|
||||
"-D__j6kernel",
|
||||
"-U__linux",
|
||||
"-U__linux__",
|
||||
"-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1",
|
||||
"-DPRINTF_INCLUDE_CONFIG_H=1",
|
||||
|
||||
"-isystem${build_root}/include/libc",
|
||||
"-isystem${source_root}/sysroot/include",
|
||||
"--sysroot='${source_root}/sysroot'" ]
|
||||
|
||||
|
||||
cflags: [ '-nostdinc' ]
|
||||
|
||||
cxxflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-nostdinc",
|
||||
"-isystem${source_root}/sysroot/include/c++/v1" ]
|
||||
|
||||
ldflags: [
|
||||
"-g",
|
||||
"-m", "elf_x86_64",
|
||||
"-nostdlib",
|
||||
"-Bstatic",
|
||||
"--no-eh-frame-hdr",
|
||||
"-z", "norelro",
|
||||
"-z", "separate-code" ]
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# This file is automatically generated by bonnibel
|
||||
|
||||
rule compile.c
|
||||
command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in
|
||||
description = Compiling $name
|
||||
description = Compiling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
@@ -17,7 +15,7 @@ rule dump_c_run
|
||||
|
||||
rule compile.cpp
|
||||
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
|
||||
description = Compiling $name
|
||||
description = Compiling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
@@ -32,25 +30,33 @@ rule dump_cpp_run
|
||||
|
||||
rule compile.s
|
||||
command = $nasm -o $out -felf64 -MD $out.d $asflags $in
|
||||
description = Assembling $name
|
||||
description = Assembling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule parse.cog
|
||||
command = cog -o $out -d -D target=$target $cogflags $in
|
||||
description = Parsing $name
|
||||
description = Parsing [$target]:$name
|
||||
|
||||
rule exe
|
||||
command = $ld $ldflags -o $out $in $libs
|
||||
description = Linking $name
|
||||
description = Linking exe [$target]:$name
|
||||
|
||||
rule driver
|
||||
command = $ld $ldflags -o $out $in $libs
|
||||
description = Linking driver [$target]:$name
|
||||
|
||||
rule lib
|
||||
command = $ld -shared -soname $soname $ldflags -o $out $in $libs
|
||||
description = Linking [$target]:$name
|
||||
|
||||
rule lib_static
|
||||
command = $ar qcs $out $in
|
||||
description = Archiving $name
|
||||
description = Archiving [$target]:$name
|
||||
|
||||
rule cp
|
||||
command = cp $in $out
|
||||
description = Copying $name
|
||||
description = Copying [$target]:$name
|
||||
|
||||
rule dump
|
||||
command = objdump -DSC -M intel $in > $out
|
||||
|
||||
26
assets/build/target.boot.yaml
Normal file
26
assets/build/target.boot.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
ld: clang++
|
||||
|
||||
ccflags: [
|
||||
"-nostdlib",
|
||||
"-nodefaultlibs",
|
||||
"-fno-builtin",
|
||||
|
||||
"-I${source_root}/external",
|
||||
"--target=x86_64-unknown-windows",
|
||||
"-ffreestanding",
|
||||
"-mno-red-zone",
|
||||
"-fshort-wchar",
|
||||
"-fno-omit-frame-pointer",
|
||||
]
|
||||
|
||||
cxxflags: [ "-fno-exceptions", "-fno-rtti" ]
|
||||
|
||||
ldflags: [
|
||||
"--target=x86_64-unknown-windows",
|
||||
"-nostdlib",
|
||||
"-Wl,-entry:efi_main",
|
||||
"-Wl,-subsystem:efi_application",
|
||||
"-fuse-ld=lld-link",
|
||||
]
|
||||
|
||||
39
assets/build/target.init.yaml
Normal file
39
assets/build/target.init.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
ccflags: [
|
||||
"--target=x86_64-jsix-elf",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-fno-stack-protector",
|
||||
|
||||
"-fvisibility=hidden",
|
||||
"-fvisibility-inlines-hidden",
|
||||
|
||||
"-D__ELF__",
|
||||
"-D__jsix__",
|
||||
"-U__linux",
|
||||
"-U__linux__",
|
||||
|
||||
"-DMSPACES",
|
||||
|
||||
"--sysroot='${source_root}/sysroot'"
|
||||
]
|
||||
|
||||
|
||||
cxxflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-Bstatic",
|
||||
"-m", "elf_x86_64",
|
||||
"--sysroot='${source_root}/sysroot'",
|
||||
"--no-eh-frame-hdr",
|
||||
"-L", "${source_root}/sysroot/lib",
|
||||
"-z", "separate-code",
|
||||
"-lc++", "-lc++abi", "-lunwind",
|
||||
"--no-dependent-libraries",
|
||||
]
|
||||
|
||||
libs: [
|
||||
"${target_dir}/crt0.o",
|
||||
]
|
||||
52
assets/build/target.kernel.yaml
Normal file
52
assets/build/target.kernel.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
asflags: [ "-I${source_root}/src/kernel/" ]
|
||||
|
||||
ccflags: [
|
||||
"--target=x86_64-jsix-elf",
|
||||
"-fno-stack-protector",
|
||||
|
||||
"-I${source_root}/external",
|
||||
|
||||
"-nostdinc",
|
||||
"-nostdlib",
|
||||
"-ffreestanding",
|
||||
"-nodefaultlibs",
|
||||
"-fno-builtin",
|
||||
"-fno-plt",
|
||||
|
||||
"-mno-sse",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-mno-red-zone",
|
||||
"-mcmodel=kernel",
|
||||
"-fvisibility=hidden",
|
||||
"-fvisibility-inlines-hidden",
|
||||
|
||||
"-D__ELF__",
|
||||
"-D__jsix__",
|
||||
"-D__j6kernel",
|
||||
"-U__linux",
|
||||
"-U__linux__",
|
||||
"-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1",
|
||||
"-DPRINTF_INCLUDE_CONFIG_H=1",
|
||||
|
||||
"--sysroot='${source_root}/sysroot'"
|
||||
]
|
||||
|
||||
|
||||
cflags: [ '-nostdinc' ]
|
||||
|
||||
cxxflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-nostdinc",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-m", "elf_x86_64",
|
||||
"-nostdlib",
|
||||
"-Bstatic",
|
||||
"--no-eh-frame-hdr",
|
||||
"-z", "norelro",
|
||||
"-z", "separate-code"
|
||||
]
|
||||
|
||||
15
assets/build/target.user.exe.yaml
Normal file
15
assets/build/target.user.exe.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
|
||||
ccflags: [
|
||||
"-fpie"
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-pie",
|
||||
"--dynamic-linker", "/jsix/lib/ld.so",
|
||||
"--push-state", "--as-needed", "-Bstatic", "-lc++", "-lc++abi", "-lunwind", "--pop-state",
|
||||
]
|
||||
|
||||
libs: [
|
||||
"${target_dir}/crt0.o",
|
||||
]
|
||||
7
assets/build/target.user.shared.yaml
Normal file
7
assets/build/target.user.shared.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
ccflags: [
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-shared",
|
||||
]
|
||||
33
assets/build/target.user.yaml
Normal file
33
assets/build/target.user.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
asflags: []
|
||||
|
||||
ccflags: [
|
||||
"--target=x86_64-jsix-elf",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-fno-stack-protector",
|
||||
|
||||
"-fvisibility=hidden",
|
||||
"-fvisibility-inlines-hidden",
|
||||
|
||||
"-D__ELF__",
|
||||
"-D__jsix__",
|
||||
"-U__linux",
|
||||
"-U__linux__",
|
||||
|
||||
"--sysroot='${source_root}/sysroot'",
|
||||
"-fpic",
|
||||
]
|
||||
|
||||
cxxflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-m", "elf_x86_64",
|
||||
"--sysroot='${source_root}/sysroot'",
|
||||
"--no-eh-frame-hdr",
|
||||
"-L", "${source_root}/sysroot/lib",
|
||||
"-z", "separate-code",
|
||||
"--no-dependent-libraries",
|
||||
]
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
extends: base
|
||||
|
||||
variables:
|
||||
asflags: [ "-I${source_root}/src/kernel/" ]
|
||||
|
||||
ccflags: [
|
||||
"--target=x86_64-jsix-elf",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-fno-stack-protector",
|
||||
|
||||
"-g",
|
||||
|
||||
"-D__ELF__",
|
||||
"-D__jsix__",
|
||||
"-U__linux",
|
||||
"-U__linux__",
|
||||
|
||||
"-isystem${source_root}/sysroot/include",
|
||||
"-isystem${build_root}/include/libc",
|
||||
"--sysroot='${source_root}/sysroot'" ]
|
||||
|
||||
|
||||
cxxflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-isystem${source_root}/sysroot/include/c++/v1" ]
|
||||
|
||||
ldflags: [
|
||||
"-g",
|
||||
"-Bstatic",
|
||||
"--sysroot='${source_root}/sysroot'",
|
||||
"-L", "${source_root}/sysroot/lib",
|
||||
"-z", "separate-code",
|
||||
"-lc++", "-lc++abi", "-lunwind",
|
||||
"--no-dependent-libraries",
|
||||
]
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import gdb
|
||||
import gdb.printing
|
||||
|
||||
import sys
|
||||
sys.path.append('./scripts')
|
||||
|
||||
import re
|
||||
|
||||
from collections import namedtuple
|
||||
Capability = namedtuple("Capability", ["id", "parent", "refcount", "caps", "type", "koid"])
|
||||
LogEntry = namedtuple("LogHeader", ["id", "bytes", "severity", "area", "message"])
|
||||
|
||||
class PrintStackCommand(gdb.Command):
|
||||
def __init__(self):
|
||||
@@ -168,12 +174,12 @@ class GetThreadsCommand(gdb.Command):
|
||||
rsp = int(gdb.parse_and_eval(f"{tcb}->rsp"))
|
||||
pri = int(gdb.parse_and_eval(f"{tcb}->priority"))
|
||||
flags = int(gdb.parse_and_eval(f"{thread}->m_state"))
|
||||
koid = int(gdb.parse_and_eval(f"{thread}->m_koid"))
|
||||
proc = int(gdb.parse_and_eval(f"{thread}->m_parent.m_koid"))
|
||||
koid = int(gdb.parse_and_eval(f"{thread}->m_obj_id"))
|
||||
proc = int(gdb.parse_and_eval(f"{thread}->m_parent.m_obj_id"))
|
||||
|
||||
creator = int(gdb.parse_and_eval(f"{thread}->m_creator"))
|
||||
if creator != 0:
|
||||
creator_koid = int(gdb.parse_and_eval(f"{thread}->m_creator->m_koid"))
|
||||
creator_koid = int(gdb.parse_and_eval(f"{thread}->m_creator->m_obj_id"))
|
||||
creator = f"{creator_koid:x}"
|
||||
else:
|
||||
creator = "<no thread>"
|
||||
@@ -233,11 +239,7 @@ class GetThreadsCommand(gdb.Command):
|
||||
self.print_cpudata(cpu)
|
||||
|
||||
previous = int(gdb.parse_and_eval(f"{runlist}.prev"))
|
||||
if previous != 0:
|
||||
tcb = f"((TCB*){previous:#x})"
|
||||
thread = f"({tcb}->thread)"
|
||||
koid = int(gdb.parse_and_eval(f"{thread}->m_koid"))
|
||||
print(f" prev: {koid:x}")
|
||||
print(f" prev: {previous:x}")
|
||||
print()
|
||||
|
||||
for pri in range(8):
|
||||
@@ -283,6 +285,79 @@ class PrintProfilesCommand(gdb.Command):
|
||||
print(f"{name:>{max_len}}: {avg:15.3f}")
|
||||
|
||||
|
||||
class DumpLogCommand(gdb.Command):
|
||||
level_names = ["", "fatal", "error", "warn", "info", "verbose", "spam"]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("j6log", gdb.COMMAND_DATA)
|
||||
|
||||
from memory import Layout
|
||||
layout = Layout("definitions/memory_layout.yaml")
|
||||
for region in layout.regions:
|
||||
if region.name == "logs":
|
||||
self.base_addr = region.start
|
||||
break
|
||||
|
||||
self.areas = []
|
||||
area_re = re.compile(r"LOG\(\s*(\w+).*")
|
||||
with open("src/libraries/j6/include/j6/tables/log_areas.inc", 'r') as areas_inc:
|
||||
for line in areas_inc:
|
||||
m = area_re.match(line)
|
||||
if m:
|
||||
self.areas.append(m.group(1))
|
||||
|
||||
def get_entry(self, addr):
|
||||
addr = int(addr)
|
||||
size = int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->bytes"))
|
||||
mlen = size - 8
|
||||
|
||||
return LogEntry(
|
||||
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->id")),
|
||||
size,
|
||||
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->severity")),
|
||||
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->area")),
|
||||
gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->message").string(length=mlen))
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
start = gdb.parse_and_eval("g_logger.m_start & (g_logger.m_buffer.count - 1)")
|
||||
end = gdb.parse_and_eval("g_logger.m_end & (g_logger.m_buffer.count - 1)")
|
||||
if end < start:
|
||||
end += gdb.parse_and_eval("g_logger.m_buffer.count")
|
||||
|
||||
print(f"Logs are {start} -> {end}")
|
||||
|
||||
addr = self.base_addr + start
|
||||
end += self.base_addr
|
||||
while addr < end:
|
||||
entry = self.get_entry(addr)
|
||||
if entry.bytes < 8:
|
||||
print(f"Bad log header size: {entry.bytes}")
|
||||
break
|
||||
addr += entry.bytes
|
||||
area = "??"
|
||||
if entry.area < len(self.areas):
|
||||
area = self.areas[entry.area]
|
||||
level = self.level_names[entry.severity]
|
||||
print(f"{area:>7}:{level:7} {entry.message}")
|
||||
|
||||
|
||||
class ShowCurrentProcessCommand(gdb.Command):
|
||||
def __init__(self):
|
||||
super().__init__("j6current", gdb.COMMAND_DATA)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
def get_obj_and_id(name):
|
||||
obj = int(gdb.parse_and_eval(f"((cpu_data*)$gs_base)->{name}"))
|
||||
oid = -1
|
||||
if obj != 0:
|
||||
oid = int(gdb.parse_and_eval(f"((obj::kobject*){obj:#x})->m_obj_id"))
|
||||
return obj, oid
|
||||
|
||||
process, pid = get_obj_and_id("process")
|
||||
thread, tid = get_obj_and_id("thread")
|
||||
print(f"{pid:02x}/{tid:02x} [ {process:x} / {thread:x} ]")
|
||||
|
||||
|
||||
class CapTablePrinter:
|
||||
def __init__(self, val):
|
||||
node_map = val["m_caps"]
|
||||
@@ -307,7 +382,7 @@ class CapTablePrinter:
|
||||
refcount = int(node["holders"]),
|
||||
caps = int(node["caps"]),
|
||||
type = str(node["type"])[14:],
|
||||
koid = node['object']['m_koid']))
|
||||
koid = node['object']['m_obj_id']))
|
||||
|
||||
self.nodes.sort(key=lambda n: n.id, reverse=True)
|
||||
|
||||
@@ -399,11 +474,39 @@ class HandleSetPrinter:
|
||||
return self._iterator(self.node_map['m_nodes'], self.capacity)
|
||||
|
||||
|
||||
class LinkedListPrinter:
|
||||
def __init__(self, llist):
|
||||
self.name = llist.type.tag
|
||||
self.head = llist['m_head']
|
||||
self.tail = llist['m_tail']
|
||||
|
||||
self.items = []
|
||||
current = self.head
|
||||
while current:
|
||||
item = current.dereference()
|
||||
self.items.append((str(len(self.items)), item))
|
||||
current = item['m_next']
|
||||
|
||||
class _iterator:
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
raise StopIteration
|
||||
|
||||
def to_string(self):
|
||||
return f"{self.name}[{len(self.items)}]"
|
||||
|
||||
def children(self):
|
||||
return self.items
|
||||
|
||||
|
||||
def build_pretty_printers():
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
|
||||
pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
|
||||
pp.add_printer("handle set", '^util::node_set<unsigned long, 0, heap_allocated>$', HandleSetPrinter)
|
||||
pp.add_printer("vector", '^util::vector<.*>$', VectorPrinter)
|
||||
pp.add_printer("linked list", '^util::linked_list<.*>$', LinkedListPrinter)
|
||||
return pp
|
||||
|
||||
gdb.printing.register_pretty_printer(
|
||||
@@ -415,8 +518,11 @@ PrintBacktraceCommand()
|
||||
TableWalkCommand()
|
||||
GetThreadsCommand()
|
||||
PrintProfilesCommand()
|
||||
DumpLogCommand()
|
||||
ShowCurrentProcessCommand()
|
||||
|
||||
gdb.execute("display/i $rip")
|
||||
gdb.execute("define hook-quit\nkill\nend")
|
||||
if not gdb.selected_inferior().was_attached:
|
||||
gdb.execute("add-symbol-file build/panic.serial.elf")
|
||||
gdb.execute("target remote :1234")
|
||||
|
||||
@@ -35,7 +35,7 @@ name: IDENTIFIER
|
||||
options: "[" ( OPTION | IDENTIFIER )+ "]"
|
||||
description: COMMENT+
|
||||
|
||||
PRIMITIVE: INT_TYPE | "size" | "string" | "buffer" | "address"
|
||||
PRIMITIVE: INT_TYPE "*"? | "size" | "string" | "buffer" | "address"
|
||||
INT_TYPE: /u?int(8|16|32|64)?/
|
||||
NUMBER: /(0x)?[0-9a-fA-F]+/
|
||||
UID: /[0-9a-fA-F]{16}/
|
||||
|
||||
@@ -8,5 +8,9 @@ panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- srv.logger
|
||||
- testapp
|
||||
drivers:
|
||||
- drv.uart
|
||||
- drv.uefi_fb
|
||||
libs:
|
||||
- ld.so
|
||||
@@ -9,5 +9,3 @@ panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- test_runner
|
||||
drivers:
|
||||
- drv.uart
|
||||
|
||||
1
configure
vendored
1
configure
vendored
@@ -35,6 +35,7 @@ def generate(output, config, manifest):
|
||||
"source_root": root,
|
||||
"build_root": output,
|
||||
"module_root": path,
|
||||
"config": config,
|
||||
}
|
||||
code = compile(open(modfile, 'r').read(), modfile, "exec")
|
||||
|
||||
|
||||
@@ -24,3 +24,6 @@
|
||||
|
||||
- name: buffers
|
||||
size: 64G
|
||||
|
||||
- name: logs
|
||||
size: 2G
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
object channel : object {
|
||||
uid 3ea38b96aa0e54c8
|
||||
|
||||
capabilities [
|
||||
send
|
||||
receive
|
||||
close
|
||||
]
|
||||
|
||||
method create [constructor]
|
||||
method close [destructor cap:close]
|
||||
|
||||
method send [cap:send] {
|
||||
param data buffer [inout]
|
||||
}
|
||||
|
||||
method receive [cap:receive] {
|
||||
param data buffer [out]
|
||||
param flags uint64
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
# Mailboxes are objects that enable synchronous IPC via short message-passing
|
||||
# of tagged handles.
|
||||
# Mailboxes are objects that enable synchronous IPC via arbitrary
|
||||
# message-passing of tagged data and/or handles. Not as efficient
|
||||
# as shared memory channels, but more flexible.
|
||||
|
||||
object mailbox : object {
|
||||
uid 99934ad04ece1e07
|
||||
@@ -13,13 +14,14 @@ object mailbox : object {
|
||||
method create [constructor]
|
||||
method close [destructor cap:close]
|
||||
|
||||
# Send a message to the reciever, and block until a
|
||||
# response is sent. Note that getting this response
|
||||
# does not require the receive capability.
|
||||
# Send a message to the reciever, and block until a response is
|
||||
# sent. Note that getting this response does not require the
|
||||
# receive capability.
|
||||
method call [cap:send] {
|
||||
param tag uint64 [inout]
|
||||
param subtag uint64 [inout]
|
||||
param give_handle ref object [optional inout handle]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param handles ref object [optional inout handle list]
|
||||
}
|
||||
|
||||
# Respond to a message sent using call, and wait for another
|
||||
@@ -28,8 +30,9 @@ object mailbox : object {
|
||||
# to waiting for a new message.
|
||||
method respond [cap:receive] {
|
||||
param tag uint64 [inout]
|
||||
param subtag uint64 [inout]
|
||||
param give_handle ref object [optional inout handle]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param handles ref object [optional inout handle list]
|
||||
param reply_tag uint64 [inout]
|
||||
param flags uint64
|
||||
}
|
||||
|
||||
@@ -10,9 +10,10 @@ object system : object {
|
||||
change_iopl
|
||||
]
|
||||
|
||||
# Get a log line from the kernel log
|
||||
# Get the next log line from the kernel log
|
||||
method get_log [cap:get_log] {
|
||||
param buffer buffer [out zero_ok] # Buffer for the log message data structure
|
||||
param seen uint64 # Last seen log id
|
||||
param buffer buffer [out zero_ok] # Buffer for the log message data structure
|
||||
}
|
||||
|
||||
# Ask the kernel to send this process messages whenever
|
||||
|
||||
@@ -7,9 +7,11 @@ object thread : object {
|
||||
]
|
||||
|
||||
method create [constructor] {
|
||||
param process ref process [cap:create_thread]
|
||||
param process ref process [optional cap:create_thread]
|
||||
param stack_top address
|
||||
param entrypoint address
|
||||
param arg0 uint64
|
||||
param arg1 uint64
|
||||
}
|
||||
|
||||
method kill [destructor cap:kill]
|
||||
|
||||
@@ -17,20 +17,21 @@ object vma : object {
|
||||
|
||||
method create_map [constructor cap:map] {
|
||||
param size size
|
||||
param address address
|
||||
param address address [inout]
|
||||
param flags uint32
|
||||
}
|
||||
|
||||
method map [cap:map] {
|
||||
param process ref process
|
||||
param address address
|
||||
param process ref process [optional]
|
||||
param address address [inout]
|
||||
param flags uint32
|
||||
}
|
||||
|
||||
method unmap [cap:unmap] {
|
||||
param process ref process
|
||||
param process ref process [optional]
|
||||
}
|
||||
|
||||
method resize [cap:resize] {
|
||||
param size size [inout]
|
||||
param size size [inout] # New size for the VMA, or 0 to query the current size without changing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "objects/object.def"
|
||||
|
||||
import "objects/channel.def"
|
||||
import "objects/event.def"
|
||||
import "objects/mailbox.def"
|
||||
import "objects/process.def"
|
||||
@@ -12,12 +11,12 @@ interface syscalls [syscall] {
|
||||
uid 01d9b6a948961097
|
||||
|
||||
expose ref object
|
||||
expose ref system
|
||||
|
||||
expose ref event
|
||||
expose ref process
|
||||
expose ref thread
|
||||
expose ref mailbox
|
||||
expose ref channel
|
||||
expose ref process
|
||||
expose ref system
|
||||
expose ref thread
|
||||
expose ref vma
|
||||
|
||||
# Simple no-op syscall for testing
|
||||
@@ -43,6 +42,19 @@ interface syscalls [syscall] {
|
||||
param mask uint32 # The capability bitmask
|
||||
}
|
||||
|
||||
# Block waiting on a futex
|
||||
function futex_wait [static] {
|
||||
param address uint32* # Address of the futex value
|
||||
param current uint32 # Current value of the futex
|
||||
param timeout uint64 # Wait timeout in nanoseconds
|
||||
}
|
||||
|
||||
# Wake threads waiting on a futex
|
||||
function futex_wake [static] {
|
||||
param address uint32* # Address of the futex value
|
||||
param count uint64 # Number of threads to wake, or 0 for all
|
||||
}
|
||||
|
||||
# Testing mode only: Have the kernel finish and exit QEMU with the given exit code
|
||||
function test_finish [test] {
|
||||
param exit_code uint32
|
||||
|
||||
4
external/zstd.module
vendored
4
external/zstd.module
vendored
@@ -3,8 +3,8 @@
|
||||
zstd = module("zstd",
|
||||
root = "${source_root}/external/zstd",
|
||||
kind = "lib",
|
||||
deps = [ ],
|
||||
output = "libzstd.a",
|
||||
copy_headers = True,
|
||||
deps = [ "libc" ],
|
||||
sources = [
|
||||
"decompress/zstd_decompress.c",
|
||||
"decompress/zstd_ddict.c",
|
||||
|
||||
77
qemu.sh
77
qemu.sh
@@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
build="$(dirname $0)/build"
|
||||
assets="$(dirname $0)/assets"
|
||||
root=$(dirname $0)
|
||||
build="${root}/build"
|
||||
assets="${root}/assets"
|
||||
|
||||
no_build=""
|
||||
debug=""
|
||||
isaexit='-device isa-debug-exit,iobase=0xf4,iosize=0x04'
|
||||
debugtarget="${build}/jsix.elf"
|
||||
@@ -9,7 +12,9 @@ gfx="-nographic"
|
||||
vga="-vga none"
|
||||
log=""
|
||||
kvm=""
|
||||
cpu="Broadwell,+pdpe1gb"
|
||||
debugcon_cmd=""
|
||||
cpu_features=",+pdpe1gb,+invtsc,+hypervisor,+x2apic,+xsavec,+xsaves,+xsaveopt"
|
||||
cpu="Broadwell"
|
||||
smp=4
|
||||
|
||||
while true; do
|
||||
@@ -35,14 +40,14 @@ while true; do
|
||||
;;
|
||||
-r | --remote)
|
||||
shift
|
||||
vnchost="${1:-${VNCHOST:-localhost}}"
|
||||
gfx="-vnc ${vnchost}:7000,reverse"
|
||||
vnchost="${1:-${VNCHOST:-"localhost:5500"}}"
|
||||
gfx="-vnc ${vnchost},reverse"
|
||||
vga=""
|
||||
shift
|
||||
;;
|
||||
-k | --kvm)
|
||||
kvm="-enable-kvm"
|
||||
cpu="host"
|
||||
cpu="max"
|
||||
shift
|
||||
;;
|
||||
-c | --cpus)
|
||||
@@ -50,7 +55,15 @@ while true; do
|
||||
shift 2
|
||||
;;
|
||||
-l | --log)
|
||||
log="-d mmu,int,guest_errors -D jsix.log"
|
||||
log="-d mmu,int,guest_errors -D ${root}/jsix.log"
|
||||
shift
|
||||
;;
|
||||
-x | --debugcon)
|
||||
debugcon_cmd="less --follow-name -R +F debugcon.log"
|
||||
shift
|
||||
;;
|
||||
--no-build)
|
||||
no_build="yes"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
@@ -67,17 +80,44 @@ if [[ ! -c /dev/kvm ]]; then
|
||||
kvm=""
|
||||
fi
|
||||
|
||||
if ! ninja -C "${build}"; then
|
||||
[[ -z "${no_build}" ]] && if ! ninja -C "${build}"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n $TMUX ]]; then
|
||||
cols=$(tput cols)
|
||||
log_width=100
|
||||
|
||||
if [[ -n $debug ]]; then
|
||||
tmux split-window -h "gdb ${debugtarget}" &
|
||||
log_cols=1
|
||||
if [[ $debugcon_cmd ]]; then
|
||||
log_cols=2
|
||||
fi
|
||||
|
||||
gdb_width=$(($cols - $log_cols * $log_width))
|
||||
|
||||
if (($gdb_width < 150)); then
|
||||
stack=1
|
||||
gdb_width=$(($cols - $log_width))
|
||||
tmux split-window -h -l $gdb_width "gdb ${debugtarget}"
|
||||
if [[ $debugcon_cmd ]]; then
|
||||
tmux select-pane -t .left
|
||||
tmux split-window -v "$debugcon_cmd"
|
||||
fi
|
||||
else
|
||||
if [[ $debugcon_cmd ]]; then
|
||||
tmux split-window -h -l $(($log_width + $gdb_width)) "$debugcon_cmd"
|
||||
fi
|
||||
tmux split-window -h -l $gdb_width "gdb ${debugtarget}"
|
||||
tmux select-pane -t .left
|
||||
tmux select-pane -t .right
|
||||
fi
|
||||
else
|
||||
tmux split-window -h -l 100 "sleep 1; telnet localhost 45455" &
|
||||
tmux last-pane
|
||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
||||
if [[ $debugcon_cmd ]]; then
|
||||
tmux split-window -h -l $log_width "$debugcon_cmd"
|
||||
tmux last-pane
|
||||
fi
|
||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454"
|
||||
fi
|
||||
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||
if [[ -n $debug ]]; then
|
||||
@@ -87,19 +127,24 @@ elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${debug}" ]]; then
|
||||
(sleep 1; echo "Debugging ready.") &
|
||||
fi
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \
|
||||
-drive "if=pflash,format=raw,readonly=on,file=${assets}/ovmf/x64/ovmf_code.fd" \
|
||||
-drive "if=pflash,format=raw,file=${build}/ovmf_vars.fd" \
|
||||
-drive "format=raw,file=${build}/jsix.img" \
|
||||
-chardev socket,host=localhost,port=45455,server,nowait,telnet=on,id=jsix_debug -device isa-debugcon,iobase=0x6600,chardev=jsix_debug \
|
||||
-chardev "file,path=${root}/debugcon.log,id=jsix_debug" \
|
||||
-device "isa-debugcon,iobase=0x6600,chardev=jsix_debug" \
|
||||
-monitor telnet:localhost:45454,server,nowait \
|
||||
-serial stdio \
|
||||
-smp "${smp}" \
|
||||
-m 4096 \
|
||||
-cpu "${cpu}" \
|
||||
-cpu "${cpu}${cpu_features}" \
|
||||
-M q35 \
|
||||
-no-reboot \
|
||||
-name "jsix" \
|
||||
-name jsix \
|
||||
$isaexit $log $gfx $vga $kvm $debug
|
||||
|
||||
((result = ($? >> 1) - 1))
|
||||
|
||||
@@ -5,3 +5,5 @@ pyyaml >= 5.4
|
||||
lark == 0.12.0
|
||||
pure-cdb == 4
|
||||
pyzstd == 0.15
|
||||
pyelftools
|
||||
iced-x86
|
||||
|
||||
68
scripts/bonnibel/config.py
Normal file
68
scripts/bonnibel/config.py
Normal file
@@ -0,0 +1,68 @@
|
||||
_config_cache = {}
|
||||
|
||||
def _load(filename, depfiles):
|
||||
from . import load_config
|
||||
if not filename in _config_cache:
|
||||
if filename.exists():
|
||||
depfiles.add(filename)
|
||||
data = load_config(filename)
|
||||
_config_cache[filename] = data
|
||||
return _config_cache.get(filename, dict())
|
||||
|
||||
|
||||
def _build_config(chain, depfiles):
|
||||
config = {}
|
||||
for d in [_load(c, depfiles) for c in chain]:
|
||||
for k, v in d.items():
|
||||
if isinstance(v, (list, tuple)):
|
||||
config[k] = config.get(k, list()) + list(v)
|
||||
elif isinstance(v, dict):
|
||||
config[k] = config.get(k, dict())
|
||||
config[k].update(v)
|
||||
else:
|
||||
config[k] = v
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def _make_ninja_config(outfile, config, files):
|
||||
from os import makedirs
|
||||
from ninja.ninja_syntax import Writer
|
||||
|
||||
makedirs(outfile.parent, exist_ok=True)
|
||||
|
||||
with open(outfile, "w") as buildfile:
|
||||
build = Writer(buildfile)
|
||||
build.comment("This file is automatically generated by bonnibel")
|
||||
build.comment("Source config files:")
|
||||
for f in files:
|
||||
build.comment(f" - {f}")
|
||||
|
||||
build.newline()
|
||||
for k, v in config.items():
|
||||
build.variable(k, v)
|
||||
|
||||
|
||||
def generate_configs(root, output, config, targets, kinds):
|
||||
|
||||
assets = root / "assets" / "build"
|
||||
base = ["global.yaml", f"config.{config}.yaml"]
|
||||
|
||||
depfiles = set()
|
||||
|
||||
for target in targets:
|
||||
chain = base + [f"target.{target}.yaml"]
|
||||
|
||||
files = [assets / f for f in chain]
|
||||
config = _build_config(files, depfiles)
|
||||
outfile = output / target / f"config.ninja"
|
||||
_make_ninja_config(outfile, config, files)
|
||||
|
||||
for kind in kinds:
|
||||
custom = [f"kind.{kind}.yaml", f"target.{target}.{kind}.yaml"]
|
||||
files = [assets / f for f in chain + custom]
|
||||
config = _build_config(files, depfiles)
|
||||
outfile = output / target / f"config.{kind}.ninja"
|
||||
_make_ninja_config(outfile, config, files)
|
||||
|
||||
return depfiles
|
||||
@@ -26,7 +26,7 @@ class Manifest:
|
||||
name="kernel", target="kernel")
|
||||
|
||||
self.init = self.__build_entry(modules,
|
||||
config.get("init", None))
|
||||
config.get("init", None), target="init")
|
||||
|
||||
self.panics = [self.__build_entry(modules, i, target="kernel")
|
||||
for i in config.get("panic", tuple())]
|
||||
@@ -37,6 +37,13 @@ class Manifest:
|
||||
self.drivers = [self.__build_entry(modules, i)
|
||||
for i in config.get("drivers", tuple())]
|
||||
|
||||
libs = set(config.get("libs", tuple()))
|
||||
libs.update(self.__libdeps([modules[e.module] for e in self.services]))
|
||||
libs.update(self.__libdeps([modules[e.module] for e in self.drivers]))
|
||||
|
||||
self.libs = [self.__build_entry(modules, i)
|
||||
for i in libs]
|
||||
|
||||
self.flags = config.get("flags", tuple())
|
||||
|
||||
self.symbols = ""
|
||||
@@ -71,7 +78,14 @@ class Manifest:
|
||||
if not f in Manifest.flags:
|
||||
raise BonnibelError(f"Manifest specifies unknown flag '{f}'")
|
||||
|
||||
return Manifest.Entry(name, target, mod.output, flags)
|
||||
return Manifest.Entry(name, target, mod.get_output(), flags)
|
||||
|
||||
def __libdeps(self, modules):
|
||||
deps = set([m.name for m in modules if m.kind == "lib"])
|
||||
for m in modules:
|
||||
if m.static: continue
|
||||
deps.update(self.__libdeps(m.depmods))
|
||||
return deps
|
||||
|
||||
def add_data(self, output, desc, flags=tuple()):
|
||||
e = Manifest.Entry(None, None, output, flags)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from . import include_rel, mod_rel, target_rel
|
||||
from . import BonnibelError
|
||||
|
||||
def resolve(path):
|
||||
if path.startswith('/') or path.startswith('$'):
|
||||
@@ -7,29 +8,43 @@ def resolve(path):
|
||||
return str(Path(path).resolve())
|
||||
|
||||
class BuildOptions:
|
||||
def __init__(self, includes=tuple(), libs=tuple(), order_only=tuple(), ld_script=None):
|
||||
def __init__(self, includes=tuple(), local=tuple(), late=tuple(), libs=tuple(), order_only=tuple(), ld_script=None):
|
||||
self.includes = list(includes)
|
||||
self.local = list(local)
|
||||
self.late = list(late)
|
||||
self.libs = list(libs)
|
||||
self.order_only = list(order_only)
|
||||
self.ld_script = ld_script and str(ld_script)
|
||||
|
||||
@property
|
||||
def implicit(self):
|
||||
libfiles = list(map(lambda x: x[0], self.libs))
|
||||
if self.ld_script is not None:
|
||||
return self.libs + [self.ld_script]
|
||||
return libfiles + [self.ld_script]
|
||||
else:
|
||||
return self.libs
|
||||
return libfiles
|
||||
|
||||
@property
|
||||
def linker_args(self):
|
||||
from pathlib import Path
|
||||
def arg(path, static):
|
||||
if static: return path
|
||||
return "-l:" + Path(path).name
|
||||
return [arg(*l) for l in self.libs]
|
||||
|
||||
|
||||
class Module:
|
||||
__fields = {
|
||||
# name: (type, default)
|
||||
"kind": (str, "exe"),
|
||||
"output": (str, None),
|
||||
"outfile": (str, None),
|
||||
"basename": (str, None),
|
||||
"targets": (set, ()),
|
||||
"deps": (set, ()),
|
||||
"public_headers": (set, ()),
|
||||
"copy_headers": (bool, False),
|
||||
"includes": (tuple, ()),
|
||||
"include_phase": (str, "normal"),
|
||||
"sources": (tuple, ()),
|
||||
"drivers": (tuple, ()),
|
||||
"variables": (dict, ()),
|
||||
@@ -37,13 +52,15 @@ class Module:
|
||||
"description": (str, None),
|
||||
"no_libc": (bool, False),
|
||||
"ld_script": (str, None),
|
||||
"static": (bool, False),
|
||||
}
|
||||
|
||||
def __init__(self, name, modfile, root, **kwargs):
|
||||
from .source import make_source
|
||||
from pathlib import Path
|
||||
from .source import make_source, make_copy_source
|
||||
|
||||
# Required fields
|
||||
self.root = root
|
||||
self.root = Path(root)
|
||||
self.name = name
|
||||
self.modfile = modfile
|
||||
|
||||
@@ -57,31 +74,46 @@ class Module:
|
||||
|
||||
for name in kwargs:
|
||||
if not name in self.__fields:
|
||||
raise AttributeError(f"No attribute named {name} on Module")
|
||||
raise AttributeError(f"No attribute named {name} on Module ({modfile})")
|
||||
|
||||
if not self.no_libc:
|
||||
self.deps.add("libc_free")
|
||||
|
||||
# Turn strings into real Source objects
|
||||
self.sources = [make_source(root, f) for f in self.sources]
|
||||
self.public_headers = [make_source(root, f) for f in self.public_headers]
|
||||
|
||||
header_source = lambda f: make_source(root, Path("include") / f)
|
||||
if self.copy_headers:
|
||||
header_source = lambda f: make_copy_source(root, f, "include")
|
||||
self.public_headers = [header_source(f) for f in self.public_headers]
|
||||
|
||||
# Filled by Module.update
|
||||
self.depmods = set()
|
||||
|
||||
def __str__(self):
|
||||
return "Module {} {}\n\t{}".format(self.kind, self.name, "\n\t".join(map(str, self.sources)))
|
||||
def __repr__(self):
|
||||
return f"<Module {self.kind} {self.name}>"
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
if self.__output is not None:
|
||||
return self.__output
|
||||
|
||||
def basename(self):
|
||||
if self.__basename is not None:
|
||||
return self.__basename
|
||||
if self.kind == "lib":
|
||||
return f"lib{self.name}.a"
|
||||
return f"lib{self.name}"
|
||||
else:
|
||||
return f"{self.name}.elf"
|
||||
return self.name
|
||||
|
||||
@output.setter
|
||||
def output(self, value):
|
||||
self.__output = value
|
||||
@basename.setter
|
||||
def basename(self, value):
|
||||
self.__basename = value
|
||||
|
||||
def get_output(self, static=False):
|
||||
if self.outfile is not None:
|
||||
return self.outfile
|
||||
elif self.kind == "headers":
|
||||
return None
|
||||
else:
|
||||
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
|
||||
return self.basename + ext.get(self.kind, "")
|
||||
|
||||
@classmethod
|
||||
def update(cls, mods):
|
||||
@@ -114,66 +146,28 @@ class Module:
|
||||
from collections import defaultdict
|
||||
from ninja.ninja_syntax import Writer
|
||||
|
||||
def walk_deps(deps):
|
||||
open_set = set(deps)
|
||||
closed_set = set()
|
||||
while open_set:
|
||||
dep = open_set.pop()
|
||||
closed_set.add(dep)
|
||||
open_set |= {m for m in dep.depmods if not m in closed_set}
|
||||
return closed_set
|
||||
def walk_deps(deps, static, results):
|
||||
for mod in deps:
|
||||
if static or mod.name not in results:
|
||||
results[mod.name] = (mod, static)
|
||||
walk_deps(mod.depmods, static or mod.static, results)
|
||||
|
||||
all_deps = walk_deps(self.depmods)
|
||||
all_deps = {}
|
||||
walk_deps(self.depmods, self.static, all_deps)
|
||||
all_deps = all_deps.values()
|
||||
|
||||
def gather_phony(build, deps, child_rel, add_headers=False):
|
||||
def gather_phony(build, deps, child_rel):
|
||||
phony = ".headers.phony"
|
||||
child_phony = [child_rel(phony, module=c.name)
|
||||
for c in all_deps]
|
||||
|
||||
header_targets = []
|
||||
if add_headers:
|
||||
if not self.no_libc:
|
||||
header_targets.append(f"${{build_root}}/include/libc/{phony}")
|
||||
if self.public_headers:
|
||||
header_targets.append(f"${{build_root}}/include/{self.name}/{phony}")
|
||||
for c, _ in all_deps]
|
||||
|
||||
build.build(
|
||||
rule = "touch",
|
||||
outputs = [mod_rel(phony)],
|
||||
implicit = child_phony + header_targets,
|
||||
implicit = child_phony,
|
||||
order_only = list(map(mod_rel, deps)),
|
||||
)
|
||||
|
||||
filename = str(output / f"headers.{self.name}.ninja")
|
||||
with open(filename, "w") as buildfile:
|
||||
build = Writer(buildfile)
|
||||
|
||||
build.comment("This file is automatically generated by bonnibel")
|
||||
build.newline()
|
||||
|
||||
build.variable("module_dir", f"${{build_root}}/include/{self.name}")
|
||||
|
||||
header_deps = []
|
||||
|
||||
inputs = []
|
||||
headers = set(self.public_headers)
|
||||
while headers:
|
||||
source = headers.pop()
|
||||
headers.update(source.next)
|
||||
|
||||
if source.action:
|
||||
build.newline()
|
||||
build.build(rule=source.action, **source.args)
|
||||
|
||||
if source.gather:
|
||||
header_deps += list(source.outputs)
|
||||
|
||||
if source.input:
|
||||
inputs.extend(map(mod_rel, source.outputs))
|
||||
|
||||
build.newline()
|
||||
gather_phony(build, header_deps, include_rel)
|
||||
|
||||
filename = str(output / f"module.{self.name}.ninja")
|
||||
with open(filename, "w") as buildfile:
|
||||
build = Writer(buildfile)
|
||||
@@ -182,13 +176,21 @@ class Module:
|
||||
build.newline()
|
||||
|
||||
build.variable("module_dir", target_rel(self.name + ".dir"))
|
||||
build.variable("module_kind", self.kind)
|
||||
build.newline()
|
||||
|
||||
build.include(f"${{target_dir}}/config.{self.kind}.ninja")
|
||||
build.newline()
|
||||
|
||||
modopts = BuildOptions(
|
||||
includes = [self.root, "${module_dir}"],
|
||||
local = [self.root, "${module_dir}"],
|
||||
ld_script = self.ld_script and self.root / self.ld_script,
|
||||
)
|
||||
if self.public_headers:
|
||||
modopts.includes += [f"${{build_root}}/include/{self.name}"]
|
||||
modopts.includes += [
|
||||
self.root / "include",
|
||||
f"${{target_dir}}/{self.name}.dir/include",
|
||||
]
|
||||
|
||||
for key, value in self.variables.items():
|
||||
build.variable(key, value)
|
||||
@@ -207,27 +209,64 @@ class Module:
|
||||
modopts.includes.append(str(incpath))
|
||||
modopts.includes.append(destpath)
|
||||
|
||||
all_deps = walk_deps(self.depmods)
|
||||
for dep in all_deps:
|
||||
for dep, static in all_deps:
|
||||
if dep.public_headers:
|
||||
modopts.includes += [f"${{build_root}}/include/{dep.name}"]
|
||||
if dep.include_phase == "normal":
|
||||
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
|
||||
elif dep.include_phase == "late":
|
||||
modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
|
||||
else:
|
||||
from . import BonnibelError
|
||||
raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}")
|
||||
|
||||
if dep.kind == "lib":
|
||||
modopts.libs.append(target_rel(dep.output))
|
||||
if dep.kind == "headers":
|
||||
continue
|
||||
elif dep.kind == "lib":
|
||||
modopts.libs.append((target_rel(dep.get_output(static)), static))
|
||||
else:
|
||||
modopts.order_only.append(target_rel(dep.output))
|
||||
modopts.order_only.append(target_rel(dep.get_output(static)))
|
||||
|
||||
cc_includes = []
|
||||
if modopts.local:
|
||||
cc_includes += [f"-iquote{i}" for i in modopts.local]
|
||||
|
||||
if modopts.includes:
|
||||
build.variable("ccflags", ["${ccflags}"] + [f"-I{i}" for i in modopts.includes])
|
||||
build.variable("asflags", ["${asflags}"] + [f"-I{i}" for i in modopts.includes])
|
||||
cc_includes += [f"-I{i}" for i in modopts.includes]
|
||||
|
||||
if modopts.late:
|
||||
cc_includes += [f"-idirafter{i}" for i in modopts.late]
|
||||
|
||||
if cc_includes:
|
||||
build.variable("ccflags", ["${ccflags}"] + cc_includes)
|
||||
|
||||
as_includes = [f"-I{d}" for d in modopts.local + modopts.includes + modopts.late]
|
||||
if as_includes:
|
||||
build.variable("asflags", ["${asflags}"] + as_includes)
|
||||
|
||||
if modopts.libs:
|
||||
build.variable("libs", ["${libs}"] + modopts.libs)
|
||||
build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
|
||||
|
||||
if modopts.ld_script:
|
||||
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
|
||||
|
||||
header_deps = []
|
||||
inputs = []
|
||||
headers = set(self.public_headers)
|
||||
while headers:
|
||||
source = headers.pop()
|
||||
headers.update(source.next)
|
||||
|
||||
if source.action:
|
||||
build.newline()
|
||||
build.build(rule=source.action, **source.args)
|
||||
|
||||
if source.gather:
|
||||
header_deps += list(source.outputs)
|
||||
|
||||
if source.input:
|
||||
inputs.extend(map(mod_rel, source.outputs))
|
||||
|
||||
build.newline()
|
||||
|
||||
inputs = []
|
||||
sources = set(self.sources)
|
||||
@@ -245,11 +284,13 @@ class Module:
|
||||
if source.input:
|
||||
inputs.extend(map(mod_rel, source.outputs))
|
||||
|
||||
build.newline()
|
||||
gather_phony(build, header_deps, target_rel)
|
||||
|
||||
gather_phony(build, header_deps, target_rel, add_headers=True)
|
||||
if self.kind == "headers":
|
||||
# Header-only, don't output a build rule
|
||||
return
|
||||
|
||||
output = target_rel(self.output)
|
||||
output = target_rel(self.get_output())
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = self.kind,
|
||||
@@ -257,6 +298,8 @@ class Module:
|
||||
inputs = inputs,
|
||||
implicit = modopts.implicit,
|
||||
order_only = modopts.order_only,
|
||||
variables = {"name": self.name,
|
||||
"soname": self.get_output()},
|
||||
)
|
||||
|
||||
dump = output + ".dump"
|
||||
@@ -268,14 +311,34 @@ class Module:
|
||||
variables = {"name": self.name},
|
||||
)
|
||||
|
||||
s_output = target_rel(self.get_output(static=True))
|
||||
if s_output != output:
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = self.kind + "_static",
|
||||
outputs = s_output,
|
||||
inputs = inputs,
|
||||
order_only = modopts.order_only,
|
||||
variables = {"name": self.name},
|
||||
)
|
||||
|
||||
dump = s_output + ".dump"
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = "dump",
|
||||
outputs = dump,
|
||||
inputs = s_output,
|
||||
variables = {"name": self.name},
|
||||
)
|
||||
|
||||
if self.default:
|
||||
build.newline()
|
||||
build.default(output)
|
||||
build.default(dump)
|
||||
|
||||
def add_input(self, path, **kwargs):
|
||||
from .source import Source
|
||||
s = Source(self.root, path, **kwargs)
|
||||
from .source import make_source
|
||||
s = make_source(self.root, path, **kwargs)
|
||||
self.sources.append(s)
|
||||
return s.outputs
|
||||
|
||||
|
||||
@@ -12,14 +12,17 @@ class Project:
|
||||
|
||||
def generate(self, root, output, modules, config, manifest_file):
|
||||
import sys
|
||||
import bonnibel
|
||||
from os.path import join
|
||||
from ninja.ninja_syntax import Writer
|
||||
from .target import Target
|
||||
|
||||
targets = set()
|
||||
kinds = set()
|
||||
for mod in modules.values():
|
||||
targets.update({Target.load(root, t, config) for t in mod.targets})
|
||||
targets.update(mod.targets)
|
||||
kinds.add(mod.kind)
|
||||
|
||||
from .config import generate_configs
|
||||
config_deps = generate_configs(root, output, config, targets, kinds)
|
||||
|
||||
with open(output / "build.ninja", "w") as buildfile:
|
||||
build = Writer(buildfile)
|
||||
@@ -28,6 +31,7 @@ class Project:
|
||||
build.variable("ninja_required_version", "1.3")
|
||||
build.variable("build_root", output)
|
||||
build.variable("source_root", root)
|
||||
build.variable("build_config", config)
|
||||
build.newline()
|
||||
|
||||
build.include(root / "assets/build/rules.ninja")
|
||||
@@ -46,11 +50,7 @@ class Project:
|
||||
build.newline()
|
||||
|
||||
for target in targets:
|
||||
build.subninja(output / target.name / "target.ninja")
|
||||
build.newline()
|
||||
|
||||
for mod in modules.values():
|
||||
build.subninja(output / f"headers.{mod.name}.ninja")
|
||||
build.subninja(output / target / "target.ninja")
|
||||
build.newline()
|
||||
|
||||
build.build(
|
||||
@@ -154,6 +154,9 @@ class Project:
|
||||
for program in manifest.drivers:
|
||||
add_initrd_stripped("jsix/drivers", program)
|
||||
|
||||
for program in manifest.libs:
|
||||
add_initrd_stripped("jsix/lib", program)
|
||||
|
||||
syms = manifest.add_data("symbol_table.dat",
|
||||
"Symbol table", ("symbols",))
|
||||
|
||||
@@ -163,7 +166,7 @@ class Project:
|
||||
build.build(
|
||||
rule = "makest",
|
||||
outputs = [syms_out],
|
||||
inputs = [f"${{build_root}}/kernel/{modules['kernel'].output}"],
|
||||
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
|
||||
)
|
||||
fatroot_content.append(syms_out)
|
||||
manifest.symbols = syms_file
|
||||
@@ -232,8 +235,7 @@ class Project:
|
||||
[f"{self.root}/configure", str(manifest_file)] + \
|
||||
[str(mod.modfile) for mod in modules.values()]
|
||||
|
||||
for target in targets:
|
||||
regen_implicits += target.depfiles
|
||||
regen_implicits += list(map(str, config_deps))
|
||||
|
||||
build.build(
|
||||
rule = "compdb",
|
||||
@@ -249,7 +251,7 @@ class Project:
|
||||
implicit = regen_implicits,
|
||||
implicit_outputs =
|
||||
[f"module.{mod.name}.ninja" for mod in modules.values()] +
|
||||
[f"{target.name}/target.ninja" for target in targets] +
|
||||
[f"{target}/target.ninja" for target in targets] +
|
||||
[boot_config],
|
||||
)
|
||||
|
||||
@@ -257,9 +259,9 @@ class Project:
|
||||
build.default(["${build_root}/jsix.img"])
|
||||
|
||||
for target in targets:
|
||||
mods = [m.name for m in modules.values() if target.name in m.targets]
|
||||
mods = [m.name for m in modules.values() if target in m.targets]
|
||||
|
||||
targetdir = output / target.name
|
||||
targetdir = output / target
|
||||
targetdir.mkdir(exist_ok=True)
|
||||
|
||||
buildfilename = str(targetdir / "target.ninja")
|
||||
@@ -268,17 +270,16 @@ class Project:
|
||||
build.comment("This file is automatically generated by bonnibel")
|
||||
build.newline()
|
||||
|
||||
build.variable("target", target.name)
|
||||
build.variable("target_dir", output / target.name)
|
||||
build.variable("target", target)
|
||||
build.variable("target_dir", output / target)
|
||||
build.newline()
|
||||
|
||||
for name, value in target.items():
|
||||
build.variable(name, value)
|
||||
build.include(f"{target}/config.ninja")
|
||||
|
||||
build.newline()
|
||||
for kind in ('defs', 'run'):
|
||||
for lang in ('c', 'cpp'):
|
||||
deffile = str(output / target.name / f"{lang}.{kind}")
|
||||
deffile = str(output / target / f"{lang}.{kind}")
|
||||
|
||||
build.build(
|
||||
rule = f"dump_{lang}_{kind}",
|
||||
|
||||
@@ -72,22 +72,21 @@ class ParseSource(Source):
|
||||
variables = dict(name=self.path),
|
||||
)
|
||||
|
||||
class HeaderSource(Source):
|
||||
class CopySource(ParseSource):
|
||||
action = "cp"
|
||||
gather = True
|
||||
|
||||
def __init__(self, path, root = "${module_dir}", deps=tuple(), prefix = ""):
|
||||
self.path = path
|
||||
self.root = root
|
||||
self.deps = deps
|
||||
self.prefix = prefix
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
return (self.path,)
|
||||
def output(self):
|
||||
from pathlib import Path
|
||||
return Path(self.prefix) / self.path
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return dict(
|
||||
outputs = [mod_rel(self.path)],
|
||||
inputs = [join(self.root, self.path)],
|
||||
implicit = list(map(_resolve, self.deps)),
|
||||
variables = dict(name=self.path),
|
||||
)
|
||||
class HeaderSource(Source): pass
|
||||
|
||||
class CompileSource(Source):
|
||||
action = property(_dynamic_action("compile"))
|
||||
@@ -117,3 +116,6 @@ def make_source(root, path):
|
||||
return HeaderSource(path, root)
|
||||
else:
|
||||
raise RuntimeError(f"{path} has no Source type")
|
||||
|
||||
def make_copy_source(root, path, prefix = ""):
|
||||
return CopySource(path, root, prefix=prefix)
|
||||
@@ -1,50 +0,0 @@
|
||||
class Target(dict):
|
||||
__targets = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls, root, name, config=None):
|
||||
from . import load_config
|
||||
|
||||
if (name, config) in cls.__targets:
|
||||
return cls.__targets[(name, config)]
|
||||
|
||||
configs = root / "assets/build"
|
||||
|
||||
dicts = []
|
||||
depfiles = []
|
||||
basename = name
|
||||
if config:
|
||||
basename += f"-{config}"
|
||||
|
||||
while basename is not None:
|
||||
filename = str(configs / (basename + ".yaml"))
|
||||
depfiles.append(filename)
|
||||
desc = load_config(filename)
|
||||
basename = desc.get("extends")
|
||||
dicts.append(desc.get("variables", dict()))
|
||||
|
||||
t = Target(name, config, depfiles)
|
||||
for d in reversed(dicts):
|
||||
for k, v in d.items():
|
||||
if isinstance(v, (list, tuple)):
|
||||
t[k] = t.get(k, list()) + list(v)
|
||||
elif isinstance(v, dict):
|
||||
t[k] = t.get(k, dict())
|
||||
t[k].update(v)
|
||||
else:
|
||||
t[k] = v
|
||||
|
||||
cls.__targets[(name, config)] = t
|
||||
return t
|
||||
|
||||
def __init__(self, name, config, depfiles):
|
||||
self.__name = name
|
||||
self.__config = config
|
||||
self.__depfiles = tuple(depfiles)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.__name, self.__config))
|
||||
|
||||
name = property(lambda self: self.__name)
|
||||
config = property(lambda self: self.__config)
|
||||
depfiles = property(lambda self: self.__depfiles)
|
||||
62
scripts/debug_buddy_alloc.gdb
Normal file
62
scripts/debug_buddy_alloc.gdb
Normal file
@@ -0,0 +1,62 @@
|
||||
das -enabled off
|
||||
|
||||
break heap_allocator.cpp:200
|
||||
commands
|
||||
silent
|
||||
printf "n %016lx %d\n", m_end, current
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:206
|
||||
commands
|
||||
silent
|
||||
printf "N %016lx %d\n", m_end, order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator::register_free_block
|
||||
commands
|
||||
silent
|
||||
printf "F %016lx %d\n", block, order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:118
|
||||
commands
|
||||
silent
|
||||
printf "f %016lx %d\n", block, info->order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:241
|
||||
commands
|
||||
silent
|
||||
printf "S %016lx %d\n", block, order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:158
|
||||
commands
|
||||
silent
|
||||
printf "P %016lx %d\n", block, order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:180
|
||||
commands
|
||||
silent
|
||||
printf "p %016lx %d\n", block, order
|
||||
continue
|
||||
end
|
||||
|
||||
break heap_allocator.cpp:182
|
||||
commands
|
||||
silent
|
||||
printf "M %016lx %016lx %d\n", block, buddy, order
|
||||
continue
|
||||
end
|
||||
|
||||
set logging file buddy_allocs.txt
|
||||
set logging overwrite on
|
||||
set logging enabled on
|
||||
continue
|
||||
18
scripts/debug_frame_alloc.gdb
Normal file
18
scripts/debug_frame_alloc.gdb
Normal file
@@ -0,0 +1,18 @@
|
||||
das -enabled off
|
||||
break frame_allocator.cpp:62
|
||||
commands
|
||||
silent
|
||||
printf "+ %016lx %3d\n", *address, n
|
||||
continue
|
||||
end
|
||||
break frame_allocator.cpp:95
|
||||
commands
|
||||
silent
|
||||
printf "- %016lx %3d\n", address, count
|
||||
continue
|
||||
end
|
||||
|
||||
set logging file frame_allocs.txt
|
||||
set logging overwrite on
|
||||
set logging enabled on
|
||||
continue
|
||||
29
scripts/debug_heap_alloc.gdb
Normal file
29
scripts/debug_heap_alloc.gdb
Normal file
@@ -0,0 +1,29 @@
|
||||
das -enabled off
|
||||
break heap_allocator.cpp:81
|
||||
commands
|
||||
silent
|
||||
printf "+ %016lx %4d\n", block, length
|
||||
continue
|
||||
end
|
||||
break heap_allocator.cpp:86
|
||||
commands
|
||||
silent
|
||||
printf "+ %016lx %4d\n", block, length
|
||||
continue
|
||||
end
|
||||
break heap_allocator.cpp:120
|
||||
commands
|
||||
silent
|
||||
printf "- %016lx\n", p
|
||||
continue
|
||||
end
|
||||
break heap_allocator.cpp:140
|
||||
commands
|
||||
silent
|
||||
printf "> %016lx %4d %4d\n", p, old_length, new_length
|
||||
continue
|
||||
end
|
||||
set logging file heap_allocs.txt
|
||||
set logging overwrite on
|
||||
set logging enabled on
|
||||
continue
|
||||
File diff suppressed because one or more lines are too long
@@ -42,31 +42,37 @@ class PrimitiveRef(Primitive):
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
_inttypes = {
|
||||
"int": "int",
|
||||
"uint": "unsigned",
|
||||
"size": "size_t",
|
||||
"address": "uintptr_t",
|
||||
|
||||
"int8": "int8_t",
|
||||
"uint8": "uint8_t",
|
||||
"int16": "int16_t",
|
||||
"uint16": "uint16_t",
|
||||
"int32": "int32_t",
|
||||
"uint32": "uint32_t",
|
||||
"int64": "int64_t",
|
||||
"uint64": "uint64_t",
|
||||
}
|
||||
|
||||
_primitives = {
|
||||
"string": PrimitiveRef("string", "char"),
|
||||
"buffer": PrimitiveRef("buffer", "void", counted=True),
|
||||
|
||||
"int": Primitive("int", "int"),
|
||||
"uint": Primitive("uint", "unsigned"),
|
||||
"size": Primitive("size", "size_t"),
|
||||
"address": Primitive("address", "uintptr_t"),
|
||||
|
||||
"int8": Primitive("int8", "int8_t"),
|
||||
"uint8": Primitive("uint8", "uint8_t"),
|
||||
|
||||
"int16": Primitive("int16", "int16_t"),
|
||||
"uint16": Primitive("uint16", "uint16_t"),
|
||||
|
||||
"int32": Primitive("int32", "int32_t"),
|
||||
"uint32": Primitive("uint32", "uint32_t"),
|
||||
|
||||
"int64": Primitive("int64", "int64_t"),
|
||||
"uint64": Primitive("uint64", "uint64_t"),
|
||||
}
|
||||
|
||||
def get_primitive(name):
|
||||
p = _primitives.get(name)
|
||||
if not p:
|
||||
from ..errors import InvalidType
|
||||
raise InvalidType(name)
|
||||
return p
|
||||
if p: return p
|
||||
|
||||
it = _inttypes.get(name.replace('*', ''))
|
||||
if it:
|
||||
if '*' in name:
|
||||
return PrimitiveRef(name, it)
|
||||
else:
|
||||
return Primitive(name, it)
|
||||
|
||||
from ..errors import InvalidType
|
||||
raise InvalidType(name)
|
||||
|
||||
93
scripts/parse_buddy_allocs.py
Executable file
93
scripts/parse_buddy_allocs.py
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
class block:
|
||||
def __init__(self, start, order, free=False):
|
||||
self.start = start
|
||||
self.order = order
|
||||
self.free = free
|
||||
|
||||
@property
|
||||
def end(self):
|
||||
return self.start + (1<<self.order)
|
||||
|
||||
def overlaps(self, other):
|
||||
return other.start < self.end and other.end > self.start
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.start:016x} {self.order:2} {self.free and 'free' or 'used'}]"
|
||||
|
||||
def get_block(blocks, addr, order, reason):
|
||||
b = blocks.get(addr)
|
||||
if b is None:
|
||||
print(f"ERROR: {reason} unknown block [{addr:016x}, {order}]")
|
||||
elif b.order != order:
|
||||
print(f"ERROR: {reason} block {b} for order {order}")
|
||||
else:
|
||||
return b
|
||||
return None
|
||||
|
||||
def new_block(blocks, addr, order):
|
||||
b = block(addr, order)
|
||||
for existing in blocks.values():
|
||||
if b.overlaps(existing):
|
||||
print(f"ERROR: new block {b} overlaps existing {existing}")
|
||||
blocks[addr] = b
|
||||
|
||||
def free_block(blocks, addr, order, free):
|
||||
s = free and "freeing" or "popping"
|
||||
b = get_block(blocks, addr, order, s)
|
||||
if b and b.free == free:
|
||||
print(f"ERROR: {s} block {b}")
|
||||
elif b:
|
||||
b.free = free
|
||||
|
||||
def split_block(blocks, addr, order):
|
||||
b = get_block(blocks, addr, order+1, "splitting")
|
||||
if b:
|
||||
b.order = order
|
||||
buddy = b.start ^ (1<<order)
|
||||
blocks[buddy] = block(buddy, order)
|
||||
|
||||
def merge_blocks(blocks, addr1, addr2, order):
|
||||
b1 = get_block(blocks, addr1, order, "merging")
|
||||
b2 = get_block(blocks, addr2, order, "merging")
|
||||
if b1.start > b2.start:
|
||||
b1, b2 = b2, b1
|
||||
del blocks[b2.start]
|
||||
b1.order = order + 1
|
||||
|
||||
def parse_line(blocks, line):
|
||||
args = line.strip().split()
|
||||
match args:
|
||||
case ['N', addr, order]:
|
||||
new_block(blocks, int(addr, base=16), int(order))
|
||||
case ['n', addr, order]:
|
||||
new_block(blocks, int(addr, base=16), int(order))
|
||||
case ['P', addr, order]:
|
||||
free_block(blocks, int(addr, base=16), int(order), False)
|
||||
case ['p', addr, order]:
|
||||
free_block(blocks, int(addr, base=16), int(order), False)
|
||||
case ['F', addr, order]:
|
||||
free_block(blocks, int(addr, base=16), int(order), True)
|
||||
case ['S', addr, order]:
|
||||
split_block(blocks, int(addr, base=16), int(order))
|
||||
case ['M', addr1, addr2, order]:
|
||||
merge_blocks(blocks, int(addr1, base=16), int(addr2, base=16), int(order))
|
||||
case _:
|
||||
pass
|
||||
|
||||
def parse_file(f):
|
||||
blocks = {}
|
||||
for line in f.readlines():
|
||||
parse_line(blocks, line)
|
||||
|
||||
#for addr in sorted(blocks.keys()):
|
||||
# print(f"{addr:09x}: {blocks[addr]}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1]) as f:
|
||||
parse_file(f)
|
||||
else:
|
||||
parse_file(sys.stdin)
|
||||
40
scripts/parse_frame_allocs.py
Executable file
40
scripts/parse_frame_allocs.py
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
def add_maps(allocs, addr, count):
|
||||
for i in range(count):
|
||||
if addr+i in allocs:
|
||||
print(f"ERROR: frame {addr+i:012x} map collision.")
|
||||
else:
|
||||
#print(f" frame {addr+i:012x} mapped")
|
||||
allocs.add(addr+i)
|
||||
|
||||
def remove_maps(allocs, addr, count):
|
||||
for i in range(count):
|
||||
if addr+i not in allocs:
|
||||
print(f" WARN: removing unmapped frame {addr+i:012x}")
|
||||
else:
|
||||
#print(f" frame {addr+i:012x} unmapped")
|
||||
allocs.remove(addr+i)
|
||||
|
||||
def parse_line(allocs, line):
|
||||
args = line.strip().split()
|
||||
match args:
|
||||
case ['+', addr, count]:
|
||||
add_maps(allocs, int(addr, base=16), int(count))
|
||||
case ['-', addr, count]:
|
||||
remove_maps(allocs, int(addr, base=16), int(count))
|
||||
case _:
|
||||
pass
|
||||
|
||||
def parse_file(f):
|
||||
allocs = set()
|
||||
for line in f.readlines():
|
||||
parse_line(allocs, line)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1]) as f:
|
||||
parse_file(f)
|
||||
else:
|
||||
parse_file(sys.stdin)
|
||||
62
scripts/parse_heap_allocs.py
Executable file
62
scripts/parse_heap_allocs.py
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from bisect import bisect_left, insort
|
||||
from operator import attrgetter
|
||||
|
||||
by_start = attrgetter('start')
|
||||
|
||||
class alloc:
|
||||
def __init__(self, start, size):
|
||||
self.start = start
|
||||
self.size = size
|
||||
|
||||
@property
|
||||
def end(self):
|
||||
return self.start + self.size
|
||||
|
||||
def overlaps(self, other):
|
||||
return other.start < self.end and other.end > self.start
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.start:012x} - {self.end:012x}]"
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.start > other.start
|
||||
|
||||
def add_alloc(allocs, addr, length):
|
||||
a = alloc(addr, length)
|
||||
for existing in allocs:
|
||||
if a.overlaps(existing):
|
||||
print(f"ERROR: allocation {a} overlaps existing {existing}")
|
||||
insort(allocs, a)
|
||||
|
||||
def remove_alloc(allocs, addr):
|
||||
a = alloc(addr, 0)
|
||||
i = bisect_left(allocs, a)
|
||||
if len(allocs) > i and allocs[i].start == addr:
|
||||
del allocs[i]
|
||||
else:
|
||||
print(f"ERROR: freeing unallocated {a}")
|
||||
|
||||
def parse_line(allocs, line):
|
||||
args = line.strip().split()
|
||||
match args:
|
||||
case ['+', addr, length]:
|
||||
add_alloc(allocs, int(addr, base=16), int(length))
|
||||
case ['-', addr]:
|
||||
remove_alloc(allocs, int(addr, base=16))
|
||||
case _:
|
||||
pass
|
||||
|
||||
def parse_file(f):
|
||||
allocs = []
|
||||
for line in f.readlines():
|
||||
parse_line(allocs, line)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
with open(sys.argv[1]) as f:
|
||||
parse_file(f)
|
||||
else:
|
||||
parse_file(sys.stdin)
|
||||
124
scripts/print_got.py
Executable file
124
scripts/print_got.py
Executable file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
def section_header(name):
|
||||
print()
|
||||
print(name)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def print_dyn(elf):
|
||||
section = elf.get_section_by_name(".dynamic")
|
||||
if section is None:
|
||||
return
|
||||
|
||||
section_header(".dynamic")
|
||||
for tag in section.iter_tags():
|
||||
print(tag)
|
||||
|
||||
|
||||
def print_got(elf, name):
|
||||
import struct
|
||||
|
||||
section = elf.get_section_by_name(name)
|
||||
if section is None:
|
||||
return
|
||||
|
||||
section_header(name)
|
||||
base_ip = section['sh_addr']
|
||||
|
||||
data = section.data()
|
||||
n = section.data_size // 8
|
||||
for i in range(n):
|
||||
addr = struct.unpack_from("Q", data, i*8)[0]
|
||||
print(f"[{i:2x}]: {base_ip+i*8:6x} {addr:16x}")
|
||||
|
||||
|
||||
def print_plt(elf):
|
||||
from iced_x86 import Decoder, Formatter, FormatterSyntax
|
||||
|
||||
section_header(".plt")
|
||||
|
||||
section = elf.get_section_by_name(".plt")
|
||||
n = section.data_size // 16
|
||||
data = section.data()
|
||||
|
||||
frm = Formatter(FormatterSyntax.NASM)
|
||||
frm.digit_separator = "'"
|
||||
frm.first_operand_char_index = 8
|
||||
frm.hex_prefix = "0x"
|
||||
frm.hex_suffix = ""
|
||||
frm.leading_zeros = False
|
||||
frm.rip_relative_addresses = False
|
||||
frm.small_hex_numbers_in_decimal = False
|
||||
frm.uppercase_hex = False
|
||||
|
||||
base_ip = section['sh_addr']
|
||||
|
||||
for i in range(n):
|
||||
entry = data[ i*16 : (i+1)*16 ]
|
||||
d = Decoder(64, entry, ip=base_ip + i*16)
|
||||
|
||||
indent = f"[{i:2x}]:"
|
||||
for instr in d:
|
||||
disasm = frm.format(instr)
|
||||
print(f"{indent:6} {instr.ip:6x} {disasm}")
|
||||
indent = ""
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def print_gnu_hash(elf):
|
||||
hash_section = elf.get_section_by_name(".gnu.hash")
|
||||
data = hash_section.data()
|
||||
|
||||
import struct
|
||||
(nbuckets, symoff, bloom_sz, bloom_sh) = struct.unpack_from("IIII", data, 0)
|
||||
blooms = struct.unpack_from(f"{bloom_sz}Q", data, 16)
|
||||
buckets = struct.unpack_from(f"{nbuckets}I", data, 16+(bloom_sz*8))
|
||||
|
||||
p = 16 + (bloom_sz*8) + (nbuckets*4)
|
||||
n = (len(data) - p) // 4
|
||||
chains = struct.unpack_from(f"{n}I", data, p)
|
||||
|
||||
section_header(".gnu.hash")
|
||||
print(f" Bucket Count: {nbuckets}")
|
||||
print(f"Symbol Offset: {symoff}")
|
||||
print(f" Buckets: {buckets}")
|
||||
|
||||
print("\n Bloom words:")
|
||||
for i in range(len(blooms)):
|
||||
print(f" [{i:2}]: {blooms[i]:016x}")
|
||||
|
||||
print("\n Hashes:")
|
||||
for i in range(len(chains)):
|
||||
h = chains[i]
|
||||
end = ""
|
||||
if (h & 1) == 1:
|
||||
end = "END"
|
||||
bloom_idx = (h>>6) % bloom_sz
|
||||
bloom_msk = ((1<<(h%64)) | (1<<((h>>bloom_sh)%64)))
|
||||
print(f" [{i+symoff:2}]: {h:08x} {end:5} {bloom_idx:2}/{bloom_msk:016x}")
|
||||
|
||||
|
||||
def print_tables(filename):
|
||||
from elftools.elf.elffile import ELFFile
|
||||
|
||||
print(filename)
|
||||
print("=" * 50)
|
||||
|
||||
with open(filename, 'rb') as f:
|
||||
elf = ELFFile(f)
|
||||
print_got(elf, ".got")
|
||||
print_got(elf, ".got.plt")
|
||||
print_plt(elf)
|
||||
print_dyn(elf)
|
||||
print_gnu_hash(elf)
|
||||
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
for filename in sys.argv[1:]:
|
||||
print_tables(filename)
|
||||
1
scripts/wsl_set_vnchost.fish
Normal file
1
scripts/wsl_set_vnchost.fish
Normal file
@@ -0,0 +1 @@
|
||||
set -gx VNCHOST (ip -j route list default | jq -r '.[0].gateway'):5500
|
||||
@@ -71,10 +71,10 @@ allocator::add_modules()
|
||||
allocate_pages(1, alloc_type::init_args, true));
|
||||
|
||||
if (m_modules)
|
||||
m_modules->next = reinterpret_cast<uintptr_t>(mods);
|
||||
m_modules->next = mods;
|
||||
|
||||
m_modules = mods;
|
||||
m_next_mod = mods->modules;
|
||||
m_next_mod = reinterpret_cast<module*>(mods+1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,9 +109,9 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
|
||||
}
|
||||
|
||||
module *
|
||||
allocator::allocate_module()
|
||||
allocator::allocate_module(size_t extra)
|
||||
{
|
||||
static constexpr size_t size = sizeof(module);
|
||||
size_t size = sizeof(module) + extra;
|
||||
|
||||
size_t remaining =
|
||||
reinterpret_cast<uintptr_t>(m_modules) + page_size
|
||||
@@ -120,8 +120,8 @@ allocator::allocate_module()
|
||||
if (size > remaining)
|
||||
add_modules();
|
||||
|
||||
++m_modules->count;
|
||||
module *m = m_next_mod;
|
||||
m->bytes = size;
|
||||
m_next_mod = util::offset_pointer(m_next_mod, size);
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
void * allocate_pages(size_t count, alloc_type type, bool zero = false);
|
||||
|
||||
module * allocate_module();
|
||||
module * allocate_module(size_t extra = 0);
|
||||
|
||||
void memset(void *start, size_t size, uint8_t value);
|
||||
void copy(void *to, const void *from, size_t size);
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
boot = module("boot",
|
||||
kind = "exe",
|
||||
output = "boot.efi",
|
||||
outfile = "boot.efi",
|
||||
targets = [ "boot" ],
|
||||
deps = [ "cpu", "elf", "util", "bootproto" ],
|
||||
static = True,
|
||||
sources = [
|
||||
"allocator.cpp",
|
||||
"bootconfig.cpp",
|
||||
|
||||
@@ -79,10 +79,11 @@ check_cpu_supported()
|
||||
status_line status {L"Checking CPU features"};
|
||||
|
||||
cpu::cpu_id cpu;
|
||||
cpu::cpu_id::features features = cpu.validate();
|
||||
cpu::features features = cpu.features();
|
||||
bool supported = true;
|
||||
|
||||
#define CPU_FEATURE_OPT(...)
|
||||
#define CPU_FEATURE_WRN(...)
|
||||
#define CPU_FEATURE_REQ(name, ...) \
|
||||
if (!features[cpu::feature::name]) { \
|
||||
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
|
||||
|
||||
@@ -167,15 +167,15 @@ load_module(
|
||||
fs::file &disk,
|
||||
const wchar_t *name,
|
||||
const wchar_t *path,
|
||||
bootproto::module_type type,
|
||||
uint16_t subtype)
|
||||
bootproto::module_type type)
|
||||
{
|
||||
status_line status(L"Loading module", name);
|
||||
|
||||
bootproto::module *mod = g_alloc.allocate_module();
|
||||
bootproto::module *mod = g_alloc.allocate_module(sizeof(util::buffer));
|
||||
mod->type = type;
|
||||
mod->subtype = subtype;
|
||||
mod->data = load_file(disk, path);
|
||||
|
||||
util::buffer *data = mod->data<util::buffer>();
|
||||
*data = load_file(disk, path);
|
||||
}
|
||||
|
||||
} // namespace loader
|
||||
|
||||
@@ -60,14 +60,12 @@ load_program(
|
||||
/// \arg name The human-readable name of the module
|
||||
/// \arg path The path of the file to load the module from
|
||||
/// \arg type The major type to set on the module
|
||||
/// \arg subtype The subtype to set on the module
|
||||
void
|
||||
load_module(
|
||||
fs::file &disk,
|
||||
const wchar_t *name,
|
||||
const wchar_t *path,
|
||||
bootproto::module_type type,
|
||||
uint16_t subtype);
|
||||
bootproto::module_type type);
|
||||
|
||||
} // namespace loader
|
||||
} // namespace boot
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <bootproto/acpi.h>
|
||||
#include <bootproto/bootconfig.h>
|
||||
#include <bootproto/kernel.h>
|
||||
#include <bootproto/memory.h>
|
||||
@@ -125,7 +126,7 @@ load_resources(
|
||||
loader::parse_program(L"init server", init, args->init);
|
||||
|
||||
loader::load_module(disk, L"initrd", bc.initrd(),
|
||||
bootproto::module_type::initrd, 0);
|
||||
bootproto::module_type::initrd);
|
||||
|
||||
return reinterpret_cast<bootproto::entrypoint>(kentry);
|
||||
}
|
||||
@@ -176,9 +177,23 @@ efi_main(uefi::handle image, uefi::system_table *st)
|
||||
bootproto::entrypoint kentry =
|
||||
load_resources(args, screen, image, pager, bs);
|
||||
|
||||
bootproto::module *acpi_mod =
|
||||
g_alloc.allocate_module(sizeof(bootproto::acpi));
|
||||
acpi_mod->type = bootproto::module_type::acpi;
|
||||
bootproto::acpi *acpi = acpi_mod->data<bootproto::acpi>();
|
||||
acpi->root = args->acpi_table;
|
||||
|
||||
pager.update_kernel_args(args);
|
||||
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
|
||||
|
||||
for (size_t i = 0; i < args->mem_map.count; ++i) {
|
||||
bootproto::mem_entry &e = args->mem_map.pointer[i];
|
||||
if (e.type == bootproto::mem_type::acpi) {
|
||||
acpi->region = util::buffer::from(e.start, e.pages * memory::page_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
args->allocations = allocs;
|
||||
args->init_modules = reinterpret_cast<uintptr_t>(modules);
|
||||
|
||||
@@ -194,6 +209,7 @@ efi_main(uefi::handle image, uefi::system_table *st)
|
||||
|
||||
change_pointer(args);
|
||||
change_pointer(args->pml4);
|
||||
change_pointer(args->symbol_table.pointer);
|
||||
change_pointer(args->init.sections.pointer);
|
||||
|
||||
//status.next();
|
||||
|
||||
@@ -249,13 +249,13 @@ build_frame_blocks(const util::counted<bootproto::mem_entry> &kmap)
|
||||
unsigned i = 0;
|
||||
|
||||
uint64_t b1 = (page_count + 4095) / 4096;
|
||||
blk->map1 = (1 << b1) - 1;
|
||||
blk->map1 = (1ull << b1) - 1;
|
||||
|
||||
uint64_t b2 = (page_count + 63) / 64;
|
||||
uint64_t b2q = b2 / 64;
|
||||
uint64_t b2r = b2 % 64;
|
||||
g_alloc.memset(blk->map2, b2q, 0xff);
|
||||
blk->map2[b2q] = (1 << b2r) - 1;
|
||||
blk->map2[b2q] = (1ull << b2r) - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -269,7 +269,7 @@ build_frame_blocks(const util::counted<bootproto::mem_entry> &kmap)
|
||||
size_t b = blk.count / 64;
|
||||
size_t r = blk.count % 64;
|
||||
g_alloc.memset(blk.bitmap, b*8, 0xff);
|
||||
blk.bitmap[b] = (1 << r) - 1;
|
||||
blk.bitmap[b] = (1ull << r) - 1;
|
||||
|
||||
bitmap += bitmap_size(blk.count);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ pick_mode(uefi::boot_services *bs)
|
||||
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
||||
int pixmode = static_cast<int>(info->pixel_format);
|
||||
|
||||
/*
|
||||
const uint32_t modes = gop->mode->max_mode;
|
||||
for (uint32_t i = 0; i < modes; ++i) {
|
||||
size_t size = 0;
|
||||
@@ -63,6 +64,7 @@ pick_mode(uefi::boot_services *bs)
|
||||
pixmode = new_pixmode;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
screen *s = new screen;
|
||||
s->mode = {
|
||||
@@ -109,20 +111,15 @@ make_module(screen *s)
|
||||
{
|
||||
using bootproto::module;
|
||||
using bootproto::module_type;
|
||||
using bootproto::device_type;
|
||||
using bootproto::devices::uefi_fb;
|
||||
namespace devices = bootproto::devices;
|
||||
|
||||
uefi_fb *fb = new uefi_fb;
|
||||
module *mod = g_alloc.allocate_module(sizeof(devices::uefi_fb));
|
||||
mod->type = module_type::device;
|
||||
mod->type_id = devices::type_id_uefi_fb;
|
||||
|
||||
devices::uefi_fb *fb = mod->data<devices::uefi_fb>();
|
||||
fb->framebuffer = s->framebuffer;
|
||||
fb->mode = s->mode;
|
||||
|
||||
module *mod = g_alloc.allocate_module();
|
||||
mod->type = module_type::device;
|
||||
mod->subtype = static_cast<uint16_t>(device_type::uefi_fb);
|
||||
mod->data = {
|
||||
.pointer = fb,
|
||||
.count = sizeof(uefi_fb),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace video
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/misc.h> // for byteswap32
|
||||
|
||||
struct acpi_table_header
|
||||
namespace acpi {
|
||||
|
||||
struct table_header
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t length;
|
||||
@@ -18,24 +20,26 @@ struct acpi_table_header
|
||||
uint32_t creator_id;
|
||||
uint32_t creator_revision;
|
||||
|
||||
bool validate(uint32_t expected_type = 0) const;
|
||||
bool validate() const { return util::checksum(this, length) == 0; }
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
#define TABLE_HEADER(signature) \
|
||||
static constexpr uint32_t type_id = util::byteswap32(signature); \
|
||||
acpi_table_header header;
|
||||
|
||||
table_header header;
|
||||
|
||||
template <typename T>
|
||||
bool acpi_validate(const T *t) { return t->header.validate(T::type_id); }
|
||||
bool validate(const T *t) {
|
||||
return t->header.validate(T::type_id);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t acpi_table_entries(const T *t, size_t size)
|
||||
{
|
||||
size_t table_entries(const T *t, size_t size) {
|
||||
return (t->header.length - sizeof(T)) / size;
|
||||
}
|
||||
|
||||
enum class acpi_gas_type : uint8_t
|
||||
|
||||
enum class gas_type : uint8_t
|
||||
{
|
||||
system_memory,
|
||||
system_io,
|
||||
@@ -46,9 +50,9 @@ enum class acpi_gas_type : uint8_t
|
||||
functional_fixed = 0x7f
|
||||
};
|
||||
|
||||
struct acpi_gas
|
||||
struct gas
|
||||
{
|
||||
acpi_gas_type type;
|
||||
gas_type type;
|
||||
|
||||
uint8_t reg_bits;
|
||||
uint8_t reg_offset;
|
||||
@@ -58,7 +62,7 @@ struct acpi_gas
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class acpi_fadt_flags : uint32_t
|
||||
enum class fadt_flags : uint32_t
|
||||
{
|
||||
wbinvd = 0x00000001,
|
||||
wbinvd_flush = 0x00000002,
|
||||
@@ -83,9 +87,9 @@ enum class acpi_fadt_flags : uint32_t
|
||||
hw_reduced_acpi = 0x00100000,
|
||||
low_pwr_s0_idle = 0x00200000
|
||||
};
|
||||
is_bitfield(acpi_fadt_flags);
|
||||
is_bitfield(fadt_flags);
|
||||
|
||||
struct acpi_fadt
|
||||
struct fadt
|
||||
{
|
||||
TABLE_HEADER('FACP');
|
||||
|
||||
@@ -130,9 +134,9 @@ struct acpi_fadt
|
||||
|
||||
uint16_t iapc_boot_arch;
|
||||
uint8_t reserved1;
|
||||
acpi_fadt_flags flags;
|
||||
fadt_flags flags;
|
||||
|
||||
acpi_gas reset_reg;
|
||||
gas reset_reg;
|
||||
uint8_t reset_value;
|
||||
|
||||
uint16_t arm_boot_arch;
|
||||
@@ -142,28 +146,28 @@ struct acpi_fadt
|
||||
uint64_t x_facs;
|
||||
uint64_t x_dsdt;
|
||||
|
||||
acpi_gas x_pm1a_event_block;
|
||||
acpi_gas x_pm1b_event_block;
|
||||
acpi_gas x_pm1a_control_block;
|
||||
acpi_gas x_pm1b_control_block;
|
||||
acpi_gas x_pm2_control_block;
|
||||
acpi_gas x_pm_timer_block;
|
||||
acpi_gas x_gpe0_block;
|
||||
acpi_gas x_gpe1_block;
|
||||
gas x_pm1a_event_block;
|
||||
gas x_pm1b_event_block;
|
||||
gas x_pm1a_control_block;
|
||||
gas x_pm1b_control_block;
|
||||
gas x_pm2_control_block;
|
||||
gas x_pm_timer_block;
|
||||
gas x_gpe0_block;
|
||||
gas x_gpe1_block;
|
||||
|
||||
acpi_gas sleep_control_reg;
|
||||
acpi_gas sleep_status_reg;
|
||||
gas sleep_control_reg;
|
||||
gas sleep_status_reg;
|
||||
|
||||
uint64_t hypervisor_vendor_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_xsdt
|
||||
struct xsdt
|
||||
{
|
||||
TABLE_HEADER('XSDT');
|
||||
acpi_table_header *headers[0];
|
||||
table_header *headers[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_apic
|
||||
struct apic
|
||||
{
|
||||
TABLE_HEADER('APIC');
|
||||
uint32_t local_address;
|
||||
@@ -171,7 +175,7 @@ struct acpi_apic
|
||||
uint8_t controller_data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_mcfg_entry
|
||||
struct mcfg_entry
|
||||
{
|
||||
uint64_t base;
|
||||
uint16_t group;
|
||||
@@ -180,24 +184,24 @@ struct acpi_mcfg_entry
|
||||
uint32_t reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_mcfg
|
||||
struct mcfg
|
||||
{
|
||||
TABLE_HEADER('MCFG');
|
||||
uint64_t reserved;
|
||||
acpi_mcfg_entry entries[0];
|
||||
mcfg_entry entries[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_hpet
|
||||
struct hpet
|
||||
{
|
||||
TABLE_HEADER('HPET');
|
||||
uint32_t hardware_id;
|
||||
acpi_gas base_address;
|
||||
gas base_address;
|
||||
uint8_t index;
|
||||
uint16_t periodic_min;
|
||||
uint8_t attributes;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_bgrt
|
||||
struct bgrt
|
||||
{
|
||||
TABLE_HEADER('BGRT');
|
||||
uint16_t version;
|
||||
@@ -208,3 +212,35 @@ struct acpi_bgrt
|
||||
uint32_t offset_y;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rsdp1
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rsdp2
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum10;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
|
||||
uint32_t length;
|
||||
table_header *xsdt_address;
|
||||
uint8_t checksum20;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
template <typename T> static const T *
|
||||
check_get_table(const table_header *header)
|
||||
{
|
||||
if (!header || header->type != T::type_id)
|
||||
return nullptr;
|
||||
return reinterpret_cast<const T *>(header);
|
||||
}
|
||||
|
||||
} // namespace acpi
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "apic.h"
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "clock.h"
|
||||
#include "interrupts.h"
|
||||
#include "io.h"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "assert.h"
|
||||
#include "idt.h"
|
||||
|
||||
namespace panic {
|
||||
|
||||
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
|
||||
void const *symbol_table = nullptr;
|
||||
|
||||
void
|
||||
install(uintptr_t entrypoint, util::const_buffer symbol_data)
|
||||
{
|
||||
IDT::set_nmi_handler(entrypoint);
|
||||
symbol_table = symbol_data.pointer;
|
||||
}
|
||||
|
||||
} // namespace panic
|
||||
|
||||
extern "C"
|
||||
void __assert_fail(const char *message, const char *file, unsigned line, const char *function) {
|
||||
panic::panic(message, nullptr, function, file, line);
|
||||
}
|
||||
@@ -68,6 +68,7 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps)
|
||||
capability *
|
||||
cap_table::find_without_retain(j6_handle_t id)
|
||||
{
|
||||
util::scoped_lock lock {m_lock};
|
||||
return m_caps.find(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
|
||||
using __exit_func = void (*)(void *);
|
||||
|
||||
|
||||
@@ -1,58 +1,72 @@
|
||||
#include <new>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <j6/memutils.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "cpu.h"
|
||||
#include "cpu/cpu_id.h"
|
||||
#include "device_manager.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "logger.h"
|
||||
#include "msr.h"
|
||||
#include "objects/thread.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscall.h"
|
||||
#include "tss.h"
|
||||
#include "xsave.h"
|
||||
|
||||
unsigned g_num_cpus = 1;
|
||||
|
||||
panic_data g_panic_data;
|
||||
panic_data *g_panic_data_p = &g_panic_data;
|
||||
|
||||
cpu_data g_bsp_cpu_data;
|
||||
static util::no_construct<cpu_data> __g_bsp_cpu_storage;
|
||||
cpu_data &g_bsp_cpu_data = __g_bsp_cpu_storage.value;
|
||||
|
||||
cpu_data **g_cpu_data = nullptr;
|
||||
|
||||
|
||||
static cpu::features
|
||||
get_features()
|
||||
{
|
||||
cpu::cpu_id cpuid;
|
||||
return cpuid.features();
|
||||
}
|
||||
|
||||
// Validate the required CPU features are present. Really, the bootloader already
|
||||
// validated the required features, but still iterate the options and log about them.
|
||||
void
|
||||
cpu_validate()
|
||||
static cpu::features
|
||||
cpu_validate(cpu_data *c)
|
||||
{
|
||||
cpu::cpu_id cpu;
|
||||
cpu::cpu_id cpuid;
|
||||
|
||||
char brand_name[50];
|
||||
cpu.brand_name(brand_name);
|
||||
cpuid.brand_name(brand_name);
|
||||
|
||||
cpu::cpu_id::features features = cpu.validate();
|
||||
cpu::features &features = c->features;
|
||||
|
||||
log::info(logs::boot, "CPU: %s", brand_name);
|
||||
log::info(logs::boot, " Vendor is %s", cpu.vendor_id());
|
||||
log::info(logs::boot, "CPU %2d: %s", c->index, brand_name);
|
||||
log::info(logs::boot, " Vendor is %s", cpuid.vendor_id());
|
||||
|
||||
log::spam(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
||||
log::spam(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||
log::spam(logs::boot, " Higest basic CPUID: 0x%02x", cpuid.highest_basic());
|
||||
log::spam(logs::boot, " Higest ext CPUID: 0x%02x", cpuid.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||
|
||||
#define CPU_FEATURE_OPT(name, ...) \
|
||||
log::verbose(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no");
|
||||
log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no");
|
||||
|
||||
#define CPU_FEATURE_WRN(name, feat_leaf, feat_sub, regname, bit) \
|
||||
log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
|
||||
if (!features[cpu::feature::name]) log::warn(logs::boot, "Missing cpu feature %s but continuing", #name);
|
||||
|
||||
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
||||
log::verbose(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
|
||||
log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
|
||||
kassert(features[cpu::feature::name], "Missing required CPU feature " #name );
|
||||
|
||||
#include "cpu/features.inc"
|
||||
#undef CPU_FEATURE_OPT
|
||||
#undef CPU_FEATURE_REQ
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +78,31 @@ cpu_early_init(cpu_data *cpu)
|
||||
cpu->idt->install();
|
||||
cpu->gdt->install();
|
||||
|
||||
util::bitset64 cr0_val = 0;
|
||||
asm ("mov %%cr0, %0" : "=r"(cr0_val));
|
||||
cr0_val
|
||||
.set(cr0::WP)
|
||||
.clear(cr0::CD);
|
||||
asm volatile ( "mov %0, %%cr0" :: "r" (cr0_val) );
|
||||
|
||||
cpu->features = get_features();
|
||||
|
||||
uintptr_t cr3_val;
|
||||
asm ("mov %%cr3, %0" : "=r"(cr3_val));
|
||||
|
||||
util::bitset64 cr4_val = 0;
|
||||
asm ("mov %%cr4, %0" : "=r"(cr4_val));
|
||||
cr4_val
|
||||
.set(cr4::OSXFSR)
|
||||
.set(cr4::OSXMMEXCPT)
|
||||
.set(cr4::PCIDE)
|
||||
.set(cr4::OSXSAVE);
|
||||
|
||||
// TODO: On KVM setting PCIDE generates a #GP even though
|
||||
// the feature is listed as available in CPUID.
|
||||
/*
|
||||
if (cpu->features[cpu::feature::pcid])
|
||||
cr4_val.set(cr4::PCIDE);
|
||||
*/
|
||||
asm volatile ( "mov %0, %%cr4" :: "r" (cr4_val) );
|
||||
|
||||
// Enable SYSCALL and NX bit
|
||||
@@ -80,6 +112,24 @@ cpu_early_init(cpu_data *cpu)
|
||||
.set(efer::NXE);
|
||||
wrmsr(msr::ia32_efer, efer_val);
|
||||
|
||||
util::bitset64 xcr0_val = get_xcr0();
|
||||
xcr0_val
|
||||
.set(xcr0::SSE);
|
||||
set_xcr0(xcr0_val);
|
||||
|
||||
// Set initial floating point state
|
||||
const util::bitset32 mxcsr_val {
|
||||
mxcsr::DAZ,
|
||||
mxcsr::IM,
|
||||
mxcsr::DM,
|
||||
mxcsr::ZM,
|
||||
mxcsr::OM,
|
||||
mxcsr::UM,
|
||||
mxcsr::PM,
|
||||
mxcsr::FTZ,
|
||||
};
|
||||
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
|
||||
|
||||
// Install the GS base pointint to the cpu_data
|
||||
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
||||
}
|
||||
@@ -87,7 +137,6 @@ cpu_early_init(cpu_data *cpu)
|
||||
cpu_data *
|
||||
bsp_early_init()
|
||||
{
|
||||
cpu_validate();
|
||||
memset(&g_panic_data, 0, sizeof(g_panic_data));
|
||||
|
||||
extern IDT &g_bsp_idt;
|
||||
@@ -104,6 +153,7 @@ bsp_early_init()
|
||||
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
|
||||
cpu->rsp0 = reinterpret_cast<uintptr_t>(&idle_stack_end);
|
||||
cpu_early_init(cpu);
|
||||
xsave_init();
|
||||
|
||||
return cpu;
|
||||
}
|
||||
@@ -120,8 +170,12 @@ bsp_late_init()
|
||||
asm ("mov %%cr0, %0" : "=r"(cr0v));
|
||||
asm ("mov %%cr4, %0" : "=r"(cr4v));
|
||||
|
||||
uint32_t mxcsrv = get_mxcsr();
|
||||
uint64_t xcr0v = get_xcr0();
|
||||
|
||||
uint64_t efer = rdmsr(msr::ia32_efer);
|
||||
log::spam(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0v, cr4v, efer);
|
||||
log::spam(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx mxcsr:%x xcr0:%x", cr0v, cr4v, efer, mxcsrv, xcr0v);
|
||||
cpu_validate(&g_bsp_cpu_data);
|
||||
}
|
||||
|
||||
cpu_data *
|
||||
@@ -162,7 +216,6 @@ cpu_init(cpu_data *cpu, bool bsp)
|
||||
|
||||
obj::thread *idle = obj::thread::create_idle_thread(
|
||||
g_kernel_process,
|
||||
scheduler::max_priority,
|
||||
cpu->rsp0);
|
||||
|
||||
cpu->thread = idle;
|
||||
@@ -190,4 +243,16 @@ cpu_init(cpu_data *cpu, bool bsp)
|
||||
cpu->id = apic->get_id();
|
||||
apic->calibrate_timer();
|
||||
}
|
||||
|
||||
xsave_enable();
|
||||
}
|
||||
|
||||
/// Set up initial per-thread CPU state. Called once from initialize_user_cpu in
|
||||
/// syscall.s, the first code run after a thread comes out of task_switch for the
|
||||
/// very first time.
|
||||
extern "C" void
|
||||
cpu_initialize_thread_state()
|
||||
{
|
||||
const util::bitset32 &mxcsr_val = obj::thread::current().m_mxcsr;
|
||||
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cpu/cpu_id.h>
|
||||
|
||||
class GDT;
|
||||
class IDT;
|
||||
@@ -17,9 +18,12 @@ enum class cr0
|
||||
{
|
||||
PE = 0, // Protected mode enable
|
||||
MP = 1, // Monitor co-processor
|
||||
EM = 2, // (FPU) Emulation
|
||||
TS = 3, // Task switched
|
||||
ET = 4, // Extension type
|
||||
NE = 5, // Numeric error
|
||||
WP = 16, // (ring 0) Write protect
|
||||
CD = 30, // Cache disable
|
||||
PG = 31, // Paging
|
||||
};
|
||||
|
||||
@@ -47,6 +51,8 @@ enum class xcr0
|
||||
ZMM_Hi256,
|
||||
ZMM_Hi16,
|
||||
PKRU = 9,
|
||||
|
||||
J6_SUPPORTED = X87 | SSE | AVX | BINDREG | BINDCSR | OPMASK | ZMM_Hi16 | ZMM_Hi256,
|
||||
};
|
||||
|
||||
enum class efer
|
||||
@@ -58,6 +64,26 @@ enum class efer
|
||||
FFXSR = 14, // Fast FXSAVE
|
||||
};
|
||||
|
||||
enum class mxcsr
|
||||
{
|
||||
IE = 0, // Invalid operation flag
|
||||
DE = 1, // Denormal flag
|
||||
ZE = 2, // Divide by zero flag
|
||||
OE = 3, // Overflow flag
|
||||
UE = 4, // Underflow flag
|
||||
PE = 5, // Precision flag
|
||||
DAZ = 6, // Denormals are zero
|
||||
IM = 7, // Invalid operation mask
|
||||
DM = 8, // Denormal mask
|
||||
ZM = 9, // Divide by zero mask
|
||||
OM = 10, // Overflow mask
|
||||
UM = 11, // Underflow mask
|
||||
PM = 12, // Precision mask
|
||||
RC0 = 13, // Rounding control bit 0
|
||||
RC1 = 14, // Rounding control bit 1
|
||||
FTZ = 15, // Flush to zero
|
||||
};
|
||||
|
||||
struct cpu_state
|
||||
{
|
||||
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
||||
@@ -105,9 +131,20 @@ struct cpu_data
|
||||
// the assembly version
|
||||
lapic *apic;
|
||||
panic_data *panic;
|
||||
cpu::features features;
|
||||
};
|
||||
|
||||
extern "C" cpu_data * _current_gsbase();
|
||||
extern "C" {
|
||||
uint32_t get_mxcsr();
|
||||
uint32_t set_mxcsr(uint32_t val);
|
||||
|
||||
uint64_t get_xcr0();
|
||||
uint64_t set_xcr0(uint64_t val);
|
||||
|
||||
cpu_data * _current_gsbase();
|
||||
|
||||
void cpu_initialize_thread_state();
|
||||
}
|
||||
|
||||
/// Do early initialization of the BSP CPU.
|
||||
/// \returns A pointer to the BSP cpu_data structure
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
global get_mxcsr: function hidden (get_mxcsr.end - get_mxcsr)
|
||||
get_mxcsr:
|
||||
push 0
|
||||
stmxcsr [rsp]
|
||||
pop rax
|
||||
ret
|
||||
.end:
|
||||
|
||||
global set_mxcsr: function hidden (set_mxcsr.end - set_mxcsr)
|
||||
set_mxcsr:
|
||||
push rdi
|
||||
ldmxcsr [rsp]
|
||||
pop rax
|
||||
ret
|
||||
.end:
|
||||
|
||||
global get_xcr0: function hidden (get_xcr0.end - get_xcr0)
|
||||
get_xcr0:
|
||||
xor rcx, rcx ; there is no dana there is only xcr0
|
||||
xgetbv
|
||||
ret ; technically edx has the high 32 bits, but bits 10+ are reserved
|
||||
.end:
|
||||
|
||||
global set_xcr0: function hidden (set_xcr0.end - set_xcr0)
|
||||
set_xcr0:
|
||||
xor rcx, rcx ; there is no dana there is only xcr0
|
||||
mov rax, rdi ; technically edx should be or'd into the high bits, but xcr0 bits 10+ are resereved
|
||||
xsetbv
|
||||
ret
|
||||
.end:
|
||||
|
||||
global get_rsp: function hidden (get_rsp.end - get_rsp)
|
||||
get_rsp:
|
||||
mov rax, rsp
|
||||
87
src/kernel/debugcon.cpp
Normal file
87
src/kernel/debugcon.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include <util/format.h>
|
||||
|
||||
#include "debugcon.h"
|
||||
#include "interrupts.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace debugcon {
|
||||
|
||||
namespace {
|
||||
static const uint8_t level_colors[] = {0x00, 0x09, 0x01, 0x0b, 0x0f, 0x07, 0x08};
|
||||
const char *level_names[] = {"", "fatal", "error", "warn", "info", "verbose", "spam"};
|
||||
const char *area_names[] = {
|
||||
#define LOG(name, lvl) #name ,
|
||||
#include <j6/tables/log_areas.inc>
|
||||
#undef LOG
|
||||
nullptr
|
||||
};
|
||||
|
||||
inline void debug_out(const char *msg, size_t size) {
|
||||
asm ( "rep outsb;" :: "c"(size), "d"(port), "S"(msg) );
|
||||
}
|
||||
|
||||
inline void debug_endline() {
|
||||
static const char *newline = "\e[0m\r\n";
|
||||
asm ( "rep outsb;" :: "c"(6), "d"(port), "S"(newline) );
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
void
|
||||
write(const char *fmt, ...)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
size_t n = util::vformat({buffer, sizeof(buffer)}, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
debug_out(buffer, n);
|
||||
debug_out("\r\n", 2);
|
||||
}
|
||||
|
||||
void
|
||||
output(j6_log_entry *entry)
|
||||
{
|
||||
char buffer [256];
|
||||
size_t dlen = util::format({buffer, sizeof(buffer)}, "\e[38;5;%dm%7s %7s\e[38;5;14m|\e[38;5;%dm ",
|
||||
level_colors[entry->severity],
|
||||
area_names[entry->area],
|
||||
level_names[entry->severity],
|
||||
level_colors[entry->severity]);
|
||||
|
||||
debug_out(buffer, dlen);
|
||||
|
||||
debug_out(entry->message, entry->bytes - sizeof(j6_log_entry));
|
||||
debug_endline();
|
||||
}
|
||||
|
||||
void
|
||||
logger_task()
|
||||
{
|
||||
using entry = j6_log_entry;
|
||||
|
||||
uint64_t seen = 0;
|
||||
size_t buf_size = 128;
|
||||
uint8_t *buf = new uint8_t [buf_size];
|
||||
|
||||
while (true) {
|
||||
size_t size = g_logger.get_entry(seen, buf, buf_size);
|
||||
if (size > buf_size) {
|
||||
delete [] buf;
|
||||
buf_size *= 2;
|
||||
buf = new uint8_t [buf_size];
|
||||
continue;
|
||||
}
|
||||
|
||||
entry *header = reinterpret_cast<entry*>(buf);
|
||||
while (size > sizeof(entry)) {
|
||||
output(header);
|
||||
seen = header->id;
|
||||
size -= header->bytes;
|
||||
header = util::offset_pointer(header, header->bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace debugcon
|
||||
31
src/kernel/debugcon.h
Normal file
31
src/kernel/debugcon.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
/// \file debugcon.h
|
||||
/// Kernel debugcon log output process
|
||||
|
||||
#include "scheduler.h"
|
||||
|
||||
namespace debugcon {
|
||||
|
||||
static constexpr bool enable =
|
||||
#ifdef __jsix_config_debug
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
static constexpr uint16_t port = 0x6600;
|
||||
|
||||
void logger_task();
|
||||
void write(const char *fmt, ...);
|
||||
|
||||
inline void
|
||||
init_logger()
|
||||
{
|
||||
if constexpr (enable) {
|
||||
static constexpr uint8_t pri = scheduler::max_priority - 1;
|
||||
scheduler &s = scheduler::get();
|
||||
s.create_kernel_task(logger_task, pri, true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace debugcon
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <new>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <util/misc.h> // for checksum
|
||||
#include <util/pointers.h>
|
||||
#include <arch/acpi/tables.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "acpi_tables.h"
|
||||
#include "kassert.h"
|
||||
#include "apic.h"
|
||||
#include "clock.h"
|
||||
#include "device_manager.h"
|
||||
@@ -21,37 +20,6 @@ static const char expected_signature[] = "RSD PTR ";
|
||||
|
||||
device_manager device_manager::s_instance;
|
||||
|
||||
struct acpi1_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi2_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum10;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
|
||||
uint32_t length;
|
||||
acpi_table_header *xsdt_address;
|
||||
uint8_t checksum20;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
bool
|
||||
acpi_table_header::validate(uint32_t expected_type) const
|
||||
{
|
||||
if (util::checksum(this, length) != 0) return false;
|
||||
return !expected_type || (expected_type == type);
|
||||
}
|
||||
|
||||
|
||||
device_manager::device_manager() :
|
||||
m_lapic_base(0)
|
||||
{
|
||||
@@ -63,37 +31,32 @@ device_manager::device_manager() :
|
||||
m_irqs[2] = {ignore_event, 0};
|
||||
}
|
||||
|
||||
template <typename T> static const T *
|
||||
check_get_table(const acpi_table_header *header)
|
||||
{
|
||||
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
|
||||
return reinterpret_cast<const T *>(header);
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::parse_acpi(const void *root_table)
|
||||
{
|
||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||
|
||||
const acpi1_rsdp *acpi1 = mem::to_virtual(
|
||||
reinterpret_cast<const acpi1_rsdp *>(root_table));
|
||||
const acpi::rsdp1 *acpi1 = mem::to_virtual(
|
||||
reinterpret_cast<const acpi::rsdp1 *>(root_table));
|
||||
|
||||
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
||||
kassert(acpi1->signature[i] == expected_signature[i],
|
||||
"ACPI RSDP table signature mismatch");
|
||||
|
||||
uint8_t sum = util::checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
||||
uint8_t sum = util::checksum(acpi1, sizeof(acpi::rsdp1), 0);
|
||||
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
||||
|
||||
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
||||
|
||||
const acpi2_rsdp *acpi2 =
|
||||
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
||||
const acpi::rsdp2 *acpi2 =
|
||||
reinterpret_cast<const acpi::rsdp2 *>(acpi1);
|
||||
|
||||
sum = util::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
||||
sum = util::checksum(acpi2, sizeof(acpi::rsdp2), sizeof(acpi::rsdp1));
|
||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||
|
||||
load_xsdt(mem::to_virtual(acpi2->xsdt_address));
|
||||
const acpi::table_header *xsdt = mem::to_virtual(acpi2->xsdt_address);
|
||||
kassert(xsdt->validate(), "Bad XSDT table");
|
||||
load_xsdt(xsdt);
|
||||
}
|
||||
|
||||
const device_manager::apic_nmi *
|
||||
@@ -129,53 +92,50 @@ put_sig(char *into, uint32_t type)
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_xsdt(const acpi_table_header *header)
|
||||
device_manager::load_xsdt(const acpi::table_header *header)
|
||||
{
|
||||
const auto *xsdt = check_get_table<acpi_xsdt>(header);
|
||||
const auto *xsdt = acpi::check_get_table<acpi::xsdt>(header);
|
||||
|
||||
char sig[5] = {0,0,0,0,0};
|
||||
log::info(logs::device, "ACPI 2.0+ tables loading");
|
||||
|
||||
put_sig(sig, xsdt->header.type);
|
||||
log::verbose(logs::device, " Found table %s", sig);
|
||||
log::verbose(logs::device, " Loading table %s", sig);
|
||||
|
||||
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
||||
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi_table_header *header =
|
||||
const acpi::table_header *header =
|
||||
mem::to_virtual(xsdt->headers[i]);
|
||||
|
||||
put_sig(sig, header->type);
|
||||
log::verbose(logs::device, " Found table %s", sig);
|
||||
|
||||
kassert(header->validate(), "Table failed validation.");
|
||||
put_sig(sig, header->type);
|
||||
|
||||
switch (header->type) {
|
||||
case acpi_apic::type_id:
|
||||
case acpi::apic::type_id:
|
||||
log::verbose(logs::device, " Loading table %s", sig);
|
||||
load_apic(header);
|
||||
break;
|
||||
|
||||
case acpi_mcfg::type_id:
|
||||
load_mcfg(header);
|
||||
break;
|
||||
|
||||
case acpi_hpet::type_id:
|
||||
case acpi::hpet::type_id:
|
||||
log::verbose(logs::device, " Loading table %s", sig);
|
||||
load_hpet(header);
|
||||
break;
|
||||
|
||||
default:
|
||||
log::verbose(logs::device, " Skipping table %s", sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_apic(const acpi_table_header *header)
|
||||
device_manager::load_apic(const acpi::table_header *header)
|
||||
{
|
||||
const auto *apic = check_get_table<acpi_apic>(header);
|
||||
const auto *apic = acpi::check_get_table<acpi::apic>(header);
|
||||
|
||||
m_lapic_base = apic->local_address;
|
||||
|
||||
size_t count = acpi_table_entries(apic, 1);
|
||||
size_t count = acpi::table_entries(apic, 1);
|
||||
uint8_t const *p = apic->controller_data;
|
||||
uint8_t const *end = p + count;
|
||||
|
||||
@@ -265,33 +225,9 @@ device_manager::load_apic(const acpi_table_header *header)
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_mcfg(const acpi_table_header *header)
|
||||
device_manager::load_hpet(const acpi::table_header *header)
|
||||
{
|
||||
const auto *mcfg = check_get_table<acpi_mcfg>(header);
|
||||
|
||||
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
|
||||
m_pci.set_size(count);
|
||||
m_devices.set_capacity(16);
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
||||
|
||||
m_pci[i].group = mcfge.group;
|
||||
m_pci[i].bus_start = mcfge.bus_start;
|
||||
m_pci[i].bus_end = mcfge.bus_end;
|
||||
m_pci[i].base = mem::to_virtual<uint32_t>(mcfge.base);
|
||||
|
||||
log::spam(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
}
|
||||
|
||||
probe_pci();
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_hpet(const acpi_table_header *header)
|
||||
{
|
||||
const auto *hpet = check_get_table<acpi_hpet>(header);
|
||||
const auto *hpet = acpi::check_get_table<acpi::hpet>(header);
|
||||
|
||||
log::verbose(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
||||
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
||||
@@ -311,28 +247,6 @@ device_manager::load_hpet(const acpi_table_header *header)
|
||||
reinterpret_cast<uint64_t*>(hpet->base_address.address + mem::linear_offset));
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::probe_pci()
|
||||
{
|
||||
for (auto &pci : m_pci) {
|
||||
log::verbose(logs::device, "Probing PCI group at base %016lx", pci.base);
|
||||
|
||||
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
|
||||
for (int dev = 0; dev < 32; ++dev) {
|
||||
if (!pci.has_device(bus, dev, 0)) continue;
|
||||
|
||||
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
|
||||
if (!d0.multi()) continue;
|
||||
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
if (pci.has_device(bus, dev, i))
|
||||
m_devices.emplace(pci, bus, dev, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
tsc_clock_source(void*)
|
||||
{
|
||||
@@ -366,7 +280,7 @@ device_manager::init_drivers()
|
||||
|
||||
// becomes the singleton
|
||||
master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
||||
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||
log::info(logs::timer, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||
} else {
|
||||
//TODO: Other clocks, APIC clock?
|
||||
master_clock = new clock(5000, tsc_clock_source, nullptr);
|
||||
@@ -385,7 +299,7 @@ device_manager::dispatch_irq(unsigned irq)
|
||||
if (!binding.target || binding.target == ignore_event)
|
||||
return binding.target == ignore_event;
|
||||
|
||||
binding.target->signal(1 << binding.signal);
|
||||
binding.target->signal(1ull << binding.signal);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -410,10 +324,10 @@ device_manager::unbind_irqs(obj::event *target)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool
|
||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||
{
|
||||
/*
|
||||
// TODO: find gaps to fill
|
||||
uint8_t irq = m_irqs.count();
|
||||
isr vector = isr::irq00 + irq;
|
||||
@@ -424,12 +338,6 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
|
||||
device.write_msi_regs(
|
||||
0xFEE00000,
|
||||
static_cast<uint16_t>(vector));
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::register_block_device(block_device *blockdev)
|
||||
{
|
||||
m_blockdevs.append(blockdev);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
|
||||
#include "apic.h"
|
||||
#include "hpet.h"
|
||||
#include "pci.h"
|
||||
|
||||
struct acpi_table_header;
|
||||
class block_device;
|
||||
|
||||
namespace acpi {
|
||||
struct table_header;
|
||||
}
|
||||
|
||||
namespace obj {
|
||||
class event;
|
||||
}
|
||||
@@ -53,18 +55,6 @@ public:
|
||||
/// \arg target The endpoint to remove
|
||||
void unbind_irqs(obj::event *target);
|
||||
|
||||
/// Allocate an MSI IRQ for a device
|
||||
/// \arg name Name of the interrupt, for display to user
|
||||
/// \arg device Device this MSI is being allocated for
|
||||
/// \arg cb Callback to call when the interrupt is received
|
||||
/// \arg data Data to pass to the callback
|
||||
/// \returns True if an interrupt was allocated successfully
|
||||
bool allocate_msi(
|
||||
const char *name,
|
||||
pci_device &device,
|
||||
irq_callback cb,
|
||||
void *data);
|
||||
|
||||
/// Dispatch an IRQ interrupt
|
||||
/// \arg irq The irq number of the interrupt
|
||||
/// \returns True if the interrupt was handled
|
||||
@@ -103,23 +93,6 @@ public:
|
||||
/// configuration, or null if no configuration was provided
|
||||
const irq_override * get_irq_override(uint8_t irq) const;
|
||||
|
||||
/// Register the existance of a block device.
|
||||
/// \arg blockdev Pointer to the block device
|
||||
void register_block_device(block_device *blockdev);
|
||||
|
||||
/// Get the number of block devices in the system
|
||||
/// \returns A count of devices
|
||||
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
|
||||
|
||||
/// Get a block device
|
||||
/// \arg i Index of the device to get
|
||||
/// \returns A pointer to the requested device, or nullptr
|
||||
inline block_device * get_block_device(unsigned i)
|
||||
{
|
||||
return i < m_blockdevs.count() ?
|
||||
m_blockdevs[i] : nullptr;
|
||||
}
|
||||
|
||||
/// Get an HPET device
|
||||
/// \arg i Index of the device to get
|
||||
/// \returns A pointer to the requested device, or nullptr
|
||||
@@ -132,23 +105,15 @@ public:
|
||||
private:
|
||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||
void load_xsdt(const acpi_table_header *xsdt);
|
||||
void load_xsdt(const acpi::table_header *xsdt);
|
||||
|
||||
/// Parse the ACPI MADT and initialize APICs from it.
|
||||
/// \arg apic Pointer to the MADT from the XSDT
|
||||
void load_apic(const acpi_table_header *apic);
|
||||
|
||||
/// Parse the ACPI MCFG and initialize PCIe from it.
|
||||
/// \arg mcfg Pointer to the MCFG from the XSDT
|
||||
void load_mcfg(const acpi_table_header *mcfg);
|
||||
void load_apic(const acpi::table_header *apic);
|
||||
|
||||
/// Parse the ACPI HPET and initialize an HPET from it.
|
||||
/// \arg hpet Pointer to the HPET from the XSDT
|
||||
void load_hpet(const acpi_table_header *hpet);
|
||||
|
||||
/// Probe the PCIe busses and add found devices to our
|
||||
/// device list. The device list is destroyed and rebuilt.
|
||||
void probe_pci();
|
||||
void load_hpet(const acpi::table_header *hpet);
|
||||
|
||||
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
||||
/// that has no callback.
|
||||
@@ -162,9 +127,6 @@ private:
|
||||
util::vector<apic_nmi> m_nmis;
|
||||
util::vector<irq_override> m_overrides;
|
||||
|
||||
util::vector<pci_group> m_pci;
|
||||
util::vector<pci_device> m_devices;
|
||||
|
||||
struct irq_binding
|
||||
{
|
||||
obj::event *target = nullptr;
|
||||
@@ -172,8 +134,6 @@ private:
|
||||
};
|
||||
util::vector<irq_binding> m_irqs;
|
||||
|
||||
util::vector<block_device *> m_blockdevs;
|
||||
|
||||
static device_manager s_instance;
|
||||
|
||||
device_manager(const device_manager &) = delete;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <bootproto/kernel.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "debugcon.h"
|
||||
#include "frame_allocator.h"
|
||||
#include "logger.h"
|
||||
#include "memory.h"
|
||||
@@ -51,7 +52,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address)
|
||||
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
|
||||
|
||||
// See how many contiguous pages are here
|
||||
unsigned n = bsf(~m3 >> o3);
|
||||
unsigned n = bsf(~(m3 >> o3));
|
||||
if (n > count)
|
||||
n = count;
|
||||
|
||||
@@ -71,6 +72,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address)
|
||||
}
|
||||
}
|
||||
|
||||
//debugcon::write("Allocated %2d frames at %016lx - %016lx", n, *address, *address + n * frame_size);
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -88,6 +90,8 @@ frame_allocator::free(uintptr_t address, size_t count)
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
//debugcon::write("Freeing %2d frames at %016lx - %016lx", count, address, address + count * frame_size);
|
||||
|
||||
for (long i = 0; i < m_count; ++i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
uintptr_t end = block.base + block.count * frame_size;
|
||||
@@ -101,9 +105,9 @@ frame_allocator::free(uintptr_t address, size_t count)
|
||||
unsigned o3 = frame & 0x3f;
|
||||
|
||||
while (count--) {
|
||||
block.map1 |= (1 << o1);
|
||||
block.map2[o1] |= (1 << o2);
|
||||
block.bitmap[o2] |= (1 << o3);
|
||||
block.map1 |= (1ull << o1);
|
||||
block.map2[o1] |= (1ull << o2);
|
||||
block.bitmap[o2] |= (1ull << o3);
|
||||
if (++o3 == 64) {
|
||||
o3 = 0;
|
||||
if (++o2 == 64) {
|
||||
@@ -139,12 +143,12 @@ frame_allocator::used(uintptr_t address, size_t count)
|
||||
unsigned o3 = frame & 0x3f;
|
||||
|
||||
while (count--) {
|
||||
block.bitmap[o2] &= ~(1 << o3);
|
||||
block.bitmap[o2] &= ~(1ull << o3);
|
||||
if (!block.bitmap[o2]) {
|
||||
block.map2[o1] &= ~(1 << o2);
|
||||
block.map2[o1] &= ~(1ull << o2);
|
||||
|
||||
if (!block.map2[o1]) {
|
||||
block.map1 &= ~(1 << o1);
|
||||
block.map1 &= ~(1ull << o1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "cpu.h"
|
||||
#include "gdt.h"
|
||||
#include "logger.h"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#include <new>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
#include <util/pointers.h>
|
||||
#include <util/util.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "heap_allocator.h"
|
||||
#include "memory.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
uint32_t & get_map_key(heap_allocator::block_info &info) { return info.offset; }
|
||||
|
||||
@@ -27,7 +28,7 @@ struct heap_allocator::free_header
|
||||
|
||||
inline free_header * buddy() const {
|
||||
return reinterpret_cast<free_header *>(
|
||||
reinterpret_cast<uintptr_t>(this) ^ (1 << order));
|
||||
reinterpret_cast<uintptr_t>(this) ^ (1ull << order));
|
||||
}
|
||||
|
||||
inline bool eldest() const { return this < buddy(); }
|
||||
@@ -46,7 +47,6 @@ heap_allocator::heap_allocator(uintptr_t start, size_t size, uintptr_t heapmap)
|
||||
m_maxsize {size},
|
||||
m_allocated_size {0},
|
||||
m_map (reinterpret_cast<block_info*>(heapmap), 512)
|
||||
|
||||
{
|
||||
memset(m_free, 0, sizeof(m_free));
|
||||
}
|
||||
@@ -57,6 +57,11 @@ heap_allocator::allocate(size_t length)
|
||||
if (length == 0)
|
||||
return nullptr;
|
||||
|
||||
static constexpr unsigned min_order =
|
||||
__debug_heap_allocation ?
|
||||
12 : // allocating full pages in debug mode
|
||||
heap_allocator::min_order;
|
||||
|
||||
unsigned order = util::log2(length);
|
||||
if (order < min_order)
|
||||
order = min_order;
|
||||
@@ -67,7 +72,7 @@ heap_allocator::allocate(size_t length)
|
||||
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
m_allocated_size += (1 << order);
|
||||
m_allocated_size += (1ull << order);
|
||||
|
||||
free_header *block = pop_free(order);
|
||||
if (!block && !split_off(order, block)) {
|
||||
@@ -95,7 +100,17 @@ heap_allocator::free(void *p)
|
||||
kassert(info, "Attempt to free pointer not known to the heap");
|
||||
if (!info) return;
|
||||
|
||||
m_allocated_size -= (1 << info->order);
|
||||
size_t size = (1ull << info->order);
|
||||
m_allocated_size -= size;
|
||||
|
||||
if constexpr (__debug_heap_allocation) {
|
||||
extern obj::vm_area_untracked &g_kernel_heap_area;
|
||||
|
||||
size_t offset = reinterpret_cast<uintptr_t>(p) - mem::heap_offset;
|
||||
size_t pages = mem::bytes_to_pages(size);
|
||||
vm_space::kernel_space().lock(g_kernel_heap_area, offset, pages);
|
||||
return;
|
||||
}
|
||||
|
||||
block->clear(info->order);
|
||||
block = merge_block(block);
|
||||
@@ -118,15 +133,15 @@ heap_allocator::reallocate(void *p, size_t old_length, size_t new_length)
|
||||
if (!info)
|
||||
return nullptr;
|
||||
|
||||
if (new_length <= (1 << info->order))
|
||||
if (new_length <= (1ull << info->order))
|
||||
return p;
|
||||
|
||||
lock.release();
|
||||
void *new_block = allocate(new_length);
|
||||
memcpy(new_block, p, old_length);
|
||||
void *reallocated = allocate(new_length);
|
||||
memcpy(reallocated, p, old_length);
|
||||
free(p);
|
||||
|
||||
return new_block;
|
||||
return reallocated;
|
||||
}
|
||||
|
||||
heap_allocator::free_header *
|
||||
@@ -180,13 +195,15 @@ heap_allocator::new_block(unsigned order)
|
||||
unsigned current = address_order(m_end);
|
||||
while (current < order) {
|
||||
register_free_block(reinterpret_cast<free_header*>(m_end), current);
|
||||
m_end += 1 << current;
|
||||
m_end += 1ull << current;
|
||||
current = address_order(m_end);
|
||||
}
|
||||
|
||||
void *block = reinterpret_cast<void*>(m_end);
|
||||
m_end += 1 << order;
|
||||
m_map[map_key(block)].order = order;
|
||||
m_end += 1ull << order;
|
||||
block_info &info = m_map[map_key(block)];
|
||||
info.order = order;
|
||||
info.free = false;
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@ public:
|
||||
/// allocation with the contents copied over.
|
||||
void * reallocate(void *p, size_t old_length, size_t new_length);
|
||||
|
||||
/// Minimum block size is (2^min_order). Must be at least 6.
|
||||
static const unsigned min_order = 6; // 2^6 == 64 B
|
||||
/// Minimum block size is (2^min_order). Must be at least 5 in
|
||||
/// order to hold a free_header.
|
||||
static const unsigned min_order = 5; // 2^5 == 32 B
|
||||
|
||||
/// Maximum block size is (2^max_order). Must be less than 32 + min_order.
|
||||
static const unsigned max_order = 22; // 2^22 == 4 MiB
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "device_manager.h"
|
||||
#include "hpet.h"
|
||||
#include "io.h"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/// \file idt.h
|
||||
/// Definitions relating to a CPU's IDT table
|
||||
#include <stdint.h>
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
|
||||
class IDT
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include <util/format.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "cpu.h"
|
||||
#include "device_manager.h"
|
||||
#include "idt.h"
|
||||
@@ -145,6 +145,30 @@ isr_handler(cpu_state *regs)
|
||||
}
|
||||
break;
|
||||
|
||||
case isr::isrSIMDFPE: {
|
||||
uint32_t mxcsr = 0;
|
||||
asm volatile ("stmxcsr %0" : "=m"(mxcsr));
|
||||
util::format({message, sizeof(message)},
|
||||
"SIMD Exception; MXCSR[%s%s%s%s%s%s%s%s%s%s%s%s%s%s rc:%d]",
|
||||
(mxcsr & 0x0001) ? " IE" : "",
|
||||
(mxcsr & 0x0002) ? " DE" : "",
|
||||
(mxcsr & 0x0004) ? " ZE" : "",
|
||||
(mxcsr & 0x0008) ? " OE" : "",
|
||||
(mxcsr & 0x0010) ? " UE" : "",
|
||||
(mxcsr & 0x0020) ? " PE" : "",
|
||||
(mxcsr & 0x0040) ? " DAZ" : "",
|
||||
(mxcsr & 0x0080) ? " IM" : "",
|
||||
(mxcsr & 0x0100) ? " DM" : "",
|
||||
(mxcsr & 0x0200) ? " ZM" : "",
|
||||
(mxcsr & 0x0400) ? " OM" : "",
|
||||
(mxcsr & 0x0800) ? " UM" : "",
|
||||
(mxcsr & 0x1000) ? " PM" : "",
|
||||
(mxcsr & 0x8000) ? " FTZ" : "",
|
||||
((mxcsr >> 13) & 0x3));
|
||||
kassert(false, message, regs);
|
||||
}
|
||||
break;
|
||||
|
||||
case isr::isrSpurious:
|
||||
// No EOI for the spurious interrupt
|
||||
return;
|
||||
|
||||
49
src/kernel/ipc_message.cpp
Normal file
49
src/kernel/ipc_message.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
#include "ipc_message.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
message::message() : tag {0}, data {nullptr, 0}, handles {nullptr, 0} {}
|
||||
|
||||
message::message(
|
||||
uint64_t in_tag,
|
||||
const util::buffer &in_data,
|
||||
const util::counted<j6_handle_t> &in_handles) :
|
||||
tag {in_tag}, data {nullptr, in_data.count}, handles {nullptr, in_handles.count}
|
||||
{
|
||||
if (data.count) {
|
||||
data.pointer = new uint8_t [data.count];
|
||||
memcpy(data.pointer, in_data.pointer, data.count);
|
||||
}
|
||||
|
||||
if (handles.count) {
|
||||
handles.pointer = new j6_handle_t [handles.count];
|
||||
memcpy(handles.pointer, in_handles.pointer, handles.count * sizeof(j6_handle_t));
|
||||
}
|
||||
}
|
||||
|
||||
message::message(message &&other) { *this = util::move(other); }
|
||||
|
||||
message::~message()
|
||||
{
|
||||
delete [] reinterpret_cast<uint8_t*>(data.pointer);
|
||||
delete [] handles.pointer;
|
||||
}
|
||||
|
||||
message &
|
||||
message::operator=(message &&other)
|
||||
{
|
||||
tag = other.tag;
|
||||
other.tag = 0;
|
||||
|
||||
data = other.data;
|
||||
other.data = {nullptr, 0};
|
||||
|
||||
handles = other.handles;
|
||||
other.handles = {nullptr, 0};
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
25
src/kernel/ipc_message.h
Normal file
25
src/kernel/ipc_message.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
/// \file ipc_message.h
|
||||
/// Definition of shared message structure
|
||||
|
||||
#include <stdint.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
struct message
|
||||
{
|
||||
uint64_t tag;
|
||||
util::buffer data;
|
||||
util::counted<j6_handle_t> handles;
|
||||
|
||||
message();
|
||||
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
|
||||
message(message &&other);
|
||||
~message();
|
||||
|
||||
message & operator=(message &&other);
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
57
src/kernel/kassert.cpp
Normal file
57
src/kernel/kassert.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "kassert.h"
|
||||
#include "idt.h"
|
||||
|
||||
namespace panic {
|
||||
|
||||
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
|
||||
void const *symbol_table = nullptr;
|
||||
|
||||
void
|
||||
install(uintptr_t entrypoint, util::const_buffer symbol_data)
|
||||
{
|
||||
IDT::set_nmi_handler(entrypoint);
|
||||
symbol_table = symbol_data.pointer;
|
||||
}
|
||||
|
||||
[[ noreturn ]]
|
||||
void panic(
|
||||
const char *message,
|
||||
const cpu_state *user,
|
||||
const char *function,
|
||||
const char *file,
|
||||
uint64_t line)
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
|
||||
// Grab the global panic block for ourselves
|
||||
cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL);
|
||||
|
||||
// If we aren't the first CPU to panic, cpu.panic will be null
|
||||
if (cpu.panic) {
|
||||
cpu.panic->symbol_data = symbol_table;
|
||||
cpu.panic->user_state = user;
|
||||
|
||||
cpu.panic->message = message;
|
||||
cpu.panic->function = function;
|
||||
cpu.panic->file = file;
|
||||
cpu.panic->line = line;
|
||||
|
||||
cpu.panic->cpus = g_num_cpus;
|
||||
|
||||
static constexpr uint32_t send_nmi_command =
|
||||
(4 << 8) | // Delivery mode NMI
|
||||
(1 << 14) | // assert level high
|
||||
(2 << 18); // destination all
|
||||
|
||||
*apic_icr = send_nmi_command;
|
||||
}
|
||||
|
||||
while (1) asm ("hlt");
|
||||
}
|
||||
|
||||
} // namespace panic
|
||||
|
||||
extern "C"
|
||||
void __assert_fail(const char *message, const char *file, unsigned line, const char *function) {
|
||||
panic::panic(message, nullptr, function, file, line);
|
||||
}
|
||||
@@ -7,45 +7,13 @@
|
||||
|
||||
namespace panic {
|
||||
|
||||
constexpr uint32_t send_nmi_command =
|
||||
(4 << 8) | // Delivery mode NMI
|
||||
(1 << 14) | // assert level high
|
||||
(2 << 18); // destination all
|
||||
|
||||
extern uint32_t *apic_icr;
|
||||
extern void const *symbol_table;
|
||||
|
||||
[[ noreturn ]]
|
||||
__attribute__ ((always_inline))
|
||||
inline void panic(
|
||||
void panic(
|
||||
const char *message = nullptr,
|
||||
const cpu_state *user = nullptr,
|
||||
const char *function = __builtin_FUNCTION(),
|
||||
const char *file = __builtin_FILE(),
|
||||
uint64_t line = __builtin_LINE())
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
|
||||
// Grab the global panic block for ourselves
|
||||
cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL);
|
||||
|
||||
// If we aren't the first CPU to panic, cpu.panic will be null
|
||||
if (cpu.panic) {
|
||||
cpu.panic->symbol_data = symbol_table;
|
||||
cpu.panic->user_state = user;
|
||||
|
||||
cpu.panic->message = message;
|
||||
cpu.panic->function = function;
|
||||
cpu.panic->file = file;
|
||||
cpu.panic->line = line;
|
||||
|
||||
cpu.panic->cpus = g_num_cpus;
|
||||
|
||||
*apic_icr = send_nmi_command;
|
||||
}
|
||||
|
||||
while (1) asm ("hlt");
|
||||
}
|
||||
uint64_t line = __builtin_LINE());
|
||||
|
||||
/// Install a panic handler.
|
||||
/// \arg entrypoint Virtual address of the panic handler's entrypoint
|
||||
@@ -1,22 +1,22 @@
|
||||
# vim: ft=python
|
||||
|
||||
kernel = module("kernel",
|
||||
kind = "exe",
|
||||
default = True,
|
||||
output = "jsix.elf",
|
||||
basename = "jsix",
|
||||
targets = [ "kernel" ],
|
||||
description = "jsix kernel",
|
||||
deps = [ "util", "cpu", "bootproto", "j6" ],
|
||||
static = True,
|
||||
ld_script = "kernel.ld",
|
||||
sources = [
|
||||
"apic.cpp",
|
||||
"assert.cpp",
|
||||
"kassert.cpp",
|
||||
"boot.s",
|
||||
"capabilities.cpp",
|
||||
"clock.cpp",
|
||||
"cpprt.cpp",
|
||||
"cpu.cpp",
|
||||
"debug.s",
|
||||
"cpu.s",
|
||||
"device_manager.cpp",
|
||||
"frame_allocator.cpp",
|
||||
"gdt.cpp",
|
||||
@@ -27,13 +27,13 @@ kernel = module("kernel",
|
||||
"interrupts.cpp",
|
||||
"interrupts.s",
|
||||
"io.cpp",
|
||||
"ipc_message.cpp",
|
||||
"kernel_main.cpp",
|
||||
"logger.cpp",
|
||||
"memory.cpp",
|
||||
"memory.h.cog",
|
||||
"memory_bootstrap.cpp",
|
||||
"msr.cpp",
|
||||
"objects/channel.cpp",
|
||||
"objects/event.cpp",
|
||||
"objects/kobject.cpp",
|
||||
"objects/mailbox.cpp",
|
||||
@@ -43,7 +43,6 @@ kernel = module("kernel",
|
||||
"objects/vm_area.cpp",
|
||||
"page_table.cpp",
|
||||
"page_tree.cpp",
|
||||
"pci.cpp",
|
||||
"scheduler.cpp",
|
||||
"smp.cpp",
|
||||
"smp.s",
|
||||
@@ -52,12 +51,12 @@ kernel = module("kernel",
|
||||
"syscall.s",
|
||||
"syscall_verify.cpp.cog",
|
||||
"syscalls.inc.cog",
|
||||
"syscalls/channel.cpp",
|
||||
"syscalls/event.cpp",
|
||||
"syscalls/handle.cpp",
|
||||
"syscalls/mailbox.cpp",
|
||||
"syscalls/object.cpp",
|
||||
"syscalls/process.cpp",
|
||||
"syscalls/futex.cpp",
|
||||
"syscalls/system.cpp",
|
||||
"syscalls/thread.cpp",
|
||||
"syscalls/vm_area.cpp",
|
||||
@@ -67,8 +66,12 @@ kernel = module("kernel",
|
||||
"tss.cpp",
|
||||
"vm_space.cpp",
|
||||
"wait_queue.cpp",
|
||||
"xsave.cpp",
|
||||
])
|
||||
|
||||
if config == "debug":
|
||||
kernel.add_input("debugcon.cpp")
|
||||
|
||||
from glob import glob
|
||||
from os.path import join
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
#include <bootproto/kernel.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "capabilities.h"
|
||||
#include "cpu.h"
|
||||
#include "debugcon.h"
|
||||
#include "device_manager.h"
|
||||
#include "interrupts.h"
|
||||
#include "logger.h"
|
||||
@@ -36,9 +37,8 @@ kernel_main(bootproto::args *args)
|
||||
panic::install(args->panic_handler, args->symbol_table);
|
||||
}
|
||||
|
||||
logger_init();
|
||||
|
||||
cpu_data *cpu = bsp_early_init();
|
||||
mem::initialize(*args);
|
||||
|
||||
kassert(args->magic == bootproto::args_magic,
|
||||
"Bad kernel args magic number");
|
||||
@@ -51,8 +51,6 @@ kernel_main(bootproto::args *args)
|
||||
|
||||
disable_legacy_pic();
|
||||
|
||||
mem::initialize(*args);
|
||||
|
||||
bsp_late_init();
|
||||
|
||||
using bootproto::boot_flags;
|
||||
@@ -73,6 +71,10 @@ kernel_main(bootproto::args *args)
|
||||
scheduler *sched = new scheduler {g_num_cpus};
|
||||
smp::ready();
|
||||
|
||||
// Initialize the debug console logger (does nothing if not built
|
||||
// in debug mode)
|
||||
debugcon::init_logger();
|
||||
|
||||
// Load the init server
|
||||
load_init_server(args->init, args->init_modules);
|
||||
|
||||
@@ -98,18 +100,12 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
|
||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
|
||||
obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags);
|
||||
space.add(sect.virt_addr, vma);
|
||||
space.add(sect.virt_addr, vma, obj::vm_flags::exact);
|
||||
}
|
||||
|
||||
uint64_t iopl = (3ull << 12);
|
||||
|
||||
obj::thread *main = p->create_thread();
|
||||
main->add_thunk_user(program.entrypoint, 0, iopl);
|
||||
main->add_thunk_user(program.entrypoint, modules_address, 0, 0, iopl);
|
||||
main->set_state(obj::thread::state::ready);
|
||||
|
||||
// Hacky: No process exists to have created a stack for init; it needs to create
|
||||
// its own stack. We take advantage of that to use rsp to pass it the init modules
|
||||
// address.
|
||||
auto *tcb = main->tcb();
|
||||
tcb->rsp3 = modules_address;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
#include <new>
|
||||
#include <string.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
#include <util/format.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "logger.h"
|
||||
#include "memory.h"
|
||||
#include "objects/system.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
static constexpr bool j6_debugcon_enable = false;
|
||||
static constexpr uint16_t j6_debugcon_port = 0x6600;
|
||||
|
||||
static uint8_t log_buffer[log::logger::log_pages * arch::frame_size];
|
||||
|
||||
// The logger is initialized _before_ global constructors are called,
|
||||
// so that we can start log output immediately. Keep its constructor
|
||||
// from being called here so as to not overwrite the previous initialization.
|
||||
@@ -24,38 +16,31 @@ log::logger &g_logger = __g_logger_storage.value;
|
||||
|
||||
namespace log {
|
||||
|
||||
namespace {
|
||||
|
||||
} // anon namespace
|
||||
|
||||
logger *logger::s_log = nullptr;
|
||||
const char *logger::s_level_names[] = {"", "fatal", "error", "warn", "info", "verbose", "spam"};
|
||||
const char *logger::s_area_names[] = {
|
||||
#define LOG(name, lvl) #name ,
|
||||
#include <j6/tables/log_areas.inc>
|
||||
#undef LOG
|
||||
nullptr
|
||||
};
|
||||
|
||||
inline void
|
||||
debug_out(const char *msg, size_t size)
|
||||
{
|
||||
asm ( "rep outsb;" :: "c"(size), "d"(j6_debugcon_port), "S"(msg) );
|
||||
}
|
||||
|
||||
inline void
|
||||
debug_newline()
|
||||
{
|
||||
static const char *newline = "\r\n";
|
||||
asm ( "rep outsb;" :: "c"(2), "d"(j6_debugcon_port), "S"(newline) );
|
||||
}
|
||||
|
||||
logger::logger() :
|
||||
m_buffer(nullptr, 0)
|
||||
m_buffer {nullptr, 0},
|
||||
m_start {0},
|
||||
m_end {0},
|
||||
m_count {0}
|
||||
{
|
||||
memset(&m_levels, 0, sizeof(m_levels));
|
||||
s_log = this;
|
||||
}
|
||||
|
||||
logger::logger(uint8_t *buffer, size_t size) :
|
||||
m_buffer(buffer, size)
|
||||
logger::logger(util::buffer data) :
|
||||
m_buffer {data},
|
||||
m_start {0},
|
||||
m_end {0},
|
||||
m_count {0}
|
||||
{
|
||||
kassert((data.count & (data.count - 1)) == 0,
|
||||
"log buffer size must be a power of two");
|
||||
|
||||
memset(&m_levels, 0, sizeof(m_levels));
|
||||
s_log = this;
|
||||
|
||||
@@ -68,71 +53,56 @@ logger::logger(uint8_t *buffer, size_t size) :
|
||||
void
|
||||
logger::output(level severity, logs area, const char *fmt, va_list args)
|
||||
{
|
||||
char buffer[256];
|
||||
static constexpr size_t buffer_len = 256;
|
||||
static constexpr size_t message_len = buffer_len - sizeof(j6_log_entry);
|
||||
|
||||
if constexpr (j6_debugcon_enable) {
|
||||
size_t dlen = util::format({buffer, sizeof(buffer)}, "%7s %7s| ",
|
||||
s_area_names[static_cast<uint8_t>(area)],
|
||||
s_level_names[static_cast<uint8_t>(severity)]);
|
||||
debug_out(buffer, dlen);
|
||||
}
|
||||
char buffer[buffer_len];
|
||||
j6_log_entry *header = reinterpret_cast<j6_log_entry *>(buffer);
|
||||
|
||||
entry *header = reinterpret_cast<entry *>(buffer);
|
||||
header->bytes = sizeof(entry);
|
||||
header->area = area;
|
||||
header->severity = severity;
|
||||
|
||||
size_t mlen = util::vformat({header->message, sizeof(buffer) - sizeof(entry) - 1}, fmt, args);
|
||||
header->message[mlen] = 0;
|
||||
header->bytes += mlen + 1;
|
||||
|
||||
if constexpr (j6_debugcon_enable) {
|
||||
debug_out(header->message, mlen);
|
||||
debug_newline();
|
||||
}
|
||||
size_t size = sizeof(j6_log_entry);
|
||||
size += util::vformat({header->message, message_len}, fmt, args);
|
||||
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
uint8_t *out;
|
||||
size_t n = m_buffer.reserve(header->bytes, reinterpret_cast<void**>(&out));
|
||||
if (n < header->bytes) {
|
||||
m_buffer.commit(0); // Cannot write the message, give up
|
||||
return;
|
||||
while (free() < size) {
|
||||
// Remove old entries until there's enough space
|
||||
const j6_log_entry *first = util::at<const j6_log_entry>(m_buffer, start());
|
||||
m_start += first->bytes;
|
||||
}
|
||||
|
||||
memcpy(out, buffer, n);
|
||||
m_buffer.commit(n);
|
||||
header->id = ++m_count;
|
||||
header->bytes = size;
|
||||
header->severity = static_cast<uint8_t>(severity);
|
||||
header->area = static_cast<uint8_t>(area);
|
||||
|
||||
m_event.signal(1);
|
||||
memcpy(util::at<void>(m_buffer, end()), buffer, size);
|
||||
m_end += size;
|
||||
|
||||
m_waiting.clear();
|
||||
}
|
||||
|
||||
size_t
|
||||
logger::get_entry(void *buffer, size_t size)
|
||||
logger::get_entry(uint64_t seen, void *buffer, size_t size)
|
||||
{
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
void *out;
|
||||
size_t out_size = m_buffer.get_block(&out);
|
||||
if (out_size == 0 || out == 0) {
|
||||
while (seen == m_count) {
|
||||
lock.release();
|
||||
m_event.wait();
|
||||
m_waiting.wait();
|
||||
lock.reacquire();
|
||||
out_size = m_buffer.get_block(&out);
|
||||
|
||||
if (out_size == 0 || out == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
kassert(out_size >= sizeof(entry), "Couldn't read a full entry");
|
||||
if (out_size < sizeof(entry))
|
||||
return 0;
|
||||
|
||||
entry *ent = reinterpret_cast<entry *>(out);
|
||||
if (size >= ent->bytes) {
|
||||
memcpy(buffer, out, ent->bytes);
|
||||
m_buffer.consume(ent->bytes);
|
||||
size_t off = m_start;
|
||||
j6_log_entry *ent = util::at<j6_log_entry>(m_buffer, offset(off));
|
||||
while (seen >= ent->id) {
|
||||
off += ent->bytes;
|
||||
kassert(off < m_end, "Got to the end while looking for new log entry");
|
||||
ent = util::at<j6_log_entry>(m_buffer, offset(off));
|
||||
}
|
||||
|
||||
if (size >= ent->bytes)
|
||||
memcpy(buffer, ent, ent->bytes);
|
||||
|
||||
return ent->bytes;
|
||||
}
|
||||
|
||||
@@ -168,9 +138,3 @@ void fatal(logs area, const char *fmt, ...)
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
|
||||
void logger_init()
|
||||
{
|
||||
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "objects/event.h"
|
||||
@@ -19,6 +19,9 @@ enum class logs : uint8_t {
|
||||
|
||||
namespace log {
|
||||
|
||||
/// Size of the log ring buffer. Must be a power of two.
|
||||
inline constexpr unsigned log_pages = 16;
|
||||
|
||||
enum class level : uint8_t {
|
||||
silent, fatal, error, warn, info, verbose, spam, max
|
||||
};
|
||||
@@ -29,26 +32,16 @@ constexpr unsigned areas_count =
|
||||
class logger
|
||||
{
|
||||
public:
|
||||
/// Size of the log ring buffer
|
||||
static constexpr unsigned log_pages = 16;
|
||||
|
||||
/// Default constructor. Creates a logger without a backing store.
|
||||
logger();
|
||||
|
||||
/// Constructor. Logs are written to the given buffer.
|
||||
/// \arg buffer Buffer to which logs are written
|
||||
/// \arg size Size of `buffer`, in bytes
|
||||
logger(uint8_t *buffer, size_t size);
|
||||
logger(util::buffer buffer);
|
||||
|
||||
/// Get the default logger.
|
||||
inline logger & get() { return *s_log; }
|
||||
|
||||
/// Get the registered name for a given area
|
||||
inline const char * area_name(logs area) const { return s_area_names[static_cast<unsigned>(area)]; }
|
||||
|
||||
/// Get the name of a level
|
||||
inline const char * level_name(level l) const { return s_level_names[static_cast<unsigned>(l)]; }
|
||||
|
||||
/// Write to the log
|
||||
/// \arg severity The severity of the message
|
||||
/// \arg area The log area to write to
|
||||
@@ -64,23 +57,18 @@ public:
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
struct entry
|
||||
{
|
||||
uint8_t bytes;
|
||||
logs area;
|
||||
level severity;
|
||||
char message[0];
|
||||
};
|
||||
|
||||
/// Get the next log entry from the buffer
|
||||
/// Get the next log entry from the buffer. Blocks the current thread until
|
||||
/// a log arrives if there are no entries newer than `seen`.
|
||||
/// \arg seen The id of the last-seen log entry, or 0 for none
|
||||
/// \arg buffer The buffer to copy the log message into
|
||||
/// \arg size Size of the passed-in buffer, in bytes
|
||||
/// \returns The size of the log entry (if larger than the
|
||||
/// buffer, then no data was copied)
|
||||
size_t get_entry(void *buffer, size_t size);
|
||||
size_t get_entry(uint64_t seen, void *buffer, size_t size);
|
||||
|
||||
/// Get whether there is currently data in the log buffer
|
||||
inline bool has_log() const { return m_buffer.size(); }
|
||||
/// Check whether or not there's a new log entry to get
|
||||
/// \arg seen The id of the last-seen log entry, or 0 for none
|
||||
inline bool has_entry(uint64_t seen) { return seen < m_count; }
|
||||
|
||||
private:
|
||||
friend void spam (logs area, const char *fmt, ...);
|
||||
@@ -100,16 +88,22 @@ private:
|
||||
return m_levels[static_cast<unsigned>(area)];
|
||||
}
|
||||
|
||||
obj::event m_event;
|
||||
inline size_t offset(size_t i) const { return i & (m_buffer.count - 1); }
|
||||
|
||||
inline size_t start() const { return offset(m_start); }
|
||||
inline size_t end() const { return offset(m_end); }
|
||||
inline size_t free() const { return m_buffer.count - (m_end - m_start); }
|
||||
|
||||
level m_levels[areas_count];
|
||||
|
||||
util::bip_buffer m_buffer;
|
||||
util::buffer m_buffer;
|
||||
size_t m_start, m_end;
|
||||
uint64_t m_count;
|
||||
|
||||
wait_queue m_waiting;
|
||||
util::spinlock m_lock;
|
||||
|
||||
static logger *s_log;
|
||||
static const char *s_area_names[areas_count+1];
|
||||
static const char *s_level_names[static_cast<unsigned>(level::max)];
|
||||
};
|
||||
|
||||
void spam (logs area, const char *fmt, ...);
|
||||
@@ -119,8 +113,6 @@ void warn (logs area, const char *fmt, ...);
|
||||
void error (logs area, const char *fmt, ...);
|
||||
void fatal (logs area, const char *fmt, ...);
|
||||
|
||||
extern log::logger &g_logger;
|
||||
|
||||
} // namespace log
|
||||
|
||||
void logger_init();
|
||||
extern log::logger &g_logger;
|
||||
|
||||
@@ -2,27 +2,4 @@
|
||||
|
||||
namespace std {
|
||||
enum class __attribute__ ((__type_visibility("default"))) align_val_t : size_t { };
|
||||
}
|
||||
|
||||
// Implementation of memset and memcpy because we're not
|
||||
// linking libc into the kernel
|
||||
extern "C" {
|
||||
|
||||
void *
|
||||
memset(void *s, uint8_t v, size_t n)
|
||||
{
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(s);
|
||||
for (size_t i = 0; i < n; ++i) p[i] = v;
|
||||
return s;
|
||||
}
|
||||
|
||||
void *
|
||||
memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
|
||||
uint8_t *d = reinterpret_cast<uint8_t *>(dest);
|
||||
for (size_t i = 0; i < n; ++i) d[i] = s[i];
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -88,3 +88,6 @@ constexpr uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + f
|
||||
void initialize(bootproto::args &args);
|
||||
|
||||
} // namespace mem
|
||||
|
||||
static constexpr bool __debug_heap_allocation = false;
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <arch/memory.h>
|
||||
#include <bootproto/kernel.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "capabilities.h"
|
||||
#include "device_manager.h"
|
||||
#include "frame_allocator.h"
|
||||
@@ -40,6 +37,9 @@ frame_allocator &g_frame_allocator = __g_frame_allocator_storage.value;
|
||||
static util::no_construct<obj::vm_area_untracked> __g_kernel_heap_area_storage;
|
||||
obj::vm_area_untracked &g_kernel_heap_area = __g_kernel_heap_area_storage.value;
|
||||
|
||||
static util::no_construct<obj::vm_area_ring> __g_kernel_log_area_storage;
|
||||
obj::vm_area_ring &g_kernel_log_area = __g_kernel_log_area_storage.value;
|
||||
|
||||
static util::no_construct<obj::vm_area_untracked> __g_kernel_heapmap_area_storage;
|
||||
obj::vm_area_untracked &g_kernel_heapmap_area = __g_kernel_heapmap_area_storage.value;
|
||||
|
||||
@@ -78,7 +78,7 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
|
||||
page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
|
||||
|
||||
|
||||
// Initialize the frame allocator
|
||||
frame_block *blocks = reinterpret_cast<frame_block*>(mem::bitmap_offset);
|
||||
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
|
||||
|
||||
@@ -91,25 +91,36 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
reg = reg->next;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the kernel "process" and vm_space
|
||||
obj::process *kp = obj::process::create_kernel_process(kpml4);
|
||||
vm_space &vm = kp->space();
|
||||
|
||||
// Create the heap space and heap allocator
|
||||
obj::vm_area *heap = new (&g_kernel_heap_area)
|
||||
obj::vm_area_untracked(mem::heap_size, vm_flags::write);
|
||||
|
||||
obj::vm_area *heap_map = new (&g_kernel_heapmap_area)
|
||||
obj::vm_area_untracked(mem::heapmap_size, vm_flags::write);
|
||||
|
||||
vm.add(mem::heap_offset, heap);
|
||||
vm.add(mem::heapmap_offset, heap_map);
|
||||
vm.add(mem::heap_offset, heap, vm_flags::exact);
|
||||
vm.add(mem::heapmap_offset, heap_map, vm_flags::exact);
|
||||
|
||||
new (&g_kernel_heap) heap_allocator {mem::heap_offset, mem::heap_size, mem::heapmap_offset};
|
||||
|
||||
// Set up the log area and logger
|
||||
size_t log_buffer_size = log::log_pages * arch::frame_size;
|
||||
obj::vm_area *logs = new (&g_kernel_log_area)
|
||||
obj::vm_area_ring(log_buffer_size, vm_flags::write);
|
||||
vm.add(mem::logs_offset, logs, vm_flags::exact);
|
||||
|
||||
new (&g_logger) log::logger(
|
||||
util::buffer::from(mem::logs_offset, log_buffer_size));
|
||||
|
||||
// Set up the capability tables
|
||||
obj::vm_area *caps = new (&g_cap_table_area)
|
||||
obj::vm_area_untracked(mem::caps_size, vm_flags::write);
|
||||
|
||||
vm.add(mem::caps_offset, caps);
|
||||
vm.add(mem::caps_offset, caps, vm_flags::exact);
|
||||
|
||||
new (&g_cap_table) cap_table {mem::caps_offset};
|
||||
|
||||
@@ -118,7 +129,7 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
mem::kernel_stack_pages,
|
||||
mem::stacks_size,
|
||||
vm_flags::write};
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks);
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flags::exact);
|
||||
|
||||
// Clean out any remaning bootloader page table entries
|
||||
for (unsigned i = 0; i < arch::kernel_root_index; ++i)
|
||||
@@ -129,7 +140,7 @@ void
|
||||
memory_initialize_post_ctors(bootproto::args &kargs)
|
||||
{
|
||||
vm_space &vm = vm_space::kernel_space();
|
||||
vm.add(mem::buffers_offset, &g_kernel_buffers);
|
||||
vm.add(mem::buffers_offset, &g_kernel_buffers, vm_flags::exact);
|
||||
|
||||
g_frame_allocator.free(
|
||||
get_physical_page(kargs.page_tables.pointer),
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "memory.h"
|
||||
#include "objects/channel.h"
|
||||
#include "objects/thread.h"
|
||||
#include "objects/vm_area.h"
|
||||
|
||||
extern obj::vm_area_guarded g_kernel_buffers;
|
||||
|
||||
namespace obj {
|
||||
|
||||
constexpr size_t buffer_bytes = mem::kernel_buffer_pages * mem::frame_size;
|
||||
|
||||
channel::channel() :
|
||||
m_len {0},
|
||||
m_data {g_kernel_buffers.get_section()},
|
||||
m_closed {false},
|
||||
m_buffer {reinterpret_cast<uint8_t*>(m_data), buffer_bytes},
|
||||
kobject {kobject::type::channel}
|
||||
{
|
||||
}
|
||||
|
||||
channel::~channel()
|
||||
{
|
||||
if (!closed()) close();
|
||||
}
|
||||
|
||||
size_t
|
||||
channel::enqueue(const util::buffer &data)
|
||||
{
|
||||
util::scoped_lock lock {m_close_lock};
|
||||
|
||||
if (closed()) return 0;
|
||||
|
||||
size_t len = data.count;
|
||||
void *buffer = nullptr;
|
||||
len = m_buffer.reserve(len, &buffer);
|
||||
|
||||
memcpy(buffer, data.pointer, len);
|
||||
m_buffer.commit(len);
|
||||
|
||||
if (len) {
|
||||
thread *t = m_queue.pop_next();
|
||||
|
||||
lock.release();
|
||||
if (t) t->wake();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t
|
||||
channel::dequeue(util::buffer buffer, bool block)
|
||||
{
|
||||
util::scoped_lock lock {m_close_lock};
|
||||
|
||||
if (closed()) return 0;
|
||||
|
||||
void *data = nullptr;
|
||||
size_t avail = m_buffer.get_block(&data);
|
||||
if (!avail && block) {
|
||||
thread &cur = thread::current();
|
||||
m_queue.add_thread(&cur);
|
||||
|
||||
lock.release();
|
||||
cur.block();
|
||||
lock.reacquire();
|
||||
avail = m_buffer.get_block(&data);
|
||||
}
|
||||
|
||||
size_t len = buffer.count > avail ? avail : buffer.count;
|
||||
|
||||
memcpy(buffer.pointer, data, len);
|
||||
m_buffer.consume(len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void
|
||||
channel::close()
|
||||
{
|
||||
util::scoped_lock lock {m_close_lock};
|
||||
m_queue.clear();
|
||||
g_kernel_buffers.return_section(m_data);
|
||||
m_closed = true;
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
/// \file channel.h
|
||||
/// Definition of channel objects and related functions
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "objects/kobject.h"
|
||||
#include "wait_queue.h"
|
||||
|
||||
namespace obj {
|
||||
|
||||
/// Channels are uni-directional means of sending data
|
||||
class channel :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
/// Capabilities on a newly constructed channel handle
|
||||
static constexpr j6_cap_t creation_caps = j6_cap_channel_all;
|
||||
static constexpr kobject::type type = kobject::type::channel;
|
||||
|
||||
channel();
|
||||
virtual ~channel();
|
||||
|
||||
/// Put a message into the channel
|
||||
/// \arg data Buffer of data to write
|
||||
/// \returns The number of bytes successfully written
|
||||
size_t enqueue(const util::buffer &data);
|
||||
|
||||
/// Get a message from the channel, copied into a provided buffer
|
||||
/// \arg buffer The buffer to copy data into
|
||||
/// \arg block If true, block the calling thread until there is data
|
||||
/// \returns The number of bytes copied into the provided buffer
|
||||
size_t dequeue(util::buffer buffer, bool block = false);
|
||||
|
||||
/// Mark this channel as closed, all future calls to enqueue or
|
||||
/// dequeue messages will fail with j6_status_closed.
|
||||
void close();
|
||||
|
||||
/// Check if this channel has been closed.
|
||||
inline bool closed() const { return m_closed; }
|
||||
|
||||
private:
|
||||
size_t m_len;
|
||||
uintptr_t m_data;
|
||||
bool m_closed;
|
||||
util::bip_buffer m_buffer;
|
||||
util::spinlock m_close_lock;
|
||||
wait_queue m_queue;
|
||||
};
|
||||
|
||||
} // namespace obj
|
||||
@@ -28,22 +28,15 @@ event::wait()
|
||||
// Wait for event::signal() to wake us with a value
|
||||
thread ¤t = thread::current();
|
||||
m_queue.add_thread(¤t);
|
||||
return current.block();
|
||||
current.block();
|
||||
return read();
|
||||
}
|
||||
|
||||
void
|
||||
event::wake_observer()
|
||||
{
|
||||
util::scoped_lock lock {m_queue.get_lock()};
|
||||
thread *t = m_queue.get_next_unlocked();
|
||||
if (!t) return;
|
||||
|
||||
uint64_t value = read();
|
||||
if (value) {
|
||||
m_queue.pop_next_unlocked();
|
||||
lock.release();
|
||||
t->wake(value);
|
||||
}
|
||||
thread *t = m_queue.pop_next();
|
||||
if (t) t->wake();
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include <j6/errors.h>
|
||||
#include <j6/types.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "logger.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
@@ -14,6 +15,13 @@ static uint32_t next_oids [types_count] = { 0 };
|
||||
static_assert(types_count <= (1 << kobject::koid_type_bits),
|
||||
"kobject::koid_type_bits cannot represent all kobject types");
|
||||
|
||||
static const char *type_names[] = {
|
||||
#define OBJECT_TYPE( name ) #name ,
|
||||
#include <j6/tables/object_types.inc>
|
||||
#undef OBJECT_TYPE
|
||||
nullptr
|
||||
};
|
||||
|
||||
static uint32_t
|
||||
oid_generate(kobject::type t)
|
||||
{
|
||||
@@ -26,13 +34,25 @@ kobject::kobject(type t) :
|
||||
m_handle_count {0},
|
||||
m_type {t},
|
||||
m_obj_id {oid_generate(t)}
|
||||
{}
|
||||
{
|
||||
log::spam(logs::objs, "%s[%02lx] created @ 0x%lx", type_name(m_type), m_obj_id, this);
|
||||
}
|
||||
|
||||
kobject::~kobject() {}
|
||||
kobject::~kobject()
|
||||
{
|
||||
log::spam(logs::objs, "%s[%02lx] deleted", type_name(m_type), m_obj_id);
|
||||
}
|
||||
|
||||
const char *
|
||||
kobject::type_name(type t)
|
||||
{
|
||||
return type_names[static_cast<int>(t)];
|
||||
}
|
||||
|
||||
void
|
||||
kobject::on_no_handles()
|
||||
{
|
||||
log::verbose(logs::objs, "Deleting %s[%02lx] on no handles", type_name(m_type), m_obj_id);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
/// Types of kernel objects.
|
||||
enum class type : uint8_t
|
||||
{
|
||||
#define OBJECT_TYPE( name, val ) name = val,
|
||||
#define OBJECT_TYPE( name ) name ,
|
||||
#include <j6/tables/object_types.inc>
|
||||
#undef OBJECT_TYPE
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
return static_cast<type>((koid >> koid_type_shift) & koid_type_mask);
|
||||
}
|
||||
|
||||
static const char * type_name(type t);
|
||||
|
||||
/// Get this object's type
|
||||
inline type get_type() const { return m_type; }
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <util/basic_types.h>
|
||||
#include <util/counted.h>
|
||||
#include <j6/memutils.h>
|
||||
|
||||
#include "objects/mailbox.h"
|
||||
#include "objects/thread.h"
|
||||
@@ -26,6 +28,10 @@ mailbox::close()
|
||||
|
||||
m_callers.clear(j6_status_closed);
|
||||
m_responders.clear(j6_status_closed);
|
||||
|
||||
util::scoped_lock lock {m_reply_lock};
|
||||
for (auto &waiting : m_reply_map)
|
||||
waiting.thread->wake(j6_status_closed);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
@@ -45,7 +51,7 @@ mailbox::call()
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::receive(thread::message_data &data, reply_tag_t &reply_tag, bool block)
|
||||
mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
@@ -77,7 +83,7 @@ mailbox::receive(thread::message_data &data, reply_tag_t &reply_tag, bool block)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::reply(reply_tag_t reply_tag, const thread::message_data &data)
|
||||
mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
@@ -91,7 +97,7 @@ mailbox::reply(reply_tag_t reply_tag, const thread::message_data &data)
|
||||
m_reply_map.erase(reply_tag);
|
||||
lock.release();
|
||||
|
||||
caller->get_message_data() = data;
|
||||
caller->set_message_data(util::move(data));
|
||||
caller->wake(j6_status_ok);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "heap_allocator.h"
|
||||
#include "ipc_message.h"
|
||||
#include "memory.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "objects/thread.h"
|
||||
#include "slab_allocated.h"
|
||||
#include "wait_queue.h"
|
||||
|
||||
namespace obj {
|
||||
@@ -23,6 +22,7 @@ class mailbox :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
|
||||
using reply_tag_t = uint64_t;
|
||||
|
||||
/// Capabilities on a newly constructed mailbox handle
|
||||
@@ -43,23 +43,23 @@ public:
|
||||
|
||||
/// Send a message to a thread waiting to receive on this mailbox, and block the
|
||||
/// current thread awaiting a response. The message contents should be in the calling
|
||||
/// thread's message_data.
|
||||
/// thread's message data.
|
||||
/// \returns j6_status_ok if a reply was received
|
||||
j6_status_t call();
|
||||
|
||||
/// Receive the next available message, optionally blocking if no messages are available.
|
||||
/// \arg data [out] a thread::message_data structure to fill
|
||||
/// \arg data [out] an ipc::message structure to fill
|
||||
/// \arg reply_tag [out] the reply_tag to use when replying to this message
|
||||
/// \arg block True if this call should block when no messages are available.
|
||||
/// \returns j6_status_ok if a message was received
|
||||
j6_status_t receive(thread::message_data &data, reply_tag_t &reply_tag, bool block);
|
||||
j6_status_t receive(ipc::message &data, reply_tag_t &reply_tag, bool block);
|
||||
|
||||
/// Find a given pending message to be responded to. Returns a replyer object, which will
|
||||
/// wake the calling thread upon destruction.
|
||||
/// wake the calling read upon destruction.
|
||||
/// \arg reply_tag The reply tag in the original message
|
||||
/// \arg data Message data to pass on to the caller
|
||||
/// \returns j6_status_ok if the reply was successfully sent
|
||||
j6_status_t reply(reply_tag_t reply_tag, const thread::message_data &data);
|
||||
j6_status_t reply(reply_tag_t reply_tag, ipc::message &&data);
|
||||
|
||||
private:
|
||||
wait_queue m_callers;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include <new>
|
||||
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "capabilities.h"
|
||||
#include "cpu.h"
|
||||
#include "objects/process.h"
|
||||
@@ -24,8 +22,6 @@ process::process() :
|
||||
kobject {kobject::type::process},
|
||||
m_state {state::running}
|
||||
{
|
||||
m_self_handle = g_cap_table.create(this, process::self_caps);
|
||||
add_handle(m_self_handle);
|
||||
}
|
||||
|
||||
// The "kernel process"-only constructor
|
||||
@@ -52,75 +48,80 @@ process::create_kernel_process(page_table *pml4)
|
||||
void
|
||||
process::exit(int32_t code)
|
||||
{
|
||||
// TODO: make this thread-safe
|
||||
if (m_state == state::exited)
|
||||
return;
|
||||
|
||||
m_state = state::exited;
|
||||
m_return_code = code;
|
||||
|
||||
thread ¤t = thread::current();
|
||||
|
||||
util::scoped_lock lock {m_threads_lock};
|
||||
for (auto *thread : m_threads) {
|
||||
thread->exit();
|
||||
}
|
||||
|
||||
if (this == current_cpu().process)
|
||||
scheduler::get().schedule();
|
||||
}
|
||||
|
||||
void
|
||||
process::update()
|
||||
{
|
||||
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||
|
||||
size_t i = 0;
|
||||
while (i < m_threads.count()) {
|
||||
thread *th = m_threads[i];
|
||||
if (th->has_state(thread::state::exited)) {
|
||||
m_threads.remove_swap_at(i);
|
||||
continue;
|
||||
if (thread != ¤t) {
|
||||
thread->exit();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (m_threads.count() == 0)
|
||||
exit(-1);
|
||||
lock.release();
|
||||
if (¤t.parent() == this)
|
||||
current.exit();
|
||||
}
|
||||
|
||||
thread *
|
||||
process::create_thread(uintptr_t rsp3, uint8_t priority)
|
||||
{
|
||||
util::scoped_lock lock {m_threads_lock};
|
||||
|
||||
if (m_state == state::exited)
|
||||
return nullptr;
|
||||
|
||||
if (priority == default_priority)
|
||||
priority = scheduler::default_priority;
|
||||
|
||||
thread *th = new thread(*this, priority);
|
||||
kassert(th, "Failed to create thread!");
|
||||
th->handle_retain();
|
||||
|
||||
if (rsp3)
|
||||
th->tcb()->rsp3 = rsp3;
|
||||
|
||||
if (this != &g_kernel_process)
|
||||
th->init_xsave_area();
|
||||
|
||||
m_threads.append(th);
|
||||
scheduler::get().add_thread(th->tcb());
|
||||
return th;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
process::thread_exited(thread *th)
|
||||
{
|
||||
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||
m_threads.remove_swap(th);
|
||||
remove_handle(th->self_handle());
|
||||
delete th;
|
||||
|
||||
// TODO: delete the thread's stack VMA
|
||||
if (m_state != state::exited) {
|
||||
// if we're already going away, just release
|
||||
// the thread's handle and skip all this
|
||||
util::scoped_lock lock {m_threads_lock};
|
||||
m_threads.remove_swap(th);
|
||||
|
||||
if (m_threads.count() == 0) {
|
||||
exit(-1);
|
||||
return true;
|
||||
// TODO: delete the thread's stack VMA
|
||||
if (m_threads.empty()) {
|
||||
lock.release();
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
th->handle_release();
|
||||
}
|
||||
|
||||
void
|
||||
process::add_handle(j6_handle_t handle)
|
||||
{
|
||||
// Passing the invalid handle is fine, just don't add anything
|
||||
if (handle == j6_handle_invalid)
|
||||
return;
|
||||
|
||||
capability *c = g_cap_table.retain(handle);
|
||||
kassert(c, "Trying to add a non-existant handle to a process!");
|
||||
|
||||
|
||||
@@ -20,9 +20,6 @@ public:
|
||||
/// Capabilities on a newly constructed process handle
|
||||
static constexpr j6_cap_t creation_caps = j6_cap_process_all;
|
||||
|
||||
/// Capabilities on a process to itself
|
||||
static constexpr j6_cap_t self_caps = j6_cap_process_all;
|
||||
|
||||
/// Top of memory area where thread stacks are allocated
|
||||
static constexpr uintptr_t stacks_top = 0x0000800000000000;
|
||||
|
||||
@@ -47,9 +44,6 @@ public:
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
|
||||
/// Update internal bookkeeping about threads.
|
||||
void update();
|
||||
|
||||
/// Get the process' virtual memory space
|
||||
vm_space & space() { return m_space; }
|
||||
|
||||
@@ -81,11 +75,7 @@ public:
|
||||
|
||||
/// Inform the process of an exited thread
|
||||
/// \args th The thread which has exited
|
||||
/// \returns True if this thread ending has ended the process
|
||||
bool thread_exited(thread *th);
|
||||
|
||||
/// Get the handle for this process to refer to itself
|
||||
inline j6_handle_t self_handle() const { return m_self_handle; }
|
||||
void thread_exited(thread *th);
|
||||
|
||||
/// Get the process object that owns kernel threads and the
|
||||
/// kernel address space
|
||||
@@ -105,12 +95,12 @@ private:
|
||||
// This constructor is called by create_kernel_process
|
||||
process(page_table *kpml4);
|
||||
|
||||
j6_handle_t m_self_handle;
|
||||
int32_t m_return_code;
|
||||
|
||||
vm_space m_space;
|
||||
|
||||
util::vector<thread*> m_threads;
|
||||
util::spinlock m_threads_lock;
|
||||
|
||||
util::node_set<j6_handle_t, j6_handle_invalid, heap_allocated> m_handles;
|
||||
util::spinlock m_handles_lock;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <util/basic_types.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "capabilities.h"
|
||||
#include "cpu.h"
|
||||
#include "logger.h"
|
||||
@@ -8,8 +10,9 @@
|
||||
#include "objects/process.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "scheduler.h"
|
||||
#include "xsave.h"
|
||||
|
||||
extern "C" void kernel_to_user_trampoline();
|
||||
extern "C" void initialize_user_cpu();
|
||||
extern obj::vm_area_guarded &g_kernel_stacks;
|
||||
|
||||
|
||||
@@ -22,6 +25,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||
m_wake_value {0},
|
||||
m_wake_timeout {0}
|
||||
{
|
||||
parent.handle_retain();
|
||||
parent.space().initialize_tcb(m_tcb);
|
||||
m_tcb.priority = pri;
|
||||
m_tcb.thread = this;
|
||||
@@ -32,13 +36,24 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||
m_tcb.rsp0 = rsp0;
|
||||
|
||||
m_creator = current_cpu().thread;
|
||||
m_self_handle = g_cap_table.create(this, thread::parent_caps);
|
||||
parent.add_handle(m_self_handle);
|
||||
|
||||
asm volatile ( "stmxcsr %0" : "=m"(m_mxcsr) );
|
||||
m_mxcsr
|
||||
.clear(mxcsr::IE)
|
||||
.clear(mxcsr::DE)
|
||||
.clear(mxcsr::ZE)
|
||||
.clear(mxcsr::OE)
|
||||
.clear(mxcsr::UE)
|
||||
.clear(mxcsr::PE);
|
||||
}
|
||||
|
||||
thread::~thread()
|
||||
{
|
||||
if (m_tcb.xsave)
|
||||
delete [] reinterpret_cast<uint8_t*>(m_tcb.xsave);
|
||||
|
||||
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
||||
m_parent.handle_release();
|
||||
}
|
||||
|
||||
thread & thread::current() { return *current_cpu().thread; }
|
||||
@@ -52,6 +67,18 @@ thread::block()
|
||||
return m_wake_value;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
thread::block(util::scoped_lock &lock)
|
||||
{
|
||||
kassert(current_cpu().thread == this,
|
||||
"unlocking block() called on non-current thread");
|
||||
|
||||
clear_state(state::ready);
|
||||
lock.release();
|
||||
scheduler::get().schedule();
|
||||
return m_wake_value;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread::join()
|
||||
{
|
||||
@@ -84,11 +111,15 @@ thread::wake_only()
|
||||
set_state(state::ready);
|
||||
}
|
||||
|
||||
void thread::set_message_data(ipc::message &&md) { m_message = util::move(md); }
|
||||
ipc::message && thread::get_message_data() { return util::move(m_message); }
|
||||
|
||||
void
|
||||
thread::exit()
|
||||
{
|
||||
m_wake_timeout = 0;
|
||||
set_state(state::exited);
|
||||
m_parent.thread_exited(this);
|
||||
m_join_queue.clear();
|
||||
block();
|
||||
}
|
||||
@@ -113,7 +144,7 @@ thread::add_thunk_kernel(uintptr_t rip)
|
||||
}
|
||||
|
||||
void
|
||||
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
||||
thread::add_thunk_user(uintptr_t rip3, uint64_t arg0, uint64_t arg1, uintptr_t rip0, uint64_t flags)
|
||||
{
|
||||
// This sets up the stack to:
|
||||
// a) come out of task_switch and return to rip0 (default is the
|
||||
@@ -126,22 +157,33 @@ thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
||||
flags |= 0x200;
|
||||
m_tcb.rflags3 = flags;
|
||||
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 9;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
|
||||
stack[6] = rip3; // return rip in rcx
|
||||
stack[5] = m_tcb.rsp3; // rbp
|
||||
stack[4] = 0xbbbbbbbb; // rbx
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
stack[0] = 0x15151515; // r15
|
||||
stack[8] = rip3; // return rip in rcx
|
||||
stack[7] = m_tcb.rsp3; // rbp
|
||||
stack[6] = 0xbbbbbbbb; // rbx
|
||||
stack[5] = 0x12121212; // r12
|
||||
stack[4] = 0x13131313; // r13
|
||||
stack[3] = 0x14141414; // r14
|
||||
stack[2] = 0x15151515; // r15
|
||||
|
||||
stack[1] = arg1; // rsi
|
||||
stack[0] = arg0; // rdi
|
||||
|
||||
static const uintptr_t trampoline =
|
||||
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||
reinterpret_cast<uintptr_t>(initialize_user_cpu);
|
||||
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
||||
}
|
||||
|
||||
void
|
||||
thread::init_xsave_area()
|
||||
{
|
||||
void *xsave_area = new uint8_t [xsave_size];
|
||||
memset(xsave_area, 0, xsave_size);
|
||||
m_tcb.xsave = reinterpret_cast<uintptr_t>(xsave_area);
|
||||
}
|
||||
|
||||
void
|
||||
thread::setup_kernel_stack()
|
||||
{
|
||||
@@ -149,8 +191,8 @@ thread::setup_kernel_stack()
|
||||
using mem::kernel_stack_pages;
|
||||
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
|
||||
constexpr unsigned null_frame_entries = 2;
|
||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||
static constexpr unsigned null_frame_entries = 2;
|
||||
static constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||
|
||||
uintptr_t stack_addr = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_end = stack_addr + stack_bytes;
|
||||
@@ -168,12 +210,19 @@ thread::setup_kernel_stack()
|
||||
}
|
||||
|
||||
thread *
|
||||
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
||||
thread::create_idle_thread(process &kernel, uintptr_t rsp0)
|
||||
{
|
||||
thread *idle = new thread(kernel, pri, rsp0);
|
||||
thread *idle = new thread(kernel, scheduler::idle_priority, rsp0);
|
||||
idle->set_state(state::constant);
|
||||
idle->set_state(state::ready);
|
||||
return idle;
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
|
||||
uint32_t
|
||||
__current_thread_id()
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
return cpu.thread ? cpu.thread->obj_id() : -1u;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
#include <util/linked_list.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "ipc_message.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "wait_queue.h"
|
||||
|
||||
struct cpu_data;
|
||||
struct page_table;
|
||||
|
||||
namespace obj {
|
||||
@@ -26,6 +27,7 @@ struct TCB
|
||||
uintptr_t rsp3;
|
||||
uintptr_t rflags3;
|
||||
uintptr_t pml4;
|
||||
uintptr_t xsave;
|
||||
// End of area used by asembly
|
||||
|
||||
obj::thread* thread;
|
||||
@@ -57,9 +59,6 @@ public:
|
||||
/// Capabilities on a newly constructed thread handle
|
||||
static constexpr j6_cap_t creation_caps = j6_cap_thread_all;
|
||||
|
||||
/// Capabilities the parent process gets on new thread handles
|
||||
static constexpr j6_cap_t parent_caps = j6_cap_thread_all;
|
||||
|
||||
static constexpr kobject::type type = kobject::type::thread;
|
||||
|
||||
enum class state : uint8_t {
|
||||
@@ -96,9 +95,14 @@ public:
|
||||
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||
|
||||
/// Block this thread, waiting for a value
|
||||
/// \returns The value passed to wake()
|
||||
/// \returns The value passed to wake()
|
||||
uint64_t block();
|
||||
|
||||
/// Block this thread, waiting for a value
|
||||
/// \arg held A held lock to unlock when blocking
|
||||
/// \returns The value passed to wake()
|
||||
uint64_t block(util::scoped_lock &held);
|
||||
|
||||
/// Block the calling thread until this thread exits
|
||||
j6_status_t join();
|
||||
|
||||
@@ -118,13 +122,8 @@ public:
|
||||
/// \returns The clock time at which to wake. 0 for no timeout.
|
||||
inline uint64_t wake_timeout() const { return m_wake_timeout; }
|
||||
|
||||
struct message_data
|
||||
{
|
||||
uint64_t tag, subtag;
|
||||
j6_handle_t handle;
|
||||
};
|
||||
|
||||
message_data & get_message_data() { return m_message_data; }
|
||||
void set_message_data(ipc::message &&md);
|
||||
ipc::message && get_message_data();
|
||||
|
||||
inline bool has_state(state s) const {
|
||||
return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) &
|
||||
@@ -152,29 +151,28 @@ public:
|
||||
/// Add a stack header that returns to the given address in user space
|
||||
/// via a function in kernel space.
|
||||
/// \arg rip3 The user space address to return to
|
||||
/// \arg arg0 An argument passed to the userspace function
|
||||
/// \arg arg1 An argument passed to the userspace function
|
||||
/// \arg rip0 The kernel function to pass through, optional
|
||||
/// \arg flags Extra RFLAGS values to set, optional
|
||||
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
||||
|
||||
/// Get the handle representing this thread to its process
|
||||
j6_handle_t self_handle() const { return m_self_handle; }
|
||||
void add_thunk_user(
|
||||
uintptr_t rip3,
|
||||
uint64_t arg0 = 0,
|
||||
uint64_t arg1 = 0,
|
||||
uintptr_t rip0 = 0,
|
||||
uint64_t flags = 0);
|
||||
|
||||
/// Create the kernel idle thread
|
||||
/// \arg kernel The process object that owns kernel tasks
|
||||
/// \arg pri The idle thread priority value
|
||||
/// \arg rsp The existing stack for the idle thread
|
||||
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||
|
||||
protected:
|
||||
/// Don't delete a thread on no handles, the scheduler takes
|
||||
/// care of that.
|
||||
virtual void on_no_handles() override {}
|
||||
static thread * create_idle_thread(process &kernel, uintptr_t rsp);
|
||||
|
||||
private:
|
||||
thread() = delete;
|
||||
thread(const thread &other) = delete;
|
||||
thread(const thread &&other) = delete;
|
||||
friend class process;
|
||||
friend void ::cpu_initialize_thread_state();
|
||||
|
||||
/// Constructor. Used when a kernel stack already exists.
|
||||
/// \arg parent The process which owns this thread
|
||||
@@ -182,6 +180,9 @@ private:
|
||||
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
||||
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
||||
|
||||
/// Set up the XSAVE saved processor state area for this thread
|
||||
void init_xsave_area();
|
||||
|
||||
/// Set up a new empty kernel stack for this thread.
|
||||
void setup_kernel_stack();
|
||||
|
||||
@@ -191,13 +192,16 @@ private:
|
||||
thread *m_creator;
|
||||
|
||||
state m_state;
|
||||
util::bitset32 m_mxcsr;
|
||||
|
||||
uint64_t m_wake_value;
|
||||
uint64_t m_wake_timeout;
|
||||
message_data m_message_data;
|
||||
|
||||
ipc::message m_message;
|
||||
|
||||
wait_queue m_join_queue;
|
||||
j6_handle_t m_self_handle;
|
||||
};
|
||||
|
||||
} // namespace obj
|
||||
|
||||
extern "C" uint32_t __current_thread_id();
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
#include "assert.h"
|
||||
#include "kassert.h"
|
||||
#include "frame_allocator.h"
|
||||
#include "memory.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "page_tree.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
namespace obj {
|
||||
@@ -86,7 +85,7 @@ vm_area_fixed::resize(size_t size)
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
@@ -106,11 +105,16 @@ vm_area_untracked::~vm_area_untracked()
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
|
||||
if (!alloc) {
|
||||
phys = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return frame_allocator::get().allocate(1, &phys);
|
||||
}
|
||||
|
||||
@@ -136,9 +140,17 @@ vm_area_open::~vm_area_open()
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (alloc)
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
else
|
||||
return page_tree::find(m_mapped, offset, &phys);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area_open::add_existing(uintptr_t offset, uintptr_t phys)
|
||||
{
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +178,7 @@ vm_area_guarded::return_section(uintptr_t addr)
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset >= m_stacks.end())
|
||||
return false;
|
||||
@@ -176,7 +188,23 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
if ((offset >> 12) % m_pages == 0)
|
||||
return false;
|
||||
|
||||
return vm_area_open::get_page(offset, phys);
|
||||
return vm_area_open::get_page(offset, phys, alloc);
|
||||
}
|
||||
|
||||
vm_area_ring::vm_area_ring(size_t size, vm_flags flags) :
|
||||
vm_area_open {size * 2, flags},
|
||||
m_bufsize {size}
|
||||
{
|
||||
}
|
||||
|
||||
vm_area_ring::~vm_area_ring() {}
|
||||
|
||||
bool
|
||||
vm_area_ring::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_bufsize)
|
||||
offset -= m_bufsize;
|
||||
return vm_area_open::get_page(offset, phys, alloc);
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user