Compare commits
47 Commits
feature/dy
...
feature/mu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7da34dbffb | ||
|
|
90a0eb3c53 | ||
|
|
d1ddbd2cf1 | ||
|
|
81b331e5da | ||
|
|
1ebee17880 | ||
|
|
4649d5f77b | ||
|
|
d3c1d6cc34 | ||
|
|
ee24ec8d5c | ||
|
|
372325fab7 | ||
|
|
8f036d9293 | ||
|
|
e345cdd1a7 | ||
|
|
fca570a163 | ||
|
|
ff64d1989f | ||
|
|
d3f5db2479 | ||
|
|
fa587060f1 | ||
|
|
b137c75eb2 | ||
|
|
05c1361283 | ||
|
|
c6835dad70 | ||
|
|
e7fa1dde97 | ||
|
|
eb62588b79 | ||
|
|
29332cbd45 | ||
|
|
172eb70551 | ||
|
|
7322df98f5 | ||
| 70af5a31cd | |||
| 0ab2f59fe8 | |||
| ab84cdb790 | |||
| da9041823b | |||
| 2d7cfc6c59 | |||
| cee4fe67fc | |||
| 53f90f5a1d | |||
| bab2dd5c69 | |||
| 3b9efc11d0 | |||
| 182d3b0a6a | |||
|
|
ed38e989b1 | ||
|
|
1bc6f422c5 | ||
|
|
4909d15c20 | ||
|
|
e725795a17 | ||
|
|
d8a21608c3 | ||
|
|
396fc131e0 | ||
|
|
40130076b2 | ||
|
|
9f54927a82 | ||
|
|
f7ea46e49e | ||
|
|
bc46c9a7d5 | ||
|
|
a1e2c58afc | ||
|
|
4e73e933db | ||
|
|
9f8e75f680 | ||
|
|
e17119254b |
56
.github/workflows/sphinx_deploy.yml
vendored
Normal file
56
.github/workflows/sphinx_deploy.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Deploy docs site with Sphinx
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
# Default to bash
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v4
|
||||
- name: Sphinx build
|
||||
uses: jsix-os/sphinx-action@master
|
||||
with:
|
||||
docs-folder: "docs/"
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: ./docs/_build/html
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ compile_commands.json
|
||||
buddy_allocs.txt
|
||||
frame_allocs.txt
|
||||
heap_allocs.txt
|
||||
/docs/_build
|
||||
|
||||
3
assets/build/amd64/config.release.yaml
Normal file
3
assets/build/amd64/config.release.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
ccflags: [
|
||||
"-O3",
|
||||
]
|
||||
40
assets/build/amd64/target.init.yaml
Normal file
40
assets/build/amd64/target.init.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
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",
|
||||
"-isystem", "${source_root}/sysroot/include/c++/v1",
|
||||
]
|
||||
|
||||
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",
|
||||
]
|
||||
34
assets/build/amd64/target.user.yaml
Normal file
34
assets/build/amd64/target.user.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
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",
|
||||
"-isystem", "${source_root}/sysroot/include/c++/v1",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-m", "elf_x86_64",
|
||||
"--sysroot='${source_root}/sysroot'",
|
||||
"--no-eh-frame-hdr",
|
||||
"-L", "${source_root}/sysroot/lib",
|
||||
"-z", "separate-code",
|
||||
"--no-dependent-libraries",
|
||||
]
|
||||
9
assets/build/linux/config.debug.yaml
Normal file
9
assets/build/linux/config.debug.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
ccflags: [
|
||||
"-g3",
|
||||
"-ggdb",
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-g",
|
||||
]
|
||||
3
assets/build/linux/config.release.yaml
Normal file
3
assets/build/linux/config.release.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
ccflags: [
|
||||
"-O3",
|
||||
]
|
||||
39
assets/build/linux/global.yaml
Normal file
39
assets/build/linux/global.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
cc: "clang-16"
|
||||
cxx: "clang++-16"
|
||||
ld: "ld.lld-16"
|
||||
ar: ar
|
||||
nasm: nasm
|
||||
objcopy: objcopy
|
||||
|
||||
ccflags: [
|
||||
"-I${source_root}/src/include",
|
||||
"-I${source_root}/sysroot/include/c++/v1",
|
||||
"-fcolor-diagnostics",
|
||||
"-U__STDCPP_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" ]
|
||||
91
assets/build/linux/rules.ninja
Normal file
91
assets/build/linux/rules.ninja
Normal file
@@ -0,0 +1,91 @@
|
||||
rule compile.c
|
||||
command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in
|
||||
description = Compiling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule dump_c_defs
|
||||
command = echo | $cc $ccflags $cflags -dM -E - > $out
|
||||
description = Dumping C defines for $target
|
||||
|
||||
rule dump_c_run
|
||||
command = echo '#!/bin/bash' > $out; echo '$cc $ccflags $cflags $$*' >> $
|
||||
$out; chmod a+x $out
|
||||
description = Dumping C arguments for $target
|
||||
|
||||
rule compile.cpp
|
||||
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
|
||||
description = Compiling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule dump_cpp_defs
|
||||
command = echo | $cxx -x c++ $ccflags $cxxflags -dM -E - > $out
|
||||
description = Dumping C++ defines for $target
|
||||
|
||||
rule dump_cpp_run
|
||||
command = echo '#!/bin/bash' > $out; echo '$cxx $ccflags $cxxflags $$*' $
|
||||
>> $out; chmod a+x $out
|
||||
description = Dumping C++ arguments for $target
|
||||
|
||||
rule compile.s
|
||||
command = $nasm -o $out -felf64 -MD $out.d $asflags $in
|
||||
description = Assembling [$target]:$name
|
||||
depfile = $out.d
|
||||
deps = gcc
|
||||
|
||||
rule parse.cog
|
||||
command = cog -o $out -d -D target=$target $cogflags $in
|
||||
description = Parsing [$target]:$name
|
||||
|
||||
rule exe
|
||||
command = $ld $ldflags -o $out $in $libs
|
||||
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 [$target]:$name
|
||||
|
||||
rule cp
|
||||
command = cp $in $out
|
||||
description = Copying [$target]:$name
|
||||
|
||||
rule dump
|
||||
command = objdump -DSC -M intel $in > $out
|
||||
description = Dumping decompiled $name
|
||||
|
||||
rule makest
|
||||
description = Making symbol table
|
||||
command = nm -n -S --demangle $in | ${source_root}/scripts/build_symbol_table.py $out
|
||||
|
||||
rule makeinitrd
|
||||
description = Creating $name
|
||||
command = ${source_root}/scripts/mkj6romfs.py -c $format $in $out
|
||||
|
||||
rule makefat
|
||||
description = Creating $name
|
||||
command = $
|
||||
cp $in $out; $
|
||||
mcopy -s -D o -i $out@@1M ${build_root}/fatroot/* ::/
|
||||
|
||||
rule strip
|
||||
description = Stripping $name
|
||||
command = $
|
||||
cp $in $out; $
|
||||
objcopy --only-keep-debug $out $debug; $
|
||||
strip --discard-all -g $out; $
|
||||
objcopy --add-gnu-debuglink=$debug $out
|
||||
|
||||
rule touch
|
||||
command = touch $out
|
||||
|
||||
rule compdb
|
||||
command = ninja -t compdb > $out
|
||||
16
assets/build/linux/target.user.exe.yaml
Normal file
16
assets/build/linux/target.user.exe.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
|
||||
ccflags: [
|
||||
"-fpie"
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-pie",
|
||||
"-rpath", "${target_dir}",
|
||||
"--dynamic-linker", "/lib64/ld-linux-x86-64.so.2",
|
||||
"--push-state", "--as-needed", "-Bstatic", "-lc++", "-lc++abi", "-lunwind", "--pop-state",
|
||||
]
|
||||
|
||||
libs: [
|
||||
"${target_dir}/crt0.o",
|
||||
]
|
||||
7
assets/build/linux/target.user.shared.yaml
Normal file
7
assets/build/linux/target.user.shared.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
ccflags: [
|
||||
]
|
||||
|
||||
ldflags: [
|
||||
"-shared",
|
||||
]
|
||||
@@ -501,6 +501,18 @@ class LinkedListPrinter:
|
||||
return self.items
|
||||
|
||||
|
||||
class IsRunning(gdb.Function):
|
||||
def __init__(self):
|
||||
super(IsRunning, self).__init__("is_running")
|
||||
|
||||
def invoke(self):
|
||||
inferior = gdb.selected_inferior()
|
||||
return \
|
||||
inferior is not None and \
|
||||
inferior.is_valid() and \
|
||||
len(inferior.threads()) > 0
|
||||
|
||||
|
||||
def build_pretty_printers():
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
|
||||
pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
|
||||
@@ -520,9 +532,10 @@ GetThreadsCommand()
|
||||
PrintProfilesCommand()
|
||||
DumpLogCommand()
|
||||
ShowCurrentProcessCommand()
|
||||
IsRunning()
|
||||
|
||||
gdb.execute("display/i $rip")
|
||||
gdb.execute("define hook-quit\nkill\nend")
|
||||
gdb.execute("define hook-quit\nif $is_running()\n kill\nend\nend")
|
||||
if not gdb.selected_inferior().was_attached:
|
||||
gdb.execute("add-symbol-file build/panic.serial.elf")
|
||||
gdb.execute("target remote :1234")
|
||||
|
||||
14
assets/manifests/minimal.yaml
Normal file
14
assets/manifests/minimal.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
location: jsix
|
||||
init: srv.init
|
||||
initrd:
|
||||
name: initrd.dat
|
||||
format: zstd
|
||||
panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- srv.logger
|
||||
drivers:
|
||||
- drv.uart
|
||||
libs:
|
||||
- ld.so
|
||||
14
assets/manifests/shell.yaml
Normal file
14
assets/manifests/shell.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
location: jsix
|
||||
init: srv.init
|
||||
initrd:
|
||||
name: initrd.dat
|
||||
format: zstd
|
||||
panic:
|
||||
- panic.serial
|
||||
services:
|
||||
- 6s
|
||||
drivers:
|
||||
- drv.uart
|
||||
libs:
|
||||
- ld.so
|
||||
32
configure
vendored
32
configure
vendored
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
def generate(output, config, manifest):
|
||||
def generate(output, config, arch, manifest):
|
||||
from os import makedirs
|
||||
from glob import iglob
|
||||
from pathlib import Path
|
||||
from bonnibel.module import Module
|
||||
from bonnibel.module import Module, ModuleList
|
||||
from bonnibel.project import Project
|
||||
|
||||
root = Path(__file__).parent.resolve()
|
||||
@@ -18,16 +18,17 @@ def generate(output, config, manifest):
|
||||
str(root / "external/*.module"),
|
||||
]
|
||||
|
||||
modules = {}
|
||||
modules = ModuleList(arch)
|
||||
for source in sources:
|
||||
for modfile in iglob(source, recursive=True):
|
||||
path = Path(modfile).parent
|
||||
modfile = Path(modfile)
|
||||
path = modfile.parent
|
||||
|
||||
def module_init(name, **kwargs):
|
||||
if not "root" in kwargs:
|
||||
kwargs["root"] = path
|
||||
m = Module(name, modfile, **kwargs)
|
||||
modules[m.name] = m
|
||||
modules.add(m)
|
||||
return m
|
||||
|
||||
glo = {
|
||||
@@ -36,18 +37,16 @@ def generate(output, config, manifest):
|
||||
"build_root": output,
|
||||
"module_root": path,
|
||||
"config": config,
|
||||
"arch": arch,
|
||||
}
|
||||
code = compile(open(modfile, 'r').read(), modfile, "exec")
|
||||
|
||||
loc = {}
|
||||
exec(code, glo, loc)
|
||||
|
||||
Module.update(modules)
|
||||
|
||||
makedirs(output.resolve(), exist_ok=True)
|
||||
project.generate(root, output, modules, config, manifest)
|
||||
for mod in modules.values():
|
||||
mod.generate(output)
|
||||
project.generate(root, output, modules, config, arch, manifest)
|
||||
modules.generate(output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
@@ -57,18 +56,25 @@ if __name__ == "__main__":
|
||||
from argparse import ArgumentParser
|
||||
from bonnibel import BonnibelError
|
||||
|
||||
default_arch = "amd64"
|
||||
|
||||
p = ArgumentParser(description="Generate jsix build files")
|
||||
p.add_argument("--manifest", "-m", metavar="FILE", default="assets/manifests/default.yaml",
|
||||
help="File to use as the system manifest")
|
||||
p.add_argument("--config", "-c", metavar="NAME", default="debug",
|
||||
p.add_argument("--conf", "-c", metavar="NAME", default="debug",
|
||||
help="Configuration to build (eg, 'debug' or 'release')")
|
||||
p.add_argument("output", metavar="DIR", default="build", nargs='?',
|
||||
p.add_argument("--arch", "-a", metavar="NAME", default=default_arch,
|
||||
help="Architecture to build (eg, 'amd64' or 'linux')")
|
||||
p.add_argument("--verbose", "-v", action='count', default=0,
|
||||
help="More verbose log output")
|
||||
p.add_argument("output", metavar="DIR", default=None, nargs='?',
|
||||
help="Where to create the build root")
|
||||
|
||||
args = p.parse_args()
|
||||
|
||||
output = args.output or f"build.{args.arch}"
|
||||
try:
|
||||
generate(args.output, args.config, args.manifest)
|
||||
generate(output, args.conf, args.arch, args.manifest)
|
||||
|
||||
except BonnibelError as be:
|
||||
import sys
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
---
|
||||
- name: linear
|
||||
desc: Linearly-mapped physical memory
|
||||
size: 64T
|
||||
shared: true
|
||||
|
||||
- name: bitmap
|
||||
desc: Used/free page tracking bitmap
|
||||
size: 1T
|
||||
shared: true
|
||||
|
||||
- name: heapmap
|
||||
desc: Kernel heap accounting structures
|
||||
size: 32G
|
||||
|
||||
- name: heap
|
||||
desc: Kernel heap
|
||||
size: 32G
|
||||
|
||||
- name: capsmap
|
||||
desc: Capabilities accounting structures
|
||||
size: 32G
|
||||
|
||||
- name: caps
|
||||
desc: Capabilities
|
||||
size: 32G
|
||||
|
||||
- name: stacks
|
||||
desc: Kernel thread stacks
|
||||
size: 64G
|
||||
|
||||
- name: buffers
|
||||
desc: Kernel buffers
|
||||
size: 64G
|
||||
|
||||
- name: logs
|
||||
desc: Kernel logs circular buffer
|
||||
size: 2G
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# An ``event`` is a simple synchronization object. It contains up to 64 signals
|
||||
# that threads can wait for and signal in parallel.
|
||||
|
||||
object event : object {
|
||||
uid f441e03da5516b1a
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ object mailbox : object {
|
||||
method call [cap:send] {
|
||||
param tag uint64 [inout]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param data_size size # number of total bytes in data buffer
|
||||
param handles ref object [optional inout handle list]
|
||||
param handles_size size # total size of handles buffer
|
||||
}
|
||||
|
||||
# Respond to a message sent using call, and wait for another
|
||||
@@ -31,8 +32,9 @@ object mailbox : object {
|
||||
method respond [cap:receive] {
|
||||
param tag uint64 [inout]
|
||||
param data buffer [optional inout]
|
||||
param data_in_len size # number of bytes in data used for input
|
||||
param data_size size # number of total bytes in data buffer
|
||||
param handles ref object [optional inout handle list]
|
||||
param handles_size size # total size of handles buffer
|
||||
param reply_tag uint64 [inout]
|
||||
param flags uint64
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# The base type of all kernel-exposed objects
|
||||
# All kernel-exposed objects inherit from the base ``object`` type, so the
|
||||
# ``object`` syscalls can be used with any object's handle.
|
||||
|
||||
object object [virtual] {
|
||||
uid 667f61fb2cd57bb4
|
||||
cname kobject
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "objects/object.def"
|
||||
|
||||
# Processes are a collection of handles and a virtual memory
|
||||
# space inside which threads are run.
|
||||
# A ``process`` object represents a process running on the system, and allows
|
||||
# control over the threads, handles, and virtual memory space of that process.
|
||||
|
||||
object process : object {
|
||||
uid 0c69ee0b7502ba31
|
||||
@@ -12,14 +12,16 @@ object process : object {
|
||||
]
|
||||
|
||||
# Create a new empty process
|
||||
method create [constructor]
|
||||
method create [constructor] {
|
||||
param name string
|
||||
}
|
||||
|
||||
# Stop all threads and exit the given process
|
||||
method kill [destructor cap:kill]
|
||||
|
||||
# Stop all threads and exit the current process
|
||||
method exit [static noreturn] {
|
||||
param result int32 # The result to retrun to the parent process
|
||||
param result int64 # The result to retrun to the parent process
|
||||
}
|
||||
|
||||
# Give the given process a handle that points to the same
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# The system object represents a handle to kernel functionality
|
||||
# needed by drivers and other priviledged services
|
||||
# The singular ``system`` object represents a handle to kernel functionality
|
||||
# needed by drivers and other priviledged services.
|
||||
|
||||
object system : object {
|
||||
uid fa72506a2cf71a30
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# A ``thread`` object represents a thread of execution within a process running
|
||||
# on the system. The actual thread does not need to be currently running to
|
||||
# hold a handle to it.
|
||||
|
||||
object thread : object {
|
||||
uid 11f23e593d5761bd
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import "objects/process.def"
|
||||
|
||||
# A ``vma`` object represents a single virtual memory area, which may be shared
|
||||
# between several processes. A process having a handle to a ``vma`` does not
|
||||
# necessarily mean that it is mapped into that process' virtual memory space.
|
||||
|
||||
object vma : object {
|
||||
uid d6a12b63b3ed3937
|
||||
cname vm_area
|
||||
|
||||
@@ -24,6 +24,8 @@ interface syscalls [syscall] {
|
||||
|
||||
# Write a message to the kernel log
|
||||
function log {
|
||||
param area uint8
|
||||
param severity uint8
|
||||
param message string
|
||||
}
|
||||
|
||||
@@ -42,6 +44,11 @@ interface syscalls [syscall] {
|
||||
param mask uint32 # The capability bitmask
|
||||
}
|
||||
|
||||
# Close the handle to an object
|
||||
function handle_close {
|
||||
param hnd ref object [handle] # The handle to close
|
||||
}
|
||||
|
||||
# Block waiting on a futex
|
||||
function futex_wait [static] {
|
||||
param address uint32* # Address of the futex value
|
||||
|
||||
23
docs/Makefile
Normal file
23
docs/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
ROOTDIR = $(SOURCEDIR)/..
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c syscall_interface.rst
|
||||
PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c kernel_memory.rst
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
254
docs/_static/custom.css
vendored
Normal file
254
docs/_static/custom.css
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/* custom.css - jsix version */
|
||||
|
||||
:root {
|
||||
--background-color: #181820;
|
||||
--link-color: #7070e0;
|
||||
--link-hover-color: #9090ff;
|
||||
--text-color: #3d3d3d;
|
||||
--text-literal-color: #d26a98;
|
||||
}
|
||||
.wy-nav-side {
|
||||
background: var(--background-color);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1100px) {
|
||||
.wy-nav-content-wrap {
|
||||
background: var(--background-color);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.rst-content {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.rst-content code.literal {
|
||||
color: var(--text-literal-color);
|
||||
}
|
||||
|
||||
.rst-content tt.literal {
|
||||
color: var(--text-literal-color);
|
||||
}
|
||||
|
||||
.rst-content .note {
|
||||
color: #003274;
|
||||
background: #ccddf3;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.rst-content .note .admonition-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rst-content .warning {
|
||||
color: #605000;
|
||||
background: #fcf4cc;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.rst-content .warning .admonition-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rst-content .highlight {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.wy-side-scroll {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
.wy-side-nav-search {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type="text"] {
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
padding: 6px 12px;
|
||||
border-color: var(--background-color);
|
||||
}
|
||||
|
||||
.wy-menu-vertical a {
|
||||
font-size: 100%;
|
||||
color: #d9d9d9;
|
||||
padding-top: 0.6rem;
|
||||
padding-bottom: 0.6rem;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical a:hover {
|
||||
background-color: unset;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a {
|
||||
background-color: var(--background-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a span.toctree-expand {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a span.toctree-expand:before {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a span.toctree-expand:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a:hover {
|
||||
background-color: var(--background-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current > a:hover span.toctree-expand {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical .toctree-l1 {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.wy-menu-vertical .toctree-l1:hover {
|
||||
opacity: 1;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l1.current {
|
||||
opacity: 1;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l1.current > a {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.wy-menu-vertical .toctree-l2:hover {
|
||||
background-color: #566673;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current > a {
|
||||
background-color: #566673;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
|
||||
background-color: #e4e7ea;
|
||||
color: #838383;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover span.toctree-expand {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current > a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a {
|
||||
border: 0px;
|
||||
background-color: #566673;
|
||||
color: #d9d9d9;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a span.toctree-expand {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a span.toctree-expand:before {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a span.toctree-expand:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a:hover {
|
||||
color: white;
|
||||
background-color: #566673;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l3.current > a {
|
||||
background-color: #e4e7ea;
|
||||
color: #838383;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a {
|
||||
background-color: #e4e7ea;
|
||||
color: #838383;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l3.current li.toctree-l4.current > a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.wy-nav-top {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: var(--text-color);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 0;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
box-shadow: unset;
|
||||
}
|
||||
|
||||
.btn-neutral {
|
||||
background: unset !important;
|
||||
color: #838383 !important;
|
||||
}
|
||||
|
||||
.btn-neutral:active {
|
||||
padding: 0.375rem 0.75rem;
|
||||
box-shadow: unset;
|
||||
}
|
||||
50
docs/conf.py
Normal file
50
docs/conf.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'jsix'
|
||||
copyright = '2024, Justin C. Miller'
|
||||
author = 'Justin C. Miller'
|
||||
release = '0.8'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = ['sphinx.ext.todo']
|
||||
primary_domain = 'cpp'
|
||||
todo_include_todos = True
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'renku'
|
||||
html_title = 'jsix'
|
||||
html_logo = 'jsix_transparent.svg'
|
||||
html_static_path = ['_static']
|
||||
html_css_files = ['custom.css']
|
||||
html_theme_options = {
|
||||
"description": "The jsix description",
|
||||
"github_repo": "https://github.com/justinian/jsix",
|
||||
"logo_only": True,
|
||||
"footer_icons": [
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/justinian/jsix",
|
||||
"html": """
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
|
||||
</svg>
|
||||
""",
|
||||
"class": "",
|
||||
},
|
||||
],
|
||||
}
|
||||
81
docs/index.rst
Normal file
81
docs/index.rst
Normal file
@@ -0,0 +1,81 @@
|
||||
.. jsix documentation master file
|
||||
.. |amd64| replace:: :abbr:`amd64 (aka x86_64)`
|
||||
|
||||
The jsix Operating System
|
||||
=========================
|
||||
Introduction
|
||||
------------
|
||||
|
||||
**jsix** is a custom multi-core x64 operating system being built from scratch,
|
||||
supporting modern [#]_ Intel or AMD CPUs, and UEFI firmware. It was initially
|
||||
created out of a desire to explore UEFI and to explore what's possible with a
|
||||
microkernel architecture on modern 64-bit architectures.
|
||||
|
||||
Most of jsix is written in C++ (C++17, using `LLVM <https://llvm.org>`_), but
|
||||
you'll also find some assembly (in `NASM <https://nasm.us>`_ syntax) and Python
|
||||
for development tooling.
|
||||
|
||||
jsix can be found `on GitHub <https://github.com/justinian/jsix>`_, and is
|
||||
released under the terms of the `MPL 2.0 <https://mozilla.org/MPL/2.0/>`_.
|
||||
|
||||
.. admonition:: A note on the name
|
||||
|
||||
This kernel was originally named Popcorn, but I have since discovered that
|
||||
the Popcorn Linux project is also developing a kernel with that name,
|
||||
started around the same time as this project. So I've renamed this kernel
|
||||
jsix as an homage to L4, xv6, and my wonderful wife.
|
||||
|
||||
The name jsix is always styled *jsix* or ``j6``, never capitalized.
|
||||
|
||||
.. [#] jsix aims to support amd64 (x86_64) CPUs released in the last 10 years.
|
||||
|
||||
Current Features
|
||||
----------------
|
||||
|
||||
The jsix kernel is quite far along now, but the userland systems are still lacking.
|
||||
|
||||
- Platform: |amd64|
|
||||
- UEFI bootloader
|
||||
- Multi-core & multi-tasking microkernel
|
||||
|
||||
- Work-stealing SMP scheduler
|
||||
- Pluggable panic handler modules
|
||||
|
||||
- Capability-style object-oriented syscall API
|
||||
|
||||
- Custom IDL for specifying and documenting syscalls
|
||||
|
||||
- Virtual memory based on sharable Virtual Memory Area objects (VMAs)
|
||||
- Kernel API library (libj6), also provides features built on kernel primitives:
|
||||
|
||||
- Channels (async stream IPC) built on shared memory and futexes
|
||||
- Ring buffers via doubly-mapped pages
|
||||
|
||||
- Custom libc
|
||||
- Runtime dynamic linker
|
||||
- Init service
|
||||
|
||||
- Built-in VFS service for the initrd
|
||||
- ELF loader
|
||||
- Service-lookup protocol service
|
||||
|
||||
- Userland UART driver
|
||||
- Userland UEFI framebuffer driver
|
||||
- Userland kernel log output service
|
||||
- Userland unit test runner
|
||||
- Build configuration system (bonnibel)
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Site Contents:
|
||||
|
||||
syscall_interface
|
||||
kernel_memory
|
||||
process_initialization
|
||||
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
||||
12
docs/jsix_transparent.svg
Normal file
12
docs/jsix_transparent.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Gravit.io -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="176.562 356.069 211.11 113" width="211.11pt" height="113pt">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path d=" M 212.981 372.36 L 219.564 376.16 L 226.147 379.961 L 226.147 387.563 L 226.147 395.164 L 219.564 398.965 L 212.981 402.766 L 206.398 398.965 L 199.815 395.164 L 199.815 387.563 L 199.815 379.961 L 206.398 376.16 L 212.981 372.36 L 212.981 372.36 L 212.981 372.36 Z M 256.292 397.366 L 262.875 401.166 L 269.458 404.967 L 269.458 412.569 L 269.458 420.17 L 262.875 423.971 L 256.292 427.772 L 249.709 423.971 L 243.126 420.17 L 243.126 412.569 L 243.126 404.967 L 249.709 401.166 L 256.292 397.366 L 256.292 397.366 Z M 183.622 387.283 L 205.52 374.64 L 227.418 361.997 L 249.316 374.64 L 271.214 387.283 L 271.214 412.569 L 271.214 437.854 L 249.316 450.497 L 227.418 463.14 L 205.52 450.497 L 183.622 437.854 L 183.622 412.569 L 183.622 387.283 L 183.622 387.283 L 183.622 387.283 Z M 241.855 372.36 L 248.438 376.16 L 255.021 379.961 L 255.021 387.563 L 255.021 395.164 L 248.438 398.965 L 241.855 402.766 L 235.272 398.965 L 228.689 395.164 L 228.689 387.563 L 228.689 379.961 L 235.272 376.16 L 241.855 372.36 Z " fill-rule="evenodd" fill="rgb(49,79,128)"/>
|
||||
<path d=" M 298.642 379.579 L 291.621 379.579 L 291.621 372.558 L 298.642 372.558 L 298.642 379.579 Z M 285.214 446.718 L 285.214 441.452 L 287.32 441.452 L 287.32 441.452 Q 289.339 441.452 290.524 440.092 L 290.524 440.092 L 290.524 440.092 Q 291.708 438.731 291.708 436.625 L 291.708 436.625 L 291.708 387.039 L 298.729 387.039 L 298.729 436.011 L 298.729 436.011 Q 298.729 440.925 295.921 443.822 L 295.921 443.822 L 295.921 443.822 Q 293.113 446.718 288.286 446.718 L 288.286 446.718 L 285.214 446.718 Z M 306.628 432.676 L 306.628 427.41 L 314.088 427.41 L 314.088 427.41 Q 317.862 427.41 319.573 425.347 L 319.573 425.347 L 319.573 425.347 Q 321.285 423.285 321.285 419.95 L 321.285 419.95 L 321.285 419.95 Q 321.285 417.317 319.705 415.474 L 319.705 415.474 L 319.705 415.474 Q 318.125 413.631 314.966 411.174 L 314.966 411.174 L 314.966 411.174 Q 312.245 408.98 310.621 407.356 L 310.621 407.356 L 310.621 407.356 Q 308.998 405.732 307.813 403.319 L 307.813 403.319 L 307.813 403.319 Q 306.628 400.905 306.628 397.746 L 306.628 397.746 L 306.628 397.746 Q 306.628 393.095 309.744 390.067 L 309.744 390.067 L 309.744 390.067 Q 312.859 387.039 318.125 387.039 L 318.125 387.039 L 325.76 387.039 L 325.76 392.305 L 319.441 392.305 L 319.441 392.305 Q 313.21 392.305 313.21 398.185 L 313.21 398.185 L 313.21 398.185 Q 313.21 400.467 314.615 402.134 L 314.615 402.134 L 314.615 402.134 Q 316.019 403.802 319.003 406.083 L 319.003 406.083 L 319.003 406.083 Q 321.723 408.19 323.479 409.901 L 323.479 409.901 L 323.479 409.901 Q 325.234 411.613 326.463 414.202 L 326.463 414.202 L 326.463 414.202 Q 327.691 416.791 327.691 420.301 L 327.691 420.301 L 327.691 420.301 Q 327.691 426.532 324.4 429.604 L 324.4 429.604 L 324.4 429.604 Q 321.109 432.676 315.141 432.676 L 315.141 432.676 L 306.628 432.676 Z M 342.611 379.579 L 335.59 379.579 L 335.59 372.558 L 342.611 372.558 L 342.611 379.579 Z M 342.611 432.676 L 335.59 432.676 L 335.59 387.039 L 342.611 387.039 L 342.611 432.676 Z M 356.126 432.676 L 348.754 432.676 L 361.392 409.77 L 349.632 387.039 L 356.39 387.039 L 364.639 403.187 L 372.977 387.039 L 379.735 387.039 L 367.974 409.77 L 380.612 432.676 L 373.24 432.676 L 364.639 416.001 L 356.126 432.676 Z " fill="rgb(49,79,128)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
178
docs/kernel_memory.rst
Normal file
178
docs/kernel_memory.rst
Normal file
@@ -0,0 +1,178 @@
|
||||
.. jsix syscall interface.
|
||||
.. Automatically updated from the definition files using cog!
|
||||
|
||||
.. [[[cog code generation
|
||||
.. from os.path import join
|
||||
.. from memory import Layout, unit
|
||||
..
|
||||
.. layout = Layout(join(definitions_path, "memory_layout.yaml"))
|
||||
.. l = max([len(r.name) for r in layout.regions])
|
||||
.. ]]]
|
||||
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
|
||||
|
||||
Kernel memory
|
||||
=============
|
||||
|
||||
While jsix probably should eventually use KASLR to randomize its memory layout,
|
||||
currently the layout is mostly fixed. (Kernel code locations are not consistent
|
||||
but aren't explicitly randomized.)
|
||||
|
||||
.. [[[cog code generation
|
||||
.. line_size = 128 * 1024**3 # Each line represents up to 32 GiB
|
||||
.. max_lines = 32
|
||||
.. totals = sum([r.size for r in layout.regions])
|
||||
.. remain = unit((128 * 1024**4) - totals)
|
||||
..
|
||||
.. def split(val):
|
||||
.. return f"0x {val >> 48:04x} {(val >> 32) & 0xffff:04x} {(val >> 16) & 0xffff:04x} {val & 0xffff:04x}"
|
||||
..
|
||||
.. cog.outl()
|
||||
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
|
||||
.. cog.outl(f"| | Address | Size | Use |")
|
||||
.. cog.outl(f"+=+=============================+==========+=======================================+")
|
||||
..
|
||||
.. for region in layout.regions:
|
||||
.. cog.outl(f"| | ``{split(region.start)}`` | {unit(region.size):>8} | {region.desc:37} |")
|
||||
.. lines = min(max_lines, region.size // line_size)
|
||||
.. for i in range(1, lines):
|
||||
.. cog.outl(f"+-+ | | |")
|
||||
.. cog.outl(f"| | | | |")
|
||||
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
|
||||
..
|
||||
.. cog.outl(f"| | ... | | |")
|
||||
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
|
||||
.. cog.outl(f"| | ``0x ffff 0000 0000 0000`` | | Kernel code / headers |")
|
||||
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
|
||||
.. cog.outl("")
|
||||
.. cog.outl("")
|
||||
.. cog.outl(f"Un-reserved virtual memory address space in the higher half: {remain}")
|
||||
.. cog.outl("")
|
||||
..
|
||||
.. ]]]
|
||||
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | Address | Size | Use |
|
||||
+=+=============================+==========+=======================================+
|
||||
| | ``0x ffff c000 0000 0000`` | 64 TiB | Linearly-mapped physical memory |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bf00 0000 0000`` | 1 TiB | Used/free page tracking bitmap |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff be00 0000 0000`` | 1 TiB | Per-page state tracking structures |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+ | | |
|
||||
| | | | |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bdf8 0000 0000`` | 32 GiB | Kernel heap accounting structures |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bdf0 0000 0000`` | 32 GiB | Kernel heap |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bde8 0000 0000`` | 32 GiB | Capabilities accounting structures |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bde0 0000 0000`` | 32 GiB | Capabilities |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bdd0 0000 0000`` | 64 GiB | Kernel thread stacks |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bdc0 0000 0000`` | 64 GiB | Kernel buffers |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff bdbf 8000 0000`` | 2 GiB | Kernel logs circular buffer |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ... | | |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
| | ``0x ffff 0000 0000 0000`` | | Kernel code / headers |
|
||||
+-+-----------------------------+----------+---------------------------------------+
|
||||
|
||||
|
||||
Un-reserved virtual memory address space in the higher half: 61 TiB
|
||||
|
||||
.. [[[end]]] (checksum: 8c336cc8151beba1a79c8d3b653f1109)
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
7
docs/modd.conf
Normal file
7
docs/modd.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
** !_build/** ../definitions/**.def {
|
||||
prep: rm -rf _build; make html
|
||||
}
|
||||
|
||||
_build/html/** {
|
||||
daemon: devd -m _build/html
|
||||
}
|
||||
42
docs/process_initialization.rst
Normal file
42
docs/process_initialization.rst
Normal file
@@ -0,0 +1,42 @@
|
||||
.. jsix process initialization in userspace
|
||||
|
||||
Process Initialization
|
||||
======================
|
||||
|
||||
jsix follows the `System V ABI`_ on the ``amd64`` architecture. All arguments
|
||||
needed for program initialization are passed to the program's initial thread on
|
||||
the stack.
|
||||
|
||||
Note that jsix adds a number of additional auxiliary vector entry types for
|
||||
passing jsix-specific data to a program. The jsix-specific auxiliary vector type
|
||||
codes (what the ABI document refers to as ``a_type``) start from ``0xf000``. See
|
||||
the header file ``<j6/init.h>`` for more detail.
|
||||
|
||||
.. _System V ABI: https://gitlab.com/x86-psABIs/x86-64-ABI
|
||||
|
||||
The initial stack frame
|
||||
-----------------------
|
||||
|
||||
============== ==================== ============ =======
|
||||
Address Value Bytes Notes
|
||||
============== ==================== ============ =======
|
||||
``top`` Stack top (out of stack bounds)
|
||||
``top`` - 16 0 16 Stack sentinel
|
||||
\ ``envp`` string data ?
|
||||
\ ``argv`` string data ?
|
||||
\ ... ? Possible padding
|
||||
\ 0, 0 (``AT_NULL``) 16 Aux vector sentinel
|
||||
\ Aux vectors 16 * `m` ``AT_NULL``-terminated array of Aux vectors
|
||||
\ 0 8 Environment sentinel
|
||||
\ ``envp`` 8 * `n` 0-terminated array of environment
|
||||
string pointers
|
||||
\ 0 8 Args sentinel
|
||||
\ ``argv`` 8 * ``argc`` Pointers to argument strings
|
||||
``rsp`` ``argc`` 8 Number of elements in argv
|
||||
============== ==================== ============ =======
|
||||
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
||||
5
docs/requirements.txt
Normal file
5
docs/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
cogapp >= 3
|
||||
pyyaml >= 5.4
|
||||
lark == 0.12.0
|
||||
sphinx
|
||||
renku-sphinx-theme
|
||||
444
docs/syscall_interface.rst
Normal file
444
docs/syscall_interface.rst
Normal file
@@ -0,0 +1,444 @@
|
||||
.. jsix syscall interface.
|
||||
.. Automatically updated from the definition files using cog!
|
||||
|
||||
.. [[[cog code generation
|
||||
.. from textwrap import indent
|
||||
.. from definitions.context import Context
|
||||
..
|
||||
.. ctx = Context(definitions_path)
|
||||
.. ctx.parse("syscalls.def")
|
||||
.. syscalls = ctx.interfaces["syscalls"]
|
||||
..
|
||||
.. def caplist(caps):
|
||||
.. return ', '.join([f"``{c}``" for c in caps])
|
||||
.. ]]]
|
||||
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
|
||||
|
||||
Syscall interface
|
||||
=================
|
||||
|
||||
The jsix kernel's syscall design is based around object handles. Object handles
|
||||
are also a collection of capabilities, encoding certain rights over the object
|
||||
they reference.
|
||||
|
||||
Very few syscalls in jsix can be made without some handle, and most of them are
|
||||
requests to the kernel to create a given kind of object. This is analogous to
|
||||
methods on an object in an object-oriented programming language.
|
||||
|
||||
.. [[[cog code generation
|
||||
.. cog.outl()
|
||||
.. for obj in syscalls.exposes:
|
||||
.. cog.outl(f"``{obj.name}`` syscalls")
|
||||
.. cog.outl(f"-------------------------")
|
||||
.. desc = obj.desc or "Undocumented"
|
||||
.. cog.outl(desc)
|
||||
.. cog.outl()
|
||||
.. cog.outl(f":capabilites: {caplist(obj.caps)}")
|
||||
.. cog.outl()
|
||||
.. for method in obj.methods:
|
||||
.. args = []
|
||||
.. if method.constructor:
|
||||
.. args.append("j6_handle_t *self")
|
||||
.. elif not method.static:
|
||||
.. args.append("j6_handle_t self")
|
||||
..
|
||||
.. for param in method.params:
|
||||
.. for type, suffix in param.type.c_names(param.options):
|
||||
.. args.append(f"{type} {param.name}{suffix}")
|
||||
..
|
||||
.. cog.outl(f".. cpp:function:: j6_result_t j6_{obj.name}_{method.name} ({', '.join(args)})")
|
||||
.. cog.outl()
|
||||
.. desc = method.desc or "Undocumented"
|
||||
.. cog.outl(indent(desc, " "))
|
||||
.. cog.outl()
|
||||
.. if "cap" in method.options:
|
||||
.. cog.outl(f" :capabilities: {caplist(method.options['cap'])}")
|
||||
.. cog.outl()
|
||||
.. if method.constructor:
|
||||
.. cog.outl(f" :param self: *[out]* Handle to the new {obj.name} object")
|
||||
.. elif not method.static:
|
||||
.. cog.outl(f" :param self: Handle to the {obj.name} object")
|
||||
.. for param in method.params:
|
||||
.. opts = param.options and f"*[{', '.join(param.options)}]*" or ""
|
||||
.. desc = param.desc or 'Undocumented'
|
||||
.. cog.outl(f" :param {param.name}: {opts} {desc}")
|
||||
.. cog.outl()
|
||||
.. ]]]
|
||||
|
||||
``object`` syscalls
|
||||
-------------------------
|
||||
All kernel-exposed objects inherit from the base ``object`` type, so the
|
||||
``object`` syscalls can be used with any object's handle.
|
||||
|
||||
:capabilites: ``clone``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_object_koid (j6_handle_t self, uint64_t * koid)
|
||||
|
||||
Get the internal kernel object id of an object
|
||||
|
||||
:param self: Handle to the object object
|
||||
:param koid: *[out]* Undocumented
|
||||
|
||||
``event`` syscalls
|
||||
-------------------------
|
||||
An ``event`` is a simple synchronization object. It contains up to 64 signals
|
||||
that threads can wait for and signal in parallel.
|
||||
|
||||
:capabilites: ``signal``, ``wait``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_event_create (j6_handle_t *self)
|
||||
|
||||
Undocumented
|
||||
|
||||
:param self: *[out]* Handle to the new event object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_event_signal (j6_handle_t self, uint64_t signals)
|
||||
|
||||
Signal events on this object
|
||||
|
||||
:capabilities: ``signal``
|
||||
|
||||
:param self: Handle to the event object
|
||||
:param signals: A bitset of which events to signal
|
||||
|
||||
.. cpp:function:: j6_result_t j6_event_wait (j6_handle_t self, uint64_t * signals, uint64_t timeout)
|
||||
|
||||
Wait for signaled events on this object
|
||||
|
||||
:capabilities: ``wait``
|
||||
|
||||
:param self: Handle to the event object
|
||||
:param signals: *[out]* A bitset of which events were signaled
|
||||
:param timeout: Wait timeout in nanoseconds
|
||||
|
||||
``mailbox`` syscalls
|
||||
-------------------------
|
||||
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.
|
||||
|
||||
:capabilites: ``send``, ``receive``, ``close``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_mailbox_create (j6_handle_t *self)
|
||||
|
||||
Undocumented
|
||||
|
||||
:param self: *[out]* Handle to the new mailbox object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_mailbox_close (j6_handle_t self)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``close``
|
||||
|
||||
:param self: Handle to the mailbox object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_mailbox_call (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_size, j6_handle_t * handles, size_t * handles_count, size_t handles_size)
|
||||
|
||||
Send a message to the reciever, and block until a response is
|
||||
sent. Note that getting this response does not require the
|
||||
receive capability.
|
||||
|
||||
:capabilities: ``send``
|
||||
|
||||
:param self: Handle to the mailbox object
|
||||
:param tag: *[inout]* Undocumented
|
||||
:param data: *[optional, inout]* Undocumented
|
||||
:param data_size: number of total bytes in data buffer
|
||||
:param handles: *[optional, inout, handle, list]* Undocumented
|
||||
:param handles_size: total size of handles buffer
|
||||
|
||||
.. cpp:function:: j6_result_t j6_mailbox_respond (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_size, j6_handle_t * handles, size_t * handles_count, size_t handles_size, uint64_t * reply_tag, uint64_t flags)
|
||||
|
||||
Respond to a message sent using call, and wait for another
|
||||
message to arrive. Note that this does not require the send
|
||||
capability. A reply tag of 0 skips the reply and goes directly
|
||||
to waiting for a new message.
|
||||
|
||||
:capabilities: ``receive``
|
||||
|
||||
:param self: Handle to the mailbox object
|
||||
:param tag: *[inout]* Undocumented
|
||||
:param data: *[optional, inout]* Undocumented
|
||||
:param data_size: number of total bytes in data buffer
|
||||
:param handles: *[optional, inout, handle, list]* Undocumented
|
||||
:param handles_size: total size of handles buffer
|
||||
:param reply_tag: *[inout]* Undocumented
|
||||
:param flags: Undocumented
|
||||
|
||||
``process`` syscalls
|
||||
-------------------------
|
||||
A ``process`` object represents a process running on the system, and allows
|
||||
control over the threads, handles, and virtual memory space of that process.
|
||||
|
||||
:capabilites: ``kill``, ``create_thread``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_process_create (j6_handle_t *self)
|
||||
|
||||
Create a new empty process
|
||||
|
||||
:param self: *[out]* Handle to the new process object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_process_kill (j6_handle_t self)
|
||||
|
||||
Stop all threads and exit the given process
|
||||
|
||||
:capabilities: ``kill``
|
||||
|
||||
:param self: Handle to the process object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_process_exit (int64_t result)
|
||||
|
||||
Stop all threads and exit the current process
|
||||
|
||||
:param result: The result to retrun to the parent process
|
||||
|
||||
.. cpp:function:: j6_result_t j6_process_give_handle (j6_handle_t self, j6_handle_t target)
|
||||
|
||||
Give the given process a handle that points to the same
|
||||
object as the specified handle.
|
||||
|
||||
:param self: Handle to the process object
|
||||
:param target: *[handle]* A handle in the caller process to send
|
||||
|
||||
``system`` syscalls
|
||||
-------------------------
|
||||
The singular ``system`` object represents a handle to kernel functionality
|
||||
needed by drivers and other priviledged services.
|
||||
|
||||
:capabilites: ``get_log``, ``bind_irq``, ``map_phys``, ``change_iopl``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_system_get_log (j6_handle_t self, uint64_t seen, void * buffer, size_t * buffer_len)
|
||||
|
||||
Get the next log line from the kernel log
|
||||
|
||||
:capabilities: ``get_log``
|
||||
|
||||
:param self: Handle to the system object
|
||||
:param seen: Last seen log id
|
||||
:param buffer: *[out, zero_ok]* Buffer for the log message data structure
|
||||
|
||||
.. cpp:function:: j6_result_t j6_system_bind_irq (j6_handle_t self, j6_handle_t dest, unsigned irq, unsigned signal)
|
||||
|
||||
Ask the kernel to send this process messages whenever
|
||||
the given IRQ fires
|
||||
|
||||
:capabilities: ``bind_irq``
|
||||
|
||||
:param self: Handle to the system object
|
||||
:param dest: Event object that will receive messages
|
||||
:param irq: IRQ number to bind
|
||||
:param signal: Signal number on the event to bind to
|
||||
|
||||
.. cpp:function:: j6_result_t j6_system_map_phys (j6_handle_t self, j6_handle_t * area, uintptr_t phys, size_t size, uint32_t flags)
|
||||
|
||||
Create a VMA and map an area of physical memory into it,
|
||||
also mapping that VMA into the current process
|
||||
|
||||
:capabilities: ``map_phys``
|
||||
|
||||
:param self: Handle to the system object
|
||||
:param area: *[out]* Receives a handle to the VMA created
|
||||
:param phys: The physical address of the area
|
||||
:param size: Size of the area, in bytes
|
||||
:param flags: Flags to apply to the created VMA
|
||||
|
||||
.. cpp:function:: j6_result_t j6_system_request_iopl (j6_handle_t self, unsigned iopl)
|
||||
|
||||
Request the kernel change the IOPL for this process. The only values
|
||||
that make sense are 0 and 3.
|
||||
|
||||
:capabilities: ``change_iopl``
|
||||
|
||||
:param self: Handle to the system object
|
||||
:param iopl: The IOPL to set for this process
|
||||
|
||||
``thread`` syscalls
|
||||
-------------------------
|
||||
A ``thread`` object represents a thread of execution within a process running
|
||||
on the system. The actual thread does not need to be currently running to
|
||||
hold a handle to it.
|
||||
|
||||
:capabilites: ``kill``, ``join``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_thread_create (j6_handle_t *self, j6_handle_t process, uintptr_t stack_top, uintptr_t entrypoint, uint64_t arg0, uint64_t arg1)
|
||||
|
||||
Undocumented
|
||||
|
||||
:param self: *[out]* Handle to the new thread object
|
||||
:param process: *[optional, cap]* Undocumented
|
||||
:param stack_top: Undocumented
|
||||
:param entrypoint: Undocumented
|
||||
:param arg0: Undocumented
|
||||
:param arg1: Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_thread_kill (j6_handle_t self)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``kill``
|
||||
|
||||
:param self: Handle to the thread object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_thread_join (j6_handle_t self)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``join``
|
||||
|
||||
:param self: Handle to the thread object
|
||||
|
||||
.. cpp:function:: j6_result_t j6_thread_exit ()
|
||||
|
||||
Undocumented
|
||||
|
||||
|
||||
.. cpp:function:: j6_result_t j6_thread_sleep (uint64_t duration)
|
||||
|
||||
Undocumented
|
||||
|
||||
:param duration: Undocumented
|
||||
|
||||
``vma`` syscalls
|
||||
-------------------------
|
||||
A ``vma`` object represents a single virtual memory area, which may be shared
|
||||
between several processes. A process having a handle to a ``vma`` does not
|
||||
necessarily mean that it is mapped into that process' virtual memory space.
|
||||
|
||||
:capabilites: ``map``, ``unmap``, ``resize``
|
||||
|
||||
.. cpp:function:: j6_result_t j6_vma_create (j6_handle_t *self, size_t size, uint32_t flags)
|
||||
|
||||
Undocumented
|
||||
|
||||
:param self: *[out]* Handle to the new vma object
|
||||
:param size: Undocumented
|
||||
:param flags: Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_vma_create_map (j6_handle_t *self, size_t size, uintptr_t * address, uint32_t flags)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``map``
|
||||
|
||||
:param self: *[out]* Handle to the new vma object
|
||||
:param size: Undocumented
|
||||
:param address: *[inout]* Undocumented
|
||||
:param flags: Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_vma_map (j6_handle_t self, j6_handle_t process, uintptr_t * address, uint32_t flags)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``map``
|
||||
|
||||
:param self: Handle to the vma object
|
||||
:param process: *[optional]* Undocumented
|
||||
:param address: *[inout]* Undocumented
|
||||
:param flags: Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_vma_unmap (j6_handle_t self, j6_handle_t process)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``unmap``
|
||||
|
||||
:param self: Handle to the vma object
|
||||
:param process: *[optional]* Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_vma_resize (j6_handle_t self, size_t * size)
|
||||
|
||||
Undocumented
|
||||
|
||||
:capabilities: ``resize``
|
||||
|
||||
:param self: Handle to the vma object
|
||||
:param size: *[inout]* New size for the VMA, or 0 to query the current size without changing
|
||||
|
||||
.. [[[end]]] (checksum: cb17f54e443d1d3b85995870f3e8dbf2)
|
||||
|
||||
Non-object syscalls
|
||||
-------------------
|
||||
|
||||
The following are the system calls that aren't constructors for objects, and
|
||||
either do not require an object handle, or operate generically on handles.
|
||||
|
||||
.. [[[cog code generation
|
||||
.. cog.outl()
|
||||
.. for func in syscalls.functions:
|
||||
.. args = []
|
||||
.. for param in func.params:
|
||||
.. for type, suffix in param.type.c_names(param.options):
|
||||
.. args.append(f"{type} {param.name}{suffix}")
|
||||
..
|
||||
.. cog.outl(f".. cpp:function:: j6_result_t j6_{func.name} ({', '.join(args)})")
|
||||
.. cog.outl()
|
||||
.. desc = func.desc or "Undocumented"
|
||||
.. cog.outl(indent(desc, " "))
|
||||
.. cog.outl()
|
||||
.. for param in func.params:
|
||||
.. opts = param.options and f"*[{', '.join(param.options)}]*" or ""
|
||||
.. desc = param.desc or 'Undocumented'
|
||||
.. cog.outl(f" :param {param.name}: {opts} {desc}")
|
||||
.. cog.outl()
|
||||
.. ]]]
|
||||
|
||||
.. cpp:function:: j6_result_t j6_noop ()
|
||||
|
||||
Simple no-op syscall for testing
|
||||
|
||||
|
||||
.. cpp:function:: j6_result_t j6_log (uint8_t area, uint8_t severity, const char * message)
|
||||
|
||||
Write a message to the kernel log
|
||||
|
||||
:param area: Undocumented
|
||||
:param severity: Undocumented
|
||||
:param message: Undocumented
|
||||
|
||||
.. cpp:function:: j6_result_t j6_handle_list (struct j6_handle_descriptor * handles, size_t * handles_size)
|
||||
|
||||
Get a list of handles owned by this process. If the
|
||||
supplied list is not big enough, will set the size
|
||||
needed in `size` and return j6_err_insufficient
|
||||
|
||||
:param handles: *[list, inout, zero_ok]* A list of handles to be filled
|
||||
|
||||
.. cpp:function:: j6_result_t j6_handle_clone (j6_handle_t orig, j6_handle_t * clone, uint32_t mask)
|
||||
|
||||
Create a clone of an existing handle, possibly with
|
||||
some capabilities masked out.
|
||||
|
||||
:param orig: *[handle, cap]* The handle to clone
|
||||
:param clone: *[out]* The new handle
|
||||
:param mask: The capability bitmask
|
||||
|
||||
.. cpp:function:: j6_result_t j6_handle_close (j6_handle_t hnd)
|
||||
|
||||
Close the handle to an object
|
||||
|
||||
:param hnd: *[handle]* The handle to close
|
||||
|
||||
.. cpp:function:: j6_result_t j6_futex_wait (const uint32_t * address, uint32_t current, uint64_t timeout)
|
||||
|
||||
Block waiting on a futex
|
||||
|
||||
:param address: Address of the futex value
|
||||
:param current: Current value of the futex
|
||||
:param timeout: Wait timeout in nanoseconds
|
||||
|
||||
.. cpp:function:: j6_result_t j6_futex_wake (const uint32_t * address, uint64_t count)
|
||||
|
||||
Wake threads waiting on a futex
|
||||
|
||||
:param address: Address of the futex value
|
||||
:param count: Number of threads to wake, or 0 for all
|
||||
|
||||
.. cpp:function:: j6_result_t j6_test_finish (uint32_t exit_code)
|
||||
|
||||
Testing mode only: Have the kernel finish and exit QEMU with the given exit code
|
||||
|
||||
:param exit_code: Undocumented
|
||||
|
||||
.. [[[end]]] (checksum: 0b9d051972abcbb6de408f411331785f)
|
||||
|
||||
8
qemu.sh
8
qemu.sh
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
root=$(dirname $0)
|
||||
build="${root}/build"
|
||||
build="${root}/build.amd64"
|
||||
assets="${root}/assets"
|
||||
|
||||
no_build=""
|
||||
@@ -41,7 +41,7 @@ while true; do
|
||||
-r | --remote)
|
||||
shift
|
||||
vnchost="${1:-${VNCHOST:-"localhost:5500"}}"
|
||||
gfx="-vnc ${vnchost},reverse"
|
||||
gfx="-vnc ${vnchost},reverse=on"
|
||||
vga=""
|
||||
shift
|
||||
;;
|
||||
@@ -117,13 +117,13 @@ if [[ -n $TMUX ]]; then
|
||||
tmux split-window -h -l $log_width "$debugcon_cmd"
|
||||
tmux last-pane
|
||||
fi
|
||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454"
|
||||
tmux split-window -l 10 "sleep 1; nc localhost 45454"
|
||||
fi
|
||||
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||
if [[ -n $debug ]]; then
|
||||
i3-msg exec i3-sensible-terminal -- -e "gdb ${debugtarget}" &
|
||||
else
|
||||
i3-msg exec i3-sensible-terminal -- -e 'telnet localhost 45454' &
|
||||
i3-msg exec i3-sensible-terminal -- -e 'nc localhost 45454' &
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -7,3 +7,5 @@ pure-cdb == 4
|
||||
pyzstd == 0.15
|
||||
pyelftools
|
||||
iced-x86
|
||||
sphinx
|
||||
renku-sphinx-theme
|
||||
|
||||
@@ -43,10 +43,10 @@ def _make_ninja_config(outfile, config, files):
|
||||
build.variable(k, v)
|
||||
|
||||
|
||||
def generate_configs(root, output, config, targets, kinds):
|
||||
def generate_configs(root, output, buildconfig, arch, targets, kinds):
|
||||
|
||||
assets = root / "assets" / "build"
|
||||
base = ["global.yaml", f"config.{config}.yaml"]
|
||||
assets = root / "assets" / "build" / arch
|
||||
base = ["global.yaml", f"config.{buildconfig}.yaml"]
|
||||
|
||||
depfiles = set()
|
||||
|
||||
@@ -65,4 +65,4 @@ def generate_configs(root, output, config, targets, kinds):
|
||||
outfile = output / target / f"config.{kind}.ninja"
|
||||
_make_ninja_config(outfile, config, files)
|
||||
|
||||
return depfiles
|
||||
return depfiles
|
||||
|
||||
@@ -37,9 +37,15 @@ 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]))
|
||||
def get_libdeps(names):
|
||||
libmods = modules.get_mods(names)
|
||||
deps = modules.all_deps(libmods, stop_at_static=True)
|
||||
deps = [m.name for m in deps if m.kind == "lib"]
|
||||
return deps
|
||||
|
||||
libs = set(get_libdeps(config.get("libs", tuple())))
|
||||
libs.update(get_libdeps([e.module for e in self.services]))
|
||||
libs.update(get_libdeps([e.module for e in self.drivers]))
|
||||
|
||||
self.libs = [self.__build_entry(modules, i)
|
||||
for i in libs]
|
||||
@@ -80,13 +86,6 @@ class Manifest:
|
||||
|
||||
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)
|
||||
self.data.append(e)
|
||||
|
||||
@@ -39,7 +39,7 @@ class Module:
|
||||
"kind": (str, "exe"),
|
||||
"outfile": (str, None),
|
||||
"basename": (str, None),
|
||||
"targets": (set, ()),
|
||||
"target": (str, None),
|
||||
"deps": (set, ()),
|
||||
"public_headers": (set, ()),
|
||||
"copy_headers": (bool, False),
|
||||
@@ -53,6 +53,8 @@ class Module:
|
||||
"no_libc": (bool, False),
|
||||
"ld_script": (str, None),
|
||||
"static": (bool, False),
|
||||
"arch_source": (dict, ()),
|
||||
"skip_arches": (tuple, ()),
|
||||
}
|
||||
|
||||
def __init__(self, name, modfile, root, **kwargs):
|
||||
@@ -81,15 +83,14 @@ class Module:
|
||||
|
||||
# Turn strings into real Source objects
|
||||
self.sources = [make_source(root, f) for f in self.sources]
|
||||
for arch in self.arch_source:
|
||||
self.arch_source[arch] = [make_source(root, f) for f in self.arch_source[arch]]
|
||||
|
||||
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 __repr__(self):
|
||||
return f"<Module {self.kind} {self.name}>"
|
||||
|
||||
@@ -115,227 +116,6 @@ class Module:
|
||||
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
|
||||
return self.basename + ext.get(self.kind, "")
|
||||
|
||||
@classmethod
|
||||
def update(cls, mods):
|
||||
from . import BonnibelError
|
||||
|
||||
def resolve(source, modlist):
|
||||
resolved = set()
|
||||
for dep in modlist:
|
||||
if not dep in mods:
|
||||
raise BonnibelError(f"module '{source.name}' references unknown module '{dep}'")
|
||||
mod = mods[dep]
|
||||
resolved.add(mod)
|
||||
return resolved
|
||||
|
||||
for mod in mods.values():
|
||||
mod.depmods = resolve(mod, mod.deps)
|
||||
|
||||
target_mods = [mod for mod in mods.values() if mod.targets]
|
||||
for mod in target_mods:
|
||||
closed = set()
|
||||
children = set(mod.depmods)
|
||||
while children:
|
||||
child = children.pop()
|
||||
closed.add(child)
|
||||
child.targets |= mod.targets
|
||||
children |= {m for m in child.depmods if not m in closed}
|
||||
|
||||
def generate(self, output):
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
from ninja.ninja_syntax import Writer
|
||||
|
||||
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, self.static, all_deps)
|
||||
all_deps = all_deps.values()
|
||||
|
||||
def gather_phony(build, deps, child_rel):
|
||||
phony = ".headers.phony"
|
||||
child_phony = [child_rel(phony, module=c.name)
|
||||
for c, _ in all_deps]
|
||||
|
||||
build.build(
|
||||
rule = "touch",
|
||||
outputs = [mod_rel(phony)],
|
||||
implicit = child_phony,
|
||||
order_only = list(map(mod_rel, deps)),
|
||||
)
|
||||
|
||||
filename = str(output / f"module.{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", 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(
|
||||
local = [self.root, "${module_dir}"],
|
||||
ld_script = self.ld_script and self.root / self.ld_script,
|
||||
)
|
||||
if self.public_headers:
|
||||
modopts.includes += [
|
||||
self.root / "include",
|
||||
f"${{target_dir}}/{self.name}.dir/include",
|
||||
]
|
||||
|
||||
for key, value in self.variables.items():
|
||||
build.variable(key, value)
|
||||
build.newline()
|
||||
|
||||
for include in self.includes:
|
||||
p = Path(include)
|
||||
if p.is_absolute():
|
||||
if not p in modopts.includes:
|
||||
modopts.includes.append(str(p.resolve()))
|
||||
elif include != ".":
|
||||
incpath = self.root / p
|
||||
destpath = mod_rel(p)
|
||||
for header in incpath.rglob("*.h"):
|
||||
dest_header = f"{destpath}/" + str(header.relative_to(incpath))
|
||||
modopts.includes.append(str(incpath))
|
||||
modopts.includes.append(destpath)
|
||||
|
||||
for dep, static in all_deps:
|
||||
if dep.public_headers:
|
||||
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 == "headers":
|
||||
continue
|
||||
elif dep.kind == "lib":
|
||||
modopts.libs.append((target_rel(dep.get_output(static)), static))
|
||||
else:
|
||||
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:
|
||||
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", ["-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)
|
||||
while sources:
|
||||
source = sources.pop()
|
||||
sources.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))
|
||||
|
||||
gather_phony(build, header_deps, target_rel)
|
||||
|
||||
if self.kind == "headers":
|
||||
# Header-only, don't output a build rule
|
||||
return
|
||||
|
||||
output = target_rel(self.get_output())
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = self.kind,
|
||||
outputs = output,
|
||||
inputs = inputs,
|
||||
implicit = modopts.implicit,
|
||||
order_only = modopts.order_only,
|
||||
variables = {"name": self.name,
|
||||
"soname": self.get_output()},
|
||||
)
|
||||
|
||||
dump = output + ".dump"
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = "dump",
|
||||
outputs = dump,
|
||||
inputs = output,
|
||||
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 make_source
|
||||
s = make_source(self.root, path, **kwargs)
|
||||
@@ -350,3 +130,285 @@ class Module:
|
||||
for source in self.public_headers:
|
||||
if source.path in paths:
|
||||
source.add_deps(deps)
|
||||
|
||||
|
||||
class ModuleList:
|
||||
def __init__(self, arch):
|
||||
self.__arch = arch
|
||||
self.__mods = {}
|
||||
self.__used = {}
|
||||
self.__targets = frozenset()
|
||||
self.__kinds = frozenset()
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.__mods.get(name)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""Return if the module name is known."""
|
||||
return name in self.__mods
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over _non-skipped_ modules."""
|
||||
return self.used.__iter__()
|
||||
|
||||
def __skip(self, mod):
|
||||
if self.__arch in mod.skip_arches: return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def used(self):
|
||||
if self.__used is None:
|
||||
self.__used = {n:m for n,m in self.__mods.items() if not self.__skip(m)}
|
||||
return self.__used
|
||||
|
||||
@property
|
||||
def targets(self):
|
||||
if self.__targets is None:
|
||||
self.__targets = frozenset([m.target for m in self.used.values() if m.target])
|
||||
return self.__targets
|
||||
|
||||
@property
|
||||
def kinds(self):
|
||||
if self.__kinds is None:
|
||||
self.__kinds = frozenset([m.kind for m in self.used.values()])
|
||||
return self.__kinds
|
||||
|
||||
def get(self, name):
|
||||
return self.__mods.get(name)
|
||||
|
||||
def add(self, mod):
|
||||
if mod.name in self.__mods:
|
||||
raise BonnibelError(f"re-adding module '{mod.name}' to this ModuleList")
|
||||
self.__mods[mod.name] = mod
|
||||
self.__used = None
|
||||
self.__targets = None
|
||||
self.__kinds = None
|
||||
|
||||
def get_mods(self, names, filt=None):
|
||||
return {self[n] for n in names
|
||||
if n in self.used
|
||||
and ((not filt) or filt(self[n]))}
|
||||
|
||||
def all_deps(self, mods, stop_at_static=False):
|
||||
search = set(mods)
|
||||
closed = list()
|
||||
|
||||
while search:
|
||||
mod = search.pop()
|
||||
if mod in closed: continue
|
||||
closed.append(mod)
|
||||
|
||||
if stop_at_static and mod.static:
|
||||
continue
|
||||
|
||||
for dep in mod.deps:
|
||||
if not dep in self.__mods:
|
||||
raise BonnibelError(f"module '{mod.name}' references unknown module '{dep}'")
|
||||
if dep in self.used:
|
||||
search.add(self.used[dep])
|
||||
|
||||
return closed
|
||||
|
||||
def target_mods(self, target):
|
||||
return self.all_deps([m for m in self.used.values() if m.target == target])
|
||||
|
||||
def generate(self, output):
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
from ninja.ninja_syntax import Writer
|
||||
|
||||
def walk_deps(deps, static, results):
|
||||
for modname in deps:
|
||||
mod = self.used.get(modname)
|
||||
if not mod: continue # skipped
|
||||
if static or modname not in results:
|
||||
results[modname] = (mod, static)
|
||||
walk_deps(mod.deps, static or mod.static, results)
|
||||
|
||||
for mod in self.used.values():
|
||||
all_deps = {}
|
||||
walk_deps(mod.deps, mod.static, all_deps)
|
||||
all_deps = all_deps.values()
|
||||
|
||||
def gather_phony(build, deps, child_rel):
|
||||
phony = ".headers.phony"
|
||||
child_phony = [child_rel(phony, module=c.name)
|
||||
for c, _ in all_deps]
|
||||
|
||||
build.build(
|
||||
rule = "touch",
|
||||
outputs = [mod_rel(phony)],
|
||||
implicit = child_phony,
|
||||
order_only = list(map(mod_rel, deps)),
|
||||
)
|
||||
|
||||
filename = str(output / f"module.{mod.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", target_rel(mod.name + ".dir"))
|
||||
build.variable("module_kind", mod.kind)
|
||||
build.newline()
|
||||
|
||||
build.include(f"${{target_dir}}/config.{mod.kind}.ninja")
|
||||
build.newline()
|
||||
|
||||
modopts = BuildOptions(
|
||||
local = [mod.root, "${module_dir}"],
|
||||
ld_script = mod.ld_script and mod.root / mod.ld_script,
|
||||
)
|
||||
if mod.public_headers:
|
||||
modopts.includes += [
|
||||
mod.root / "include",
|
||||
f"${{target_dir}}/{mod.name}.dir/include",
|
||||
]
|
||||
|
||||
for key, value in mod.variables.items():
|
||||
build.variable(key, value)
|
||||
build.newline()
|
||||
|
||||
for include in mod.includes:
|
||||
p = Path(include)
|
||||
if p.is_absolute():
|
||||
if not p in modopts.includes:
|
||||
modopts.includes.append(str(p.resolve()))
|
||||
elif include != ".":
|
||||
incpath = mod.root / p
|
||||
destpath = mod_rel(p)
|
||||
for header in incpath.rglob("*.h"):
|
||||
dest_header = f"{destpath}/" + str(header.relative_to(incpath))
|
||||
modopts.includes.append(str(incpath))
|
||||
modopts.includes.append(destpath)
|
||||
|
||||
for dep, static in all_deps:
|
||||
if dep.public_headers:
|
||||
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 == "headers":
|
||||
continue
|
||||
elif dep.kind == "lib":
|
||||
modopts.libs.append((target_rel(dep.get_output(static)), static))
|
||||
else:
|
||||
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:
|
||||
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", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
|
||||
|
||||
if modopts.ld_script:
|
||||
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
|
||||
|
||||
header_deps = []
|
||||
inputs = []
|
||||
headers = set(mod.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(mod.sources)
|
||||
sources.update(set(mod.arch_source.get(self.__arch, tuple())))
|
||||
while sources:
|
||||
source = sources.pop()
|
||||
sources.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))
|
||||
|
||||
gather_phony(build, header_deps, target_rel)
|
||||
|
||||
if mod.kind == "headers":
|
||||
# Header-only, don't output a build rule
|
||||
continue
|
||||
|
||||
mod_output = target_rel(mod.get_output())
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = mod.kind,
|
||||
outputs = mod_output,
|
||||
inputs = inputs,
|
||||
implicit = modopts.implicit,
|
||||
order_only = modopts.order_only,
|
||||
variables = {"name": mod.name,
|
||||
"soname": mod.get_output()},
|
||||
)
|
||||
|
||||
dump = mod_output + ".dump"
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = "dump",
|
||||
outputs = dump,
|
||||
inputs = mod_output,
|
||||
variables = {"name": mod.name},
|
||||
)
|
||||
|
||||
s_output = target_rel(mod.get_output(static=True))
|
||||
if s_output != mod_output:
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = mod.kind + "_static",
|
||||
outputs = s_output,
|
||||
inputs = inputs,
|
||||
order_only = modopts.order_only,
|
||||
variables = {"name": mod.name},
|
||||
)
|
||||
|
||||
dump = s_output + ".dump"
|
||||
build.newline()
|
||||
build.build(
|
||||
rule = "dump",
|
||||
outputs = dump,
|
||||
inputs = s_output,
|
||||
variables = {"name": mod.name},
|
||||
)
|
||||
|
||||
if mod.default:
|
||||
build.newline()
|
||||
build.default(mod_output)
|
||||
build.default(dump)
|
||||
|
||||
|
||||
|
||||
@@ -10,22 +10,17 @@ class Project:
|
||||
def __str__(self):
|
||||
return f"{self.name} {self.version.major}.{self.version.minor}.{self.version.patch}-{self.version.sha}"
|
||||
|
||||
def generate(self, root, output, modules, config, manifest_file):
|
||||
def generate(self, root, output, modules, config, arch, manifest_file):
|
||||
import sys
|
||||
from os.path import join
|
||||
from ninja.ninja_syntax import Writer
|
||||
|
||||
targets = set()
|
||||
kinds = set()
|
||||
for mod in modules.values():
|
||||
targets.update(mod.targets)
|
||||
kinds.add(mod.kind)
|
||||
|
||||
from .config import generate_configs
|
||||
config_deps = generate_configs(root, output, config, targets, kinds)
|
||||
config_deps = generate_configs(root, output, config, arch, modules.targets, modules.kinds)
|
||||
|
||||
with open(output / "build.ninja", "w") as buildfile:
|
||||
build = Writer(buildfile)
|
||||
default_builds = []
|
||||
|
||||
build.comment("This file is automatically generated by bonnibel")
|
||||
build.variable("ninja_required_version", "1.3")
|
||||
@@ -34,7 +29,7 @@ class Project:
|
||||
build.variable("build_config", config)
|
||||
build.newline()
|
||||
|
||||
build.include(root / "assets/build/rules.ninja")
|
||||
build.include(root / "assets" / "build" / arch / "rules.ninja")
|
||||
build.newline()
|
||||
|
||||
build.variable("version_major", self.version.major)
|
||||
@@ -49,7 +44,7 @@ class Project:
|
||||
])
|
||||
build.newline()
|
||||
|
||||
for target in targets:
|
||||
for target in modules.targets:
|
||||
build.subninja(output / target / "target.ninja")
|
||||
build.newline()
|
||||
|
||||
@@ -57,7 +52,7 @@ class Project:
|
||||
rule = "touch",
|
||||
outputs = "${build_root}/.all_headers",
|
||||
implicit = [f"${{build_root}}/include/{m.name}/.headers.phony"
|
||||
for m in modules.values() if m.public_headers],
|
||||
for m in modules.used.values() if m.public_headers],
|
||||
)
|
||||
build.build(
|
||||
rule = "phony",
|
||||
@@ -78,56 +73,24 @@ class Project:
|
||||
initrdroot = output / "initrd_root"
|
||||
initrdroot.mkdir(exist_ok=True)
|
||||
|
||||
fatroot_content = []
|
||||
initrd_content = []
|
||||
image_content = {'initrd_root': [], 'fatroot': []}
|
||||
|
||||
def add_fatroot(source, name):
|
||||
output = join(manifest.location, name)
|
||||
fatroot_output = f"${{build_root}}/fatroot/{output}"
|
||||
def add_image_content(image, path, name):
|
||||
output = join(path, name)
|
||||
image_output = f"${{build_root}}/{image}/{output}"
|
||||
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [fatroot_output],
|
||||
inputs = [source],
|
||||
variables = {
|
||||
"description": f"Installing {output}",
|
||||
})
|
||||
|
||||
fatroot_content.append(fatroot_output)
|
||||
build.newline()
|
||||
|
||||
def add_fatroot_exe(entry):
|
||||
input_path = f"${{build_root}}/{entry.target}/{entry.output}"
|
||||
intermediary = f"${{build_root}}/{entry.output}"
|
||||
|
||||
build.build(
|
||||
rule = "strip",
|
||||
outputs = [intermediary],
|
||||
inputs = [input_path],
|
||||
implicit = [f"{input_path}.dump"],
|
||||
variables = {
|
||||
"name": f"Stripping {entry.module}",
|
||||
"debug": f"${{build_root}}/.debug/{entry.output}.debug",
|
||||
})
|
||||
|
||||
add_fatroot(intermediary, entry.output)
|
||||
|
||||
def add_initrd_content(root, name):
|
||||
output = join(root, name)
|
||||
initrd_output = f"${{build_root}}/initrd_root/{output}"
|
||||
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [initrd_output],
|
||||
outputs = [image_output],
|
||||
inputs = [f"${{build_root}}/{name}"],
|
||||
variables = {
|
||||
"description": f"Installing {name}",
|
||||
})
|
||||
|
||||
initrd_content.append(initrd_output)
|
||||
image_content[image].append(image_output)
|
||||
build.newline()
|
||||
|
||||
def add_initrd_stripped(root, entry):
|
||||
def add_image_content_exe(image, path, entry):
|
||||
input_path = f"${{build_root}}/{entry.target}/{entry.output}"
|
||||
intermediary = f"${{build_root}}/{entry.output}"
|
||||
|
||||
@@ -141,87 +104,108 @@ class Project:
|
||||
"debug": f"${{build_root}}/.debug/{entry.output}.debug",
|
||||
})
|
||||
|
||||
add_initrd_content(root, entry.output)
|
||||
build.build(
|
||||
rule = "phony",
|
||||
outputs = [entry.output],
|
||||
inputs = [intermediary])
|
||||
|
||||
add_image_content(image, path, entry.output)
|
||||
|
||||
if 'kernel' in modules.used:
|
||||
add_image_content_exe("fatroot", manifest.location, manifest.kernel)
|
||||
|
||||
syms = manifest.add_data("symbol_table.dat",
|
||||
"Symbol table", ("symbols",))
|
||||
|
||||
syms_file = "jsix.symbols"
|
||||
syms_path = join(manifest.location, syms_file );
|
||||
syms_out = join(fatroot, syms_path)
|
||||
build.build(
|
||||
rule = "makest",
|
||||
outputs = [syms_out],
|
||||
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
|
||||
)
|
||||
image_content['fatroot'].append(syms_out)
|
||||
manifest.symbols = syms_file
|
||||
|
||||
build.newline()
|
||||
default_builds.append("${build_root}/jsix.img")
|
||||
|
||||
def try_add_manifest_module(entry, section, image, path="jsix"):
|
||||
if entry.module in modules.used:
|
||||
add_image_content_exe(image, path, entry)
|
||||
elif entry.module not in modules:
|
||||
raise BonnibelError(f'unknown {section} module in manifest: {entry.module}')
|
||||
|
||||
try_add_manifest_module(manifest.init, "init", "fatroot")
|
||||
|
||||
add_fatroot_exe(manifest.kernel)
|
||||
add_fatroot_exe(manifest.init)
|
||||
for program in manifest.panics:
|
||||
add_fatroot_exe(program)
|
||||
try_add_manifest_module(program, "panic", "fatroot")
|
||||
|
||||
for program in manifest.services:
|
||||
add_initrd_stripped("jsix/services", program)
|
||||
try_add_manifest_module(program, "services", "initrd_root", "jsix/services")
|
||||
|
||||
for program in manifest.drivers:
|
||||
add_initrd_stripped("jsix/drivers", program)
|
||||
try_add_manifest_module(program, "drivers", "initrd_root", "jsix/drivers")
|
||||
|
||||
for program in manifest.libs:
|
||||
add_initrd_stripped("jsix/lib", program)
|
||||
try_add_manifest_module(program, "libs", "initrd_root", "jsix/lib")
|
||||
|
||||
syms = manifest.add_data("symbol_table.dat",
|
||||
"Symbol table", ("symbols",))
|
||||
extra_regen_outputs = []
|
||||
|
||||
syms_file = "jsix.symbols"
|
||||
syms_path = join(manifest.location, syms_file );
|
||||
syms_out = join(fatroot, syms_path)
|
||||
build.build(
|
||||
rule = "makest",
|
||||
outputs = [syms_out],
|
||||
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
|
||||
)
|
||||
fatroot_content.append(syms_out)
|
||||
manifest.symbols = syms_file
|
||||
|
||||
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [bootloader],
|
||||
inputs = ["${build_root}/boot/boot.efi"],
|
||||
variables = {
|
||||
"description": "Installing bootloader",
|
||||
})
|
||||
build.newline()
|
||||
|
||||
boot_config = join(fatroot, "jsix", "boot.conf")
|
||||
manifest.write_boot_config(boot_config)
|
||||
|
||||
initrd = str(fatroot / manifest.location / manifest.initrd["name"])
|
||||
build.build(
|
||||
rule = "makeinitrd",
|
||||
outputs = [initrd],
|
||||
inputs = [str(initrdroot)],
|
||||
implicit = initrd_content + ["${source_root}/scripts/mkj6romfs.py"],
|
||||
variables = {"format": manifest.initrd["format"]},
|
||||
)
|
||||
build.newline()
|
||||
|
||||
fatroot_content.append(initrd)
|
||||
|
||||
build.build(
|
||||
rule = "makefat",
|
||||
outputs = ["${build_root}/jsix.img"],
|
||||
inputs = ["${source_root}/assets/diskbase.img"],
|
||||
implicit = fatroot_content + [bootloader],
|
||||
variables = {"name": "jsix.img"},
|
||||
)
|
||||
build.newline()
|
||||
|
||||
default_assets = {
|
||||
"UEFI Variables": ("ovmf/x64/ovmf_vars.fd", "ovmf_vars.fd"),
|
||||
"GDB Debug Helpers": ("debugging/jsix.elf-gdb.py", "jsix.elf-gdb.py"),
|
||||
}
|
||||
|
||||
for name, assets in default_assets.items():
|
||||
p = root / "assets" / assets[0]
|
||||
out = f"${{build_root}}/{assets[1]}"
|
||||
if 'boot' in modules.used:
|
||||
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [out],
|
||||
inputs = [str(p)],
|
||||
variables = {"name": name},
|
||||
)
|
||||
build.default([out])
|
||||
outputs = [bootloader],
|
||||
inputs = ["${build_root}/boot/boot.efi"],
|
||||
variables = {
|
||||
"description": "Installing bootloader",
|
||||
})
|
||||
build.newline()
|
||||
|
||||
boot_config = join(fatroot, "jsix", "boot.conf")
|
||||
manifest.write_boot_config(boot_config)
|
||||
extra_regen_outputs.append(boot_config)
|
||||
|
||||
initrd = str(fatroot / manifest.location / manifest.initrd["name"])
|
||||
build.build(
|
||||
rule = "makeinitrd",
|
||||
outputs = [initrd],
|
||||
inputs = [str(initrdroot)],
|
||||
implicit = image_content['initrd_root'] + ["${source_root}/scripts/mkj6romfs.py"],
|
||||
variables = {"format": manifest.initrd["format"]},
|
||||
)
|
||||
build.newline()
|
||||
|
||||
image_content['fatroot'].append(initrd)
|
||||
|
||||
build.build(
|
||||
rule = "makefat",
|
||||
outputs = ["${build_root}/jsix.img"],
|
||||
inputs = ["${source_root}/assets/diskbase.img"],
|
||||
implicit = image_content['fatroot'] + [bootloader],
|
||||
variables = {"name": "jsix.img"},
|
||||
)
|
||||
build.newline()
|
||||
|
||||
default_assets = {
|
||||
"UEFI Variables": ("ovmf/x64/ovmf_vars.fd", "ovmf_vars.fd"),
|
||||
"GDB Debug Helpers": ("debugging/jsix.elf-gdb.py", "jsix.elf-gdb.py"),
|
||||
}
|
||||
|
||||
for name, assets in default_assets.items():
|
||||
p = root / "assets" / assets[0]
|
||||
out = f"${{build_root}}/{assets[1]}"
|
||||
build.build(
|
||||
rule = "cp",
|
||||
outputs = [out],
|
||||
inputs = [str(p)],
|
||||
variables = {"name": name},
|
||||
)
|
||||
build.newline()
|
||||
default_builds.append(out)
|
||||
|
||||
compdb = "${source_root}/compile_commands.json"
|
||||
|
||||
build.rule("regen",
|
||||
@@ -233,7 +217,7 @@ class Project:
|
||||
|
||||
regen_implicits = \
|
||||
[f"{self.root}/configure", str(manifest_file)] + \
|
||||
[str(mod.modfile) for mod in modules.values()]
|
||||
[str(mod.modfile) for mod in modules.used.values()]
|
||||
|
||||
regen_implicits += list(map(str, config_deps))
|
||||
|
||||
@@ -242,24 +226,24 @@ class Project:
|
||||
outputs = [compdb],
|
||||
implicit = regen_implicits,
|
||||
)
|
||||
build.default([compdb])
|
||||
build.newline()
|
||||
default_builds.append(compdb)
|
||||
|
||||
build.build(
|
||||
rule = "regen",
|
||||
outputs = ['build.ninja'],
|
||||
implicit = regen_implicits,
|
||||
implicit_outputs =
|
||||
[f"module.{mod.name}.ninja" for mod in modules.values()] +
|
||||
[f"{target}/target.ninja" for target in targets] +
|
||||
[boot_config],
|
||||
[f"module.{mod.name}.ninja" for mod in modules.used.values()] +
|
||||
[f"{target}/target.ninja" for target in modules.targets] +
|
||||
extra_regen_outputs,
|
||||
)
|
||||
|
||||
build.newline()
|
||||
build.default(["${build_root}/jsix.img"])
|
||||
build.default(default_builds)
|
||||
|
||||
for target in targets:
|
||||
mods = [m.name for m in modules.values() if target in m.targets]
|
||||
for target in modules.targets:
|
||||
mods = modules.target_mods(target)
|
||||
|
||||
targetdir = output / target
|
||||
targetdir.mkdir(exist_ok=True)
|
||||
@@ -290,4 +274,4 @@ class Project:
|
||||
build.newline()
|
||||
|
||||
for mod in mods:
|
||||
build.subninja(f"module.{mod}.ninja")
|
||||
build.subninja(f"module.{mod.name}.ninja")
|
||||
|
||||
21
scripts/codegen/__init__.py
Normal file
21
scripts/codegen/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import cog
|
||||
|
||||
supported_architectures = {
|
||||
"amd64": "__amd64__",
|
||||
}
|
||||
|
||||
def arch_includes(header, root=""):
|
||||
from pathlib import Path
|
||||
root = Path(root)
|
||||
header = Path(header)
|
||||
|
||||
prefix = "if"
|
||||
for arch, define in supported_architectures.items():
|
||||
path = root / "arch" / arch / header
|
||||
cog.outl(f"#{prefix} defined({define})")
|
||||
cog.outl(f"#include <{path}>")
|
||||
prefix = "elif"
|
||||
cog.outl("#else")
|
||||
cog.outl('#error "Unsupported platform"')
|
||||
cog.outl("#endif")
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import cog
|
||||
|
||||
supported_architectures = {
|
||||
"amd64": "__amd64__",
|
||||
}
|
||||
|
||||
def arch_includes(header):
|
||||
prefix = "if"
|
||||
for arch, define in supported_architectures.items():
|
||||
cog.outl(f"#{prefix} defined({define})")
|
||||
cog.outl(f"#include <__j6libc/arch/{arch}/{header}>")
|
||||
prefix = "elif"
|
||||
cog.outl("#endif")
|
||||
|
||||
|
||||
int_widths = (8, 16, 32, 64)
|
||||
int_mods = ("fast", "least")
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
|
||||
def unit(size):
|
||||
units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']
|
||||
|
||||
o = 0
|
||||
while size >= 1024 and o < len(units):
|
||||
o += 1
|
||||
size = size >> 10
|
||||
|
||||
return f"{size} {units[o]}"
|
||||
|
||||
|
||||
class Layout:
|
||||
from collections import namedtuple
|
||||
Region = namedtuple("Region", ("name", "start", "size", "shared"))
|
||||
Region = namedtuple("Region", ("name", "desc", "start", "size", "shared"))
|
||||
|
||||
sizes = {'G': 1024 ** 3, 'T': 1024 ** 4}
|
||||
|
||||
@@ -26,7 +38,7 @@ class Layout:
|
||||
for r in data:
|
||||
size = Layout.get_size(r["size"])
|
||||
addr -= size
|
||||
regions.append(Layout.Region(r["name"], addr, size,
|
||||
regions.append(Layout.Region(r["name"], r["desc"], addr, size,
|
||||
r.get("shared", False)))
|
||||
|
||||
self.regions = tuple(regions)
|
||||
|
||||
@@ -104,7 +104,7 @@ def add_dir(path, files, dirs, inode_data, dir_inodes, output, compress):
|
||||
compressed = uncompressed
|
||||
comp_size = uncomp_size
|
||||
|
||||
output.write(uncompressed)
|
||||
output.write(compressed)
|
||||
inode_data.append((inode_type_dir, offset, comp_size, uncomp_size))
|
||||
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
boot = module("boot",
|
||||
kind = "exe",
|
||||
outfile = "boot.efi",
|
||||
targets = [ "boot" ],
|
||||
target = "boot",
|
||||
deps = [ "cpu", "elf", "util", "bootproto" ],
|
||||
static = True,
|
||||
skip_arches = [ "linux" ],
|
||||
sources = [
|
||||
"allocator.cpp",
|
||||
"bootconfig.cpp",
|
||||
|
||||
@@ -22,7 +22,7 @@ read_string(util::buffer &data)
|
||||
static void
|
||||
read_descriptor(descriptor &e, util::buffer &data)
|
||||
{
|
||||
e.flags = static_cast<desc_flags>(*util::read<uint16_t>(data));
|
||||
e.flags = util::bitset16 {*util::read<uint16_t>(data)};
|
||||
e.path = read_string(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <bootproto/bootconfig.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace uefi {
|
||||
@@ -14,7 +15,7 @@ namespace boot {
|
||||
using desc_flags = bootproto::desc_flags;
|
||||
|
||||
struct descriptor {
|
||||
desc_flags flags;
|
||||
util::bitset16 flags;
|
||||
wchar_t const *path;
|
||||
};
|
||||
|
||||
@@ -27,7 +28,7 @@ public:
|
||||
/// Constructor. Loads bootconfig from the given buffer.
|
||||
bootconfig(util::buffer data, uefi::boot_services *bs);
|
||||
|
||||
inline uint16_t flags() const { return m_flags; }
|
||||
inline util::bitset16 flags() const { return m_flags; }
|
||||
inline const descriptor & kernel() const { return m_kernel; }
|
||||
inline const descriptor & init() const { return m_init; }
|
||||
inline const wchar_t * initrd() const { return m_initrd; }
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
inline const descriptors & panics() { return m_panics; }
|
||||
|
||||
private:
|
||||
uint16_t m_flags;
|
||||
util::bitset16 m_flags;
|
||||
descriptor m_kernel;
|
||||
descriptor m_init;
|
||||
descriptors m_panics;
|
||||
|
||||
@@ -110,7 +110,7 @@ parse_program(const wchar_t *name, util::const_buffer data, bootproto::program &
|
||||
section.phys_addr = elf.base() + seg.offset;
|
||||
section.virt_addr = seg.vaddr;
|
||||
section.size = seg.mem_size;
|
||||
section.type = static_cast<bootproto::section_flags>(seg.flags);
|
||||
section.type = seg.flags;
|
||||
|
||||
if (seg.mem_size != seg.file_size)
|
||||
section.phys_addr = allocate_bss(seg);
|
||||
@@ -128,8 +128,6 @@ load_program(
|
||||
paging::pager &pager,
|
||||
bool verify)
|
||||
{
|
||||
using util::bits::has;
|
||||
|
||||
status_line status(L"Loading program", name);
|
||||
|
||||
elf::file elf {data};
|
||||
@@ -155,8 +153,8 @@ load_program(
|
||||
|
||||
pager.map_pages(phys_addr, seg.vaddr,
|
||||
memory::bytes_to_pages(seg.mem_size),
|
||||
has(seg.flags, elf::segment_flags::write),
|
||||
has(seg.flags, elf::segment_flags::exec));
|
||||
seg.flags.get(elf::segment_flags::write),
|
||||
seg.flags.get(elf::segment_flags::exec));
|
||||
}
|
||||
|
||||
return elf.entrypoint();
|
||||
|
||||
@@ -82,9 +82,8 @@ load_resources(
|
||||
util::buffer kernel = loader::load_file(disk, bc.kernel().path);
|
||||
uintptr_t kentry = loader::load_program(kernel, L"jsix kernel", pager, true);
|
||||
|
||||
args->flags = static_cast<bootproto::boot_flags>(bc.flags());
|
||||
args->flags = bc.flags();
|
||||
|
||||
namespace bits = util::bits;
|
||||
using bootproto::desc_flags;
|
||||
|
||||
bool has_panic = false;
|
||||
@@ -96,7 +95,7 @@ load_resources(
|
||||
// Find the screen-specific panic handler first to
|
||||
// give it priority
|
||||
for (const descriptor &d : bc.panics()) {
|
||||
if (bits::has(d.flags, desc_flags::graphical)) {
|
||||
if (d.flags.get(desc_flags::graphical)) {
|
||||
panic = loader::load_file(disk, d.path);
|
||||
has_panic = true;
|
||||
break;
|
||||
@@ -106,7 +105,7 @@ load_resources(
|
||||
|
||||
if (!has_panic) {
|
||||
for (const descriptor &d : bc.panics()) {
|
||||
if (!bits::has(d.flags, desc_flags::graphical)) {
|
||||
if (!d.flags.get(desc_flags::graphical)) {
|
||||
panic = loader::load_file(disk, d.path);
|
||||
has_panic = true;
|
||||
break;
|
||||
|
||||
@@ -71,21 +71,19 @@ lapic::get_id()
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi(ipi mode, isr vector, uint8_t dest)
|
||||
lapic::send_ipi(util::bitset32 mode, isr vector, uint8_t dest)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode);
|
||||
uint32_t command = util::bitset32::from(vector) | mode;
|
||||
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi_broadcast(ipi mode, bool self, isr vector)
|
||||
lapic::send_ipi_broadcast(util::bitset32 mode, bool self, isr vector)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// Classes to control both local and I/O APICs.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
#include "interrupts.h"
|
||||
|
||||
@@ -33,33 +33,30 @@ public:
|
||||
/// Get the local APIC's ID
|
||||
uint8_t get_id();
|
||||
|
||||
enum class ipi : uint32_t
|
||||
enum class ipi_flags
|
||||
{
|
||||
// Delivery modes
|
||||
fixed = 0x0000,
|
||||
smi = 0x0200,
|
||||
nmi = 0x0400,
|
||||
init = 0x0500,
|
||||
startup = 0x0600,
|
||||
|
||||
// Flags
|
||||
deassert = 0x0000,
|
||||
assert = 0x4000,
|
||||
edge = 0x0000, ///< edge-triggered
|
||||
level = 0x8000, ///< level-triggered
|
||||
logical = 11,
|
||||
pending = 12,
|
||||
assert = 14,
|
||||
level = 15,
|
||||
};
|
||||
|
||||
// IPI flags based on delivery mode (bits 8-10)
|
||||
static constexpr util::bitset32 ipi_fixed = 0;
|
||||
static constexpr util::bitset32 ipi_init = 0x500;
|
||||
static constexpr util::bitset32 ipi_sipi = 0x600;
|
||||
|
||||
/// Send an inter-processor interrupt.
|
||||
/// \arg mode The sending mode
|
||||
/// \arg vector The interrupt vector
|
||||
/// \arg dest The APIC ID of the destination
|
||||
void send_ipi(ipi mode, isr vector, uint8_t dest);
|
||||
void send_ipi(util::bitset32 mode, isr vector, uint8_t dest);
|
||||
|
||||
/// Send an inter-processor broadcast interrupt to all other CPUs
|
||||
/// \arg mode The sending mode
|
||||
/// \arg self If true, include this CPU in the broadcast
|
||||
/// \arg vector The interrupt vector
|
||||
void send_ipi_broadcast(ipi mode, bool self, isr vector);
|
||||
void send_ipi_broadcast(util::bitset32 mode, bool self, isr vector);
|
||||
|
||||
/// Wait for an IPI to finish sending. This is done automatically
|
||||
/// before sending another IPI with send_ipi().
|
||||
@@ -145,6 +142,3 @@ private:
|
||||
uint8_t m_id;
|
||||
uint8_t m_version;
|
||||
};
|
||||
|
||||
is_bitfield(lapic::ipi);
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ cpu_early_init(cpu_data *cpu)
|
||||
set_xcr0(xcr0_val);
|
||||
|
||||
// Set initial floating point state
|
||||
const util::bitset32 mxcsr_val {
|
||||
const util::bitset32 mxcsr_val = util::bitset32::of(
|
||||
mxcsr::DAZ,
|
||||
mxcsr::IM,
|
||||
mxcsr::DM,
|
||||
@@ -126,8 +126,7 @@ cpu_early_init(cpu_data *cpu)
|
||||
mxcsr::OM,
|
||||
mxcsr::UM,
|
||||
mxcsr::PM,
|
||||
mxcsr::FTZ,
|
||||
};
|
||||
mxcsr::FTZ);
|
||||
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
|
||||
|
||||
// Install the GS base pointint to the cpu_data
|
||||
|
||||
@@ -44,7 +44,7 @@ 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 ",
|
||||
size_t dlen = util::format({buffer, sizeof(buffer)}, "\e[38;5;%dm%7s %7s\e[38;5;14m\u2502\e[38;5;%dm ",
|
||||
level_colors[entry->severity],
|
||||
area_names[entry->area],
|
||||
level_names[entry->severity],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stdint.h>
|
||||
#include <util/misc.h> // for checksum
|
||||
#include <util/pointers.h>
|
||||
#include <arch/acpi/tables.h>
|
||||
#include <acpi/tables.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "apic.h"
|
||||
|
||||
@@ -22,6 +22,9 @@ static constexpr uint8_t tss_index = 6; // Note that this takes TWO GDT en
|
||||
static util::no_construct<GDT> __g_bsp_gdt_storage;
|
||||
GDT &g_bsp_gdt = __g_bsp_gdt_storage.value;
|
||||
|
||||
static constexpr util::bitset8 ring3 = util::bitset8::of( GDT::type::ring1, GDT::type::ring2 );
|
||||
static constexpr util::bitset8 rw = util::bitset8::of( GDT::type::read_write );
|
||||
static constexpr util::bitset8 rwx = util::bitset8::of( GDT::type::read_write, GDT::type::execute );
|
||||
|
||||
GDT::GDT(TSS *tss) :
|
||||
m_tss(tss)
|
||||
@@ -32,13 +35,13 @@ GDT::GDT(TSS *tss) :
|
||||
m_ptr.base = &m_entries[0];
|
||||
|
||||
// Kernel CS/SS - always 64bit
|
||||
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
||||
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
||||
set(kern_cs_index, 0, 0xfffff, true, rwx);
|
||||
set(kern_ss_index, 0, 0xfffff, true, rw);
|
||||
|
||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
||||
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
||||
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
||||
set(user_cs32_index, 0, 0xfffff, false, ring3 | rwx);
|
||||
set(user_ss_index, 0, 0xfffff, true, ring3 | rw);
|
||||
set(user_cs64_index, 0, 0xfffff, true, ring3 | rwx);
|
||||
|
||||
set_tss(tss);
|
||||
}
|
||||
@@ -61,7 +64,7 @@ GDT::install() const
|
||||
}
|
||||
|
||||
void
|
||||
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
|
||||
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, util::bitset8 t)
|
||||
{
|
||||
m_entries[i].limit_low = limit & 0xffff;
|
||||
m_entries[i].size = (limit >> 16) & 0xf;
|
||||
@@ -71,7 +74,9 @@ GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
|
||||
m_entries[i].base_mid = (base >> 16) & 0xff;
|
||||
m_entries[i].base_high = (base >> 24) & 0xff;
|
||||
|
||||
m_entries[i].type = t | type::system | type::present;
|
||||
static constexpr util::bitset8 sp = util::bitset8::of( type::system, type::present );
|
||||
m_entries[i].type = t;
|
||||
m_entries[i].type |= sp;
|
||||
}
|
||||
|
||||
struct tss_descriptor
|
||||
@@ -79,7 +84,7 @@ struct tss_descriptor
|
||||
uint16_t limit_low;
|
||||
uint16_t base_00;
|
||||
uint8_t base_16;
|
||||
GDT::type type;
|
||||
util::bitset8 type;
|
||||
uint8_t size;
|
||||
uint8_t base_24;
|
||||
uint32_t base_32;
|
||||
@@ -102,11 +107,9 @@ GDT::set_tss(TSS *tss)
|
||||
tssd.base_32 = (base >> 32) & 0xffffffff;
|
||||
tssd.reserved = 0;
|
||||
|
||||
tssd.type =
|
||||
type::accessed |
|
||||
type::execute |
|
||||
type::ring3 |
|
||||
type::present;
|
||||
static constexpr util::bitset8 tss_mark =
|
||||
util::bitset8::of(type::accessed, type::execute, type::present);
|
||||
tssd.type = ring3 | tss_mark;
|
||||
|
||||
memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// Definitions relating to a CPU's GDT table
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
class TSS;
|
||||
|
||||
@@ -25,21 +25,20 @@ public:
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
|
||||
enum class type : uint8_t
|
||||
enum class type
|
||||
{
|
||||
accessed = 0x01,
|
||||
read_write = 0x02,
|
||||
conforming = 0x04,
|
||||
execute = 0x08,
|
||||
system = 0x10,
|
||||
ring1 = 0x20,
|
||||
ring2 = 0x40,
|
||||
ring3 = 0x60,
|
||||
present = 0x80
|
||||
accessed,
|
||||
read_write,
|
||||
conforming,
|
||||
execute,
|
||||
system,
|
||||
ring1,
|
||||
ring2,
|
||||
present
|
||||
};
|
||||
|
||||
private:
|
||||
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
|
||||
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, util::bitset8 t);
|
||||
void set_tss(TSS *tss);
|
||||
|
||||
struct descriptor
|
||||
@@ -47,7 +46,7 @@ private:
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
type type;
|
||||
util::bitset8 type;
|
||||
uint8_t size;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed, align(8)));
|
||||
@@ -63,5 +62,3 @@ private:
|
||||
|
||||
ptr m_ptr;
|
||||
};
|
||||
|
||||
is_bitfield(GDT::type);
|
||||
|
||||
@@ -117,4 +117,5 @@ ISR (0xe1, 0, isrLINT0)
|
||||
ISR (0xe2, 0, isrLINT1)
|
||||
ISR (0xe3, 0, isrAPICError)
|
||||
ISR (0xe4, 0, ipiSchedule)
|
||||
ISR (0xe5, 0, ipiShootdown)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + mem::linear_offset;
|
||||
extern "C" {
|
||||
void isr_handler(cpu_state*);
|
||||
void irq_handler(cpu_state*);
|
||||
void _reload_cr3();
|
||||
}
|
||||
|
||||
uint8_t
|
||||
@@ -123,16 +124,18 @@ isr_handler(cpu_state *regs)
|
||||
uintptr_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
bool user = cr2 < mem::kernel_offset;
|
||||
vm_space::fault_type ft =
|
||||
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||
// The zero page is always invalid
|
||||
if (cr2 > mem::frame_size) {
|
||||
bool user = cr2 < mem::kernel_offset;
|
||||
util::bitset8 ft = regs->errorcode;
|
||||
|
||||
vm_space &space = user
|
||||
? obj::process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
vm_space &space = user
|
||||
? obj::process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
}
|
||||
|
||||
util::format({message, sizeof(message)},
|
||||
"Page fault: %016lx%s%s%s%s%s", cr2,
|
||||
@@ -185,6 +188,11 @@ isr_handler(cpu_state *regs)
|
||||
scheduler::get().schedule();
|
||||
break;
|
||||
|
||||
case isr::ipiShootdown:
|
||||
// TODO: Real shootdown algorithm
|
||||
_reload_cr3();
|
||||
break;
|
||||
|
||||
default:
|
||||
util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt);
|
||||
kassert(false, message, regs);
|
||||
|
||||
@@ -1,49 +1,142 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "ipc_message.h"
|
||||
#include "j6/types.h"
|
||||
|
||||
namespace ipc {
|
||||
|
||||
message::message() : tag {0}, data {nullptr, 0}, handles {nullptr, 0} {}
|
||||
message::message() : tag {0}, data_size {0}, handle_count {0}, out_of_band {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}
|
||||
out_of_band {0}
|
||||
{
|
||||
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));
|
||||
}
|
||||
set(in_tag, in_data, in_handles);
|
||||
}
|
||||
|
||||
|
||||
message::message(message &&other) {
|
||||
*this = util::move(other);
|
||||
}
|
||||
|
||||
message::message(message &&other) { *this = util::move(other); }
|
||||
|
||||
message::~message()
|
||||
{
|
||||
delete [] reinterpret_cast<uint8_t*>(data.pointer);
|
||||
delete [] handles.pointer;
|
||||
clear_oob();
|
||||
}
|
||||
|
||||
|
||||
util::buffer
|
||||
message::data()
|
||||
{
|
||||
uint8_t *buf = content + (handle_count * sizeof(j6_handle_t));
|
||||
if (out_of_band)
|
||||
buf = reinterpret_cast<uint8_t**>(content)[handle_count];
|
||||
|
||||
return {
|
||||
.pointer = buf,
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::const_buffer
|
||||
message::data() const
|
||||
{
|
||||
const uint8_t *buf = content + (handle_count * sizeof(j6_handle_t));
|
||||
if (out_of_band)
|
||||
buf = reinterpret_cast<uint8_t *const *const>(content)[handle_count];
|
||||
|
||||
return {
|
||||
.pointer = buf,
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::counted<j6_handle_t>
|
||||
message::handles()
|
||||
{
|
||||
return {
|
||||
.pointer = reinterpret_cast<j6_handle_t*>(content),
|
||||
.count = handle_count,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
util::counted<const j6_handle_t>
|
||||
message::handles() const
|
||||
{
|
||||
return {
|
||||
.pointer = reinterpret_cast<const j6_handle_t*>(content),
|
||||
.count = data_size,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
message &
|
||||
message::operator=(message &&other)
|
||||
{
|
||||
clear_oob();
|
||||
|
||||
tag = other.tag;
|
||||
other.tag = 0;
|
||||
|
||||
data = other.data;
|
||||
other.data = {nullptr, 0};
|
||||
data_size = other.data_size;
|
||||
other.data_size = 0;
|
||||
|
||||
handles = other.handles;
|
||||
other.handles = {nullptr, 0};
|
||||
handle_count = other.handle_count;
|
||||
other.handle_count = 0;
|
||||
|
||||
out_of_band = other.out_of_band;
|
||||
other.out_of_band = 0;
|
||||
|
||||
memcpy(content, other.content, sizeof(content));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
void
|
||||
message::set(
|
||||
uint64_t in_tag,
|
||||
const util::buffer &in_data,
|
||||
const util::counted<j6_handle_t> &in_handles)
|
||||
{
|
||||
clear_oob();
|
||||
tag = in_tag;
|
||||
handle_count = in_handles.count;
|
||||
data_size = in_data.count;
|
||||
|
||||
if (in_handles.count) {
|
||||
kassert(in_handles.count < (sizeof(content) / sizeof(j6_handle_t)) - sizeof(void*));
|
||||
util::counted<j6_handle_t> handlebuf = handles();
|
||||
memcpy(handlebuf.pointer, in_handles.pointer, handlebuf.count * sizeof(j6_handle_t));
|
||||
}
|
||||
|
||||
if (in_data.count) {
|
||||
if (in_data.count > sizeof(content) - (handle_count * sizeof(j6_handle_t))) {
|
||||
out_of_band = 1;
|
||||
auto *buf = new uint8_t [in_data.count];
|
||||
reinterpret_cast<uint8_t**>(content)[handle_count] = buf;
|
||||
}
|
||||
util::buffer databuf = data();
|
||||
memcpy(databuf.pointer, in_data.pointer, databuf.count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
message::clear_oob()
|
||||
{
|
||||
if (out_of_band) {
|
||||
uint8_t *buf = reinterpret_cast<uint8_t**>(content)[handle_count];
|
||||
delete [] buf;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
@@ -5,14 +5,29 @@
|
||||
#include <stdint.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
static constexpr size_t message_size = 64;
|
||||
|
||||
struct message
|
||||
{
|
||||
uint64_t tag;
|
||||
util::buffer data;
|
||||
util::counted<j6_handle_t> handles;
|
||||
uint16_t data_size;
|
||||
|
||||
uint16_t handle_count : 4;
|
||||
uint16_t out_of_band : 1;
|
||||
|
||||
uint32_t _reserved;
|
||||
|
||||
uint8_t content[ message_size - 8 ];
|
||||
|
||||
util::buffer data();
|
||||
util::const_buffer data() const;
|
||||
|
||||
util::counted<j6_handle_t> handles();
|
||||
util::counted<const j6_handle_t> handles() const;
|
||||
|
||||
message();
|
||||
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
|
||||
@@ -20,6 +35,13 @@ struct message
|
||||
~message();
|
||||
|
||||
message & operator=(message &&other);
|
||||
|
||||
void set(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
|
||||
|
||||
private:
|
||||
void clear_oob();
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
using message_ptr = util::unique_ptr<message>;
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
kernel = module("kernel",
|
||||
default = True,
|
||||
basename = "jsix",
|
||||
targets = [ "kernel" ],
|
||||
target = "kernel",
|
||||
description = "jsix kernel",
|
||||
deps = [ "util", "cpu", "bootproto", "j6" ],
|
||||
deps = [ "util", "cpu", "bootproto", "j6", "acpi" ],
|
||||
static = True,
|
||||
ld_script = "kernel.ld",
|
||||
skip_arches = [ "linux" ],
|
||||
sources = [
|
||||
"apic.cpp",
|
||||
"kassert.cpp",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
@@ -54,7 +53,7 @@ kernel_main(bootproto::args *args)
|
||||
bsp_late_init();
|
||||
|
||||
using bootproto::boot_flags;
|
||||
bool enable_test = util::bits::has(args->flags, boot_flags::test);
|
||||
bool enable_test = args->flags.get(boot_flags::test);
|
||||
syscall_initialize(enable_test);
|
||||
|
||||
device_manager &devices = device_manager::get();
|
||||
@@ -87,7 +86,7 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
|
||||
using bootproto::section_flags;
|
||||
using obj::vm_flags;
|
||||
|
||||
obj::process *p = new obj::process;
|
||||
obj::process *p = new obj::process {"srv.init"};
|
||||
|
||||
j6_handle_t sys_handle =
|
||||
g_cap_table.create(&obj::system::get(), obj::system::init_caps);
|
||||
@@ -95,12 +94,15 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
|
||||
|
||||
vm_space &space = p->space();
|
||||
for (const auto § : program.sections) {
|
||||
vm_flags flags =
|
||||
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
util::bitset32 flags = util::bitset32::of(vm_flags::exact);
|
||||
if (sect.type.get(section_flags::execute))
|
||||
flags.set(vm_flags::exec);
|
||||
|
||||
if (sect.type.get(section_flags::write))
|
||||
flags.set(vm_flags::write);
|
||||
|
||||
obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags);
|
||||
space.add(sect.virt_addr, vma, obj::vm_flags::exact);
|
||||
space.add(sect.virt_addr, vma, flags);
|
||||
}
|
||||
|
||||
uint64_t iopl = (3ull << 12);
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "kassert.h"
|
||||
#include "logger.h"
|
||||
#include "objects/system.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
// The logger is initialized _before_ global constructors are called,
|
||||
// so that we can start log output immediately. Keep its constructor
|
||||
@@ -124,7 +122,8 @@ LOG_LEVEL_FUNCTION(info);
|
||||
LOG_LEVEL_FUNCTION(warn);
|
||||
LOG_LEVEL_FUNCTION(error);
|
||||
|
||||
void fatal(logs area, const char *fmt, ...)
|
||||
void
|
||||
fatal(logs area, const char *fmt, ...)
|
||||
{
|
||||
logger *l = logger::s_log;
|
||||
if (!l) return;
|
||||
@@ -137,4 +136,19 @@ void fatal(logs area, const char *fmt, ...)
|
||||
kassert(false, "log::fatal");
|
||||
}
|
||||
|
||||
void
|
||||
log(logs area, level severity, const char *fmt, ...)
|
||||
{
|
||||
logger *l = logger::s_log;
|
||||
if (!l) return;
|
||||
|
||||
level limit = l->get_level(area);
|
||||
if (severity > limit) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
l->output(severity, area, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "objects/event.h"
|
||||
#include "wait_queue.h"
|
||||
|
||||
enum class logs : uint8_t {
|
||||
#define LOG(name, lvl) name,
|
||||
@@ -77,6 +78,7 @@ private:
|
||||
friend void warn (logs area, const char *fmt, ...);
|
||||
friend void error (logs area, const char *fmt, ...);
|
||||
friend void fatal (logs area, const char *fmt, ...);
|
||||
friend void log (logs area, level severity, const char *fmt, ...);
|
||||
|
||||
void output(level severity, logs area, const char *fmt, va_list args);
|
||||
|
||||
@@ -113,6 +115,8 @@ void warn (logs area, const char *fmt, ...);
|
||||
void error (logs area, const char *fmt, ...);
|
||||
void fatal (logs area, const char *fmt, ...);
|
||||
|
||||
void log (logs area, level severity, const char *fmt, ...);
|
||||
|
||||
} // namespace log
|
||||
|
||||
extern log::logger &g_logger;
|
||||
|
||||
@@ -20,7 +20,8 @@ extern "C" {
|
||||
|
||||
using bootproto::allocation_register;
|
||||
|
||||
using obj::vm_flags;
|
||||
inline constexpr util::bitset32 vm_flag_write = util::bitset32::of(obj::vm_flags::write);
|
||||
inline constexpr util::bitset32 vm_flag_exact = util::bitset32::of(obj::vm_flags::exact);
|
||||
|
||||
// These objects are initialized _before_ global constructors are called,
|
||||
// so we don't want them to have global constructors at all, lest they
|
||||
@@ -53,7 +54,7 @@ obj::vm_area_guarded g_kernel_buffers {
|
||||
mem::buffers_offset,
|
||||
mem::kernel_buffer_pages,
|
||||
mem::buffers_size,
|
||||
vm_flags::write};
|
||||
vm_flag_write};
|
||||
|
||||
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
||||
@@ -97,30 +98,30 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
|
||||
// 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_untracked(mem::heap_size, vm_flag_write);
|
||||
|
||||
obj::vm_area *heap_map = new (&g_kernel_heapmap_area)
|
||||
obj::vm_area_untracked(mem::heapmap_size, vm_flags::write);
|
||||
obj::vm_area_untracked(mem::heapmap_size, vm_flag_write);
|
||||
|
||||
vm.add(mem::heap_offset, heap, vm_flags::exact);
|
||||
vm.add(mem::heapmap_offset, heap_map, vm_flags::exact);
|
||||
vm.add(mem::heap_offset, heap, vm_flag_exact);
|
||||
vm.add(mem::heapmap_offset, heap_map, vm_flag_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);
|
||||
obj::vm_area_ring(log_buffer_size, vm_flag_write);
|
||||
vm.add(mem::logs_offset, logs, vm_flag_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);
|
||||
obj::vm_area_untracked(mem::caps_size, vm_flag_write);
|
||||
|
||||
vm.add(mem::caps_offset, caps, vm_flags::exact);
|
||||
vm.add(mem::caps_offset, caps, vm_flag_exact);
|
||||
|
||||
new (&g_cap_table) cap_table {mem::caps_offset};
|
||||
|
||||
@@ -128,8 +129,8 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
mem::stacks_offset,
|
||||
mem::kernel_stack_pages,
|
||||
mem::stacks_size,
|
||||
vm_flags::write};
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flags::exact);
|
||||
vm_flag_write};
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flag_exact);
|
||||
|
||||
// Clean out any remaning bootloader page table entries
|
||||
for (unsigned i = 0; i < arch::kernel_root_index; ++i)
|
||||
@@ -140,7 +141,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_flags::exact);
|
||||
vm.add(mem::buffers_offset, &g_kernel_buffers, vm_flag_exact);
|
||||
|
||||
g_frame_allocator.free(
|
||||
get_physical_page(kargs.page_tables.pointer),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <util/counted.h>
|
||||
#include <j6/memutils.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "objects/mailbox.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
@@ -26,6 +27,8 @@ mailbox::close()
|
||||
bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL);
|
||||
if (was_closed) return;
|
||||
|
||||
log::spam(logs::ipc, "mbx[%2x] closing...", obj_id());
|
||||
|
||||
m_callers.clear(j6_status_closed);
|
||||
m_responders.clear(j6_status_closed);
|
||||
|
||||
@@ -44,14 +47,20 @@ mailbox::call()
|
||||
m_callers.add_thread(¤t);
|
||||
|
||||
thread *responder = m_responders.pop_next();
|
||||
if (responder)
|
||||
if (responder) {
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] call() waking thread[%2x]...",
|
||||
current.obj_id(), obj_id(), responder->obj_id());
|
||||
responder->wake(j6_status_ok);
|
||||
} else {
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] call() found no responder yet.",
|
||||
current.obj_id(), obj_id());
|
||||
}
|
||||
|
||||
return current.block();
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
mailbox::receive(ipc::message_ptr &data, reply_tag_t &reply_tag, bool block)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
@@ -67,6 +76,9 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] receive() blocking waiting for a caller",
|
||||
current.obj_id(), obj_id());
|
||||
|
||||
m_responders.add_thread(¤t);
|
||||
j6_status_t s = current.block();
|
||||
if (s != j6_status_ok)
|
||||
@@ -78,12 +90,15 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
m_reply_map.insert({ reply_tag, caller });
|
||||
lock.release();
|
||||
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] receive() found caller thread[%2x], rt = %x",
|
||||
current.obj_id(), obj_id(), caller->obj_id(), reply_tag);
|
||||
|
||||
data = caller->get_message_data();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
|
||||
mailbox::reply(reply_tag_t reply_tag, ipc::message_ptr data)
|
||||
{
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
@@ -97,6 +112,10 @@ mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
|
||||
m_reply_map.erase(reply_tag);
|
||||
lock.release();
|
||||
|
||||
thread ¤t = thread::current();
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] reply() to caller thread[%2x], rt = %x",
|
||||
current.obj_id(), obj_id(), caller->obj_id(), reply_tag);
|
||||
|
||||
caller->set_message_data(util::move(data));
|
||||
caller->wake(j6_status_ok);
|
||||
return j6_status_ok;
|
||||
|
||||
@@ -22,7 +22,6 @@ class mailbox :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
|
||||
using reply_tag_t = uint64_t;
|
||||
|
||||
/// Capabilities on a newly constructed mailbox handle
|
||||
@@ -48,18 +47,18 @@ public:
|
||||
j6_status_t call();
|
||||
|
||||
/// Receive the next available message, optionally blocking if no messages are available.
|
||||
/// \arg data [out] an ipc::message structure to fill
|
||||
/// \arg data [out] the message
|
||||
/// \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(ipc::message &data, reply_tag_t &reply_tag, bool block);
|
||||
j6_status_t receive(ipc::message_ptr &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 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, ipc::message &&data);
|
||||
j6_status_t reply(reply_tag_t reply_tag, ipc::message_ptr data);
|
||||
|
||||
private:
|
||||
wait_queue m_callers;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "kassert.h"
|
||||
@@ -18,10 +19,18 @@ obj::process &g_kernel_process = __g_kernel_process_storage.value;
|
||||
|
||||
namespace obj {
|
||||
|
||||
process::process() :
|
||||
process::process(const char *name) :
|
||||
kobject {kobject::type::process},
|
||||
m_state {state::running}
|
||||
{
|
||||
if constexpr(__use_process_names) {
|
||||
memset(m_name, 0, sizeof(m_name));
|
||||
if (name) {
|
||||
static constexpr size_t charlen = sizeof(m_name) - 1;
|
||||
for (size_t i = 0; i < charlen && name[i]; ++i)
|
||||
m_name[i] = name[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "kernel process"-only constructor
|
||||
@@ -30,6 +39,11 @@ process::process(page_table *kpml4) :
|
||||
m_space {kpml4},
|
||||
m_state {state::running}
|
||||
{
|
||||
if constexpr(__use_process_names) {
|
||||
static constexpr char kernel[] = "KERNEL";
|
||||
memcpy(m_name, kernel, sizeof(kernel));
|
||||
memset(m_name + sizeof(kernel), 0, sizeof(m_name) - sizeof(kernel));
|
||||
}
|
||||
}
|
||||
|
||||
process::~process()
|
||||
@@ -46,7 +60,7 @@ process::create_kernel_process(page_table *pml4)
|
||||
}
|
||||
|
||||
void
|
||||
process::exit(int32_t code)
|
||||
process::exit(int64_t code)
|
||||
{
|
||||
if (m_state == state::exited)
|
||||
return;
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
namespace obj {
|
||||
|
||||
static constexpr bool __use_process_names =
|
||||
#ifdef __jsix_config_debug
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
|
||||
class process :
|
||||
public kobject
|
||||
{
|
||||
@@ -32,7 +40,7 @@ public:
|
||||
static constexpr kobject::type type = kobject::type::process;
|
||||
|
||||
/// Constructor.
|
||||
process();
|
||||
process(const char *name);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~process();
|
||||
@@ -42,11 +50,14 @@ public:
|
||||
|
||||
/// Terminate this process.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
void exit(int64_t code);
|
||||
|
||||
/// Get the process' virtual memory space
|
||||
vm_space & space() { return m_space; }
|
||||
|
||||
/// Get the debugging name of the process
|
||||
const char *name() { if constexpr(__use_process_names) return m_name; else return nullptr; }
|
||||
|
||||
/// Create a new thread in this process
|
||||
/// \args rsp3 If non-zero, sets the ring3 stack pointer to this value
|
||||
/// \args priority The new thread's scheduling priority
|
||||
@@ -95,7 +106,7 @@ private:
|
||||
// This constructor is called by create_kernel_process
|
||||
process(page_table *kpml4);
|
||||
|
||||
int32_t m_return_code;
|
||||
int64_t m_return_code;
|
||||
|
||||
vm_space m_space;
|
||||
|
||||
@@ -107,6 +118,15 @@ private:
|
||||
|
||||
enum class state : uint8_t { running, exited };
|
||||
state m_state;
|
||||
|
||||
static constexpr size_t max_name_len =
|
||||
#ifdef __jsix_config_debug
|
||||
32;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
char m_name[max_name_len];
|
||||
};
|
||||
|
||||
} // namespace obj
|
||||
|
||||
@@ -111,9 +111,6 @@ 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()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/// Definition of thread kobject types
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/linked_list.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
@@ -122,8 +121,8 @@ public:
|
||||
/// \returns The clock time at which to wake. 0 for no timeout.
|
||||
inline uint64_t wake_timeout() const { return m_wake_timeout; }
|
||||
|
||||
void set_message_data(ipc::message &&md);
|
||||
ipc::message && get_message_data();
|
||||
inline void set_message_data(ipc::message_ptr md) { m_message = util::move(md); }
|
||||
inline ipc::message_ptr get_message_data() { return util::move(m_message); }
|
||||
|
||||
inline bool has_state(state s) const {
|
||||
return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) &
|
||||
@@ -197,7 +196,7 @@ private:
|
||||
uint64_t m_wake_value;
|
||||
uint64_t m_wake_timeout;
|
||||
|
||||
ipc::message m_message;
|
||||
ipc::message_ptr m_message;
|
||||
|
||||
wait_queue m_join_queue;
|
||||
};
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace obj {
|
||||
|
||||
using mem::frame_size;
|
||||
|
||||
vm_area::vm_area(size_t size, vm_flags flags) :
|
||||
m_size {size},
|
||||
vm_area::vm_area(size_t size, util::bitset32 flags) :
|
||||
m_size {mem::page_count(size) * mem::frame_size},
|
||||
m_flags {flags},
|
||||
m_spaces {m_vector_static, 0, static_size},
|
||||
kobject {kobject::type::vma}
|
||||
@@ -34,6 +34,10 @@ void
|
||||
vm_area::remove_from(vm_space *space)
|
||||
{
|
||||
m_spaces.remove_swap(space);
|
||||
|
||||
// If we were keeping this space around after its refcount
|
||||
// dropped to zero because it was mapped, check if we should
|
||||
// clean it up now.
|
||||
if (!m_spaces.count() && !handle_count())
|
||||
delete this;
|
||||
}
|
||||
@@ -62,7 +66,7 @@ vm_area::can_resize(size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
||||
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, util::bitset32 flags) :
|
||||
m_start {start},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
@@ -70,7 +74,7 @@ vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
||||
|
||||
vm_area_fixed::~vm_area_fixed()
|
||||
{
|
||||
if (m_flags && vm_flags::mmio)
|
||||
if (m_flags.get(vm_flags::mmio))
|
||||
return;
|
||||
|
||||
size_t pages = mem::page_count(m_size);
|
||||
@@ -94,7 +98,7 @@ vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
return true;
|
||||
}
|
||||
|
||||
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
||||
vm_area_untracked::vm_area_untracked(size_t size, util::bitset32 flags) :
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
@@ -127,7 +131,7 @@ vm_area_untracked::add_to(vm_space *space)
|
||||
}
|
||||
|
||||
|
||||
vm_area_open::vm_area_open(size_t size, vm_flags flags) :
|
||||
vm_area_open::vm_area_open(size_t size, util::bitset32 flags) :
|
||||
m_mapped {nullptr},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
@@ -154,7 +158,7 @@ vm_area_open::add_existing(uintptr_t offset, uintptr_t phys)
|
||||
}
|
||||
|
||||
|
||||
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, vm_flags flags) :
|
||||
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, util::bitset32 flags) :
|
||||
m_pages {buf_pages + 1}, // Sections are N+1 pages for the leading guard page
|
||||
m_stacks {start, m_pages*mem::frame_size},
|
||||
vm_area_open {size, flags}
|
||||
@@ -191,7 +195,7 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
return vm_area_open::get_page(offset, phys, alloc);
|
||||
}
|
||||
|
||||
vm_area_ring::vm_area_ring(size_t size, vm_flags flags) :
|
||||
vm_area_ring::vm_area_ring(size_t size, util::bitset32 flags) :
|
||||
vm_area_open {size * 2, flags},
|
||||
m_bufsize {size}
|
||||
{
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/vector.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
|
||||
#include "block_allocator.h"
|
||||
#include "objects/kobject.h"
|
||||
@@ -17,16 +17,15 @@ class vm_space;
|
||||
|
||||
namespace obj {
|
||||
|
||||
enum class vm_flags : uint32_t
|
||||
enum class vm_flags
|
||||
{
|
||||
#define VM_FLAG(name, v) name = v,
|
||||
#include <j6/tables/vm_flags.inc>
|
||||
#undef VM_FLAG
|
||||
driver_mask = 0x00ff'ffff, ///< flags allowed via syscall for drivers
|
||||
user_mask = 0x000f'ffff, ///< flags allowed via syscall for non-drivers
|
||||
};
|
||||
is_bitfield(vm_flags);
|
||||
|
||||
inline constexpr util::bitset32 vm_driver_mask = 0x00ff'ffff; ///< flags allowed via syscall for drivers
|
||||
inline constexpr util::bitset32 vm_user_mask = 0x000f'ffff; ///< flags allowed via syscall for non-drivers
|
||||
|
||||
/// Virtual memory areas allow control over memory allocation
|
||||
class vm_area :
|
||||
@@ -40,7 +39,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||
vm_area(size_t size, util::bitset32 flags = 0);
|
||||
|
||||
virtual ~vm_area();
|
||||
|
||||
@@ -48,7 +47,7 @@ public:
|
||||
inline size_t size() const { return m_size; }
|
||||
|
||||
/// Get the flags set for this area
|
||||
inline vm_flags flags() const { return m_flags; }
|
||||
inline util::bitset32 flags() const { return m_flags; }
|
||||
|
||||
/// Track that this area was added to a vm_space
|
||||
/// \arg space The space to add this area to
|
||||
@@ -83,7 +82,7 @@ protected:
|
||||
bool can_resize(size_t size);
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
util::bitset32 m_flags;
|
||||
util::vector<vm_space*> m_spaces;
|
||||
|
||||
// Initial static space for m_spaces - most areas will never grow
|
||||
@@ -103,7 +102,7 @@ public:
|
||||
/// \arg start Starting physical address of this area
|
||||
/// \arg size Size of the physical memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_fixed(uintptr_t start, size_t size, vm_flags flags = vm_flags::none);
|
||||
vm_area_fixed(uintptr_t start, size_t size, util::bitset32 flags = 0);
|
||||
virtual ~vm_area_fixed();
|
||||
|
||||
virtual size_t resize(size_t size) override;
|
||||
@@ -122,7 +121,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_open(size_t size, vm_flags flags);
|
||||
vm_area_open(size_t size, util::bitset32 flags);
|
||||
virtual ~vm_area_open();
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
@@ -144,7 +143,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_untracked(size_t size, vm_flags flags);
|
||||
vm_area_untracked(size_t size, util::bitset32 flags);
|
||||
virtual ~vm_area_untracked();
|
||||
|
||||
virtual bool add_to(vm_space *space) override;
|
||||
@@ -166,7 +165,7 @@ public:
|
||||
uintptr_t start,
|
||||
size_t sec_pages,
|
||||
size_t size,
|
||||
vm_flags flags);
|
||||
util::bitset32 flags);
|
||||
|
||||
virtual ~vm_area_guarded();
|
||||
|
||||
@@ -194,7 +193,7 @@ public:
|
||||
/// \arg size Virtual size of the ring buffer. Note that
|
||||
/// the VMA size will be double this value.
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_ring(size_t size, vm_flags flags);
|
||||
vm_area_ring(size_t size, util::bitset32 flags);
|
||||
virtual ~vm_area_ring();
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
@@ -14,10 +14,7 @@ free_page_header * page_table::s_page_cache = nullptr;
|
||||
util::spinlock page_table::s_lock;
|
||||
constexpr size_t page_table::entry_sizes[4];
|
||||
|
||||
|
||||
constexpr page_table::flag table_flags =
|
||||
page_table::flag::present |
|
||||
page_table::flag::write;
|
||||
inline constexpr util::bitset64 table_flags = page_flags::present | page_flags::write;
|
||||
|
||||
|
||||
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) :
|
||||
@@ -140,9 +137,9 @@ page_table::iterator::ensure_table(level l)
|
||||
uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~linear_offset;
|
||||
|
||||
uint64_t &parent = entry(l - 1);
|
||||
flag flags = table_flags;
|
||||
util::bitset64 flags = table_flags;
|
||||
if (m_index[0] < arch::kernel_root_index)
|
||||
flags |= flag::user;
|
||||
flags.set(flag::user);
|
||||
|
||||
m_table[unsigned(l)] = table;
|
||||
parent = (phys & ~0xfffull) | flags;
|
||||
|
||||
@@ -4,11 +4,33 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/memory.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
struct free_page_header;
|
||||
|
||||
namespace page_flags {
|
||||
inline constexpr util::bitset64 none = 0x0000;
|
||||
inline constexpr util::bitset64 present = 0x0001; /// Entry is present in the table
|
||||
inline constexpr util::bitset64 write = 0x0002; /// Section may be written
|
||||
inline constexpr util::bitset64 user = 0x0004; /// User-accessible
|
||||
inline constexpr util::bitset64 pat0 = 0x0008; /// PAT selector bit 0
|
||||
inline constexpr util::bitset64 pat1 = 0x0010; /// PAT selector bit 1
|
||||
inline constexpr util::bitset64 accessed = 0x0020; /// Entry has been accessed
|
||||
inline constexpr util::bitset64 dirty = 0x0040; /// Page has been written to
|
||||
inline constexpr util::bitset64 page = 0x0080; /// Entry is a large page
|
||||
inline constexpr util::bitset64 pat2 = 0x0080; /// PAT selector bit 2 on PT entries
|
||||
inline constexpr util::bitset64 global = 0x0100; /// Entry is not PCID-specific
|
||||
inline constexpr util::bitset64 pat2_lg = 0x1000; /// PAT selector bit 2 on large/huge pages
|
||||
|
||||
inline constexpr util::bitset64 wb = 0;
|
||||
inline constexpr util::bitset64 wt = pat0;
|
||||
inline constexpr util::bitset64 uc_ = pat1;
|
||||
inline constexpr util::bitset64 uc = pat0 | pat1;
|
||||
inline constexpr util::bitset64 wc = pat0 | pat1 | pat2;
|
||||
inline constexpr util::bitset64 wc_lg = pat0 | pat1 | pat2_lg;
|
||||
} // page_flags
|
||||
|
||||
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||
struct page_table
|
||||
{
|
||||
@@ -16,27 +38,19 @@ struct page_table
|
||||
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
||||
|
||||
/// Page entry flags
|
||||
enum class flag : uint64_t
|
||||
enum class flag
|
||||
{
|
||||
none = 0x0000,
|
||||
present = 0x0001, /// Entry is present in the table
|
||||
write = 0x0002, /// Section may be written
|
||||
user = 0x0004, /// User-accessible
|
||||
pat0 = 0x0008, /// PAT selector bit 0
|
||||
pat1 = 0x0010, /// PAT selector bit 1
|
||||
accessed = 0x0020, /// Entry has been accessed
|
||||
dirty = 0x0040, /// Page has been written to
|
||||
page = 0x0080, /// Entry is a large page
|
||||
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
||||
global = 0x0100, /// Entry is not PCID-specific
|
||||
pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages
|
||||
|
||||
wb = none,
|
||||
wt = pat0,
|
||||
uc_ = pat1,
|
||||
uc = pat0 | pat1,
|
||||
wc = pat0 | pat1 | pat2,
|
||||
wc_lg = pat0 | pat1 | pat2_lg,
|
||||
present = 0, /// Entry is present in the table
|
||||
write = 1, /// Section may be written
|
||||
user = 2, /// User-accessible
|
||||
pat0 = 3, /// PAT selector bit 0
|
||||
pat1 = 4, /// PAT selector bit 1
|
||||
accessed = 5, /// Entry has been accessed
|
||||
dirty = 6, /// Page has been written to
|
||||
page = 7, /// Entry is a large page
|
||||
pat2 = 7, /// PAT selector bit 2 on PT entries
|
||||
global = 8, /// Entry is not PCID-specific
|
||||
pat2_lg = 12, /// PAT selector bit 2 on large/huge pages
|
||||
};
|
||||
|
||||
/// Helper for getting the next level value
|
||||
@@ -194,5 +208,3 @@ inline bool operator<(page_table::level a, page_table::level b) {
|
||||
|
||||
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
|
||||
inline page_table::level& operator--(page_table::level& l) { l = l - 1; return l; }
|
||||
|
||||
is_bitfield(page_table::flag);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "cpu.h"
|
||||
#include "display.h"
|
||||
#include "kernel.dir/memory.h"
|
||||
#include "objects/process.h"
|
||||
#include "objects/thread.h"
|
||||
#include "serial.h"
|
||||
@@ -9,6 +10,44 @@
|
||||
|
||||
namespace panicking {
|
||||
|
||||
template <typename T> inline bool
|
||||
check_pointer(T p)
|
||||
{
|
||||
static constexpr uint64_t large_flag = (1<<7);
|
||||
static constexpr uint64_t pointer_mask = 0x0000fffffffff000;
|
||||
static constexpr uintptr_t canon_mask = 0xffff800000000000;
|
||||
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
|
||||
|
||||
uintptr_t canon_bits = addr & canon_mask;
|
||||
if (canon_bits && canon_bits != canon_mask)
|
||||
return false;
|
||||
|
||||
uintptr_t pml4 = 0;
|
||||
asm volatile ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
pml4 += mem::linear_offset;
|
||||
|
||||
uint64_t *table = reinterpret_cast<uint64_t*>(pml4);
|
||||
unsigned shift = 39;
|
||||
|
||||
while (table) {
|
||||
unsigned index = (addr >> shift) & 0x1ffull;
|
||||
uint64_t entry = table[index];
|
||||
|
||||
if ((entry & 0x1) == 0)
|
||||
return false;
|
||||
|
||||
if ((entry & large_flag) || shift <= 12)
|
||||
return true;
|
||||
|
||||
uint64_t next = (entry & pointer_mask) + mem::linear_offset;
|
||||
table = reinterpret_cast<uint64_t*>(next);
|
||||
shift -= 9;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *clear = "\e[0m\n";
|
||||
|
||||
void
|
||||
@@ -40,23 +79,17 @@ print_cpu(serial_port &out, cpu_data &cpu)
|
||||
{
|
||||
uint32_t process = cpu.process ? cpu.process->obj_id() : 0;
|
||||
uint32_t thread = cpu.thread ? cpu.thread->obj_id() : 0;
|
||||
const char *name = cpu.process ? cpu.process->name() : "<Unknown>";
|
||||
|
||||
out.write("\n \e[0;31m==[ CPU: ");
|
||||
|
||||
char buffer[64];
|
||||
util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx>",
|
||||
cpu.id + 1, process, thread);
|
||||
size_t len = util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx> ]: %s ",
|
||||
cpu.id + 1, process, thread, name);
|
||||
out.write(buffer);
|
||||
|
||||
out.write(" ]=============================================================\n");
|
||||
}
|
||||
|
||||
template <typename T> inline bool
|
||||
canonical(T p)
|
||||
{
|
||||
static constexpr uintptr_t mask = 0xffff800000000000;
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
|
||||
return (addr & mask) == mask || (addr & mask) == 0;
|
||||
for (size_t i = 0; i < (74-len); ++i) out.write("=");
|
||||
out.write("\n");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -65,18 +98,26 @@ print_callstack(serial_port &out, symbol_table &syms, frame const *fp)
|
||||
char message[512];
|
||||
unsigned count = 0;
|
||||
|
||||
while (canonical(fp) && fp && fp->return_addr) {
|
||||
char const *name = syms.find_symbol(fp->return_addr);
|
||||
while (fp && check_pointer(fp)) {
|
||||
const uintptr_t ret = fp->return_addr;
|
||||
char const *name = ret ? syms.find_symbol(ret) : "<END>";
|
||||
if (!name)
|
||||
name = canonical(fp->return_addr) ? "<unknown>" : "<corrupt>";
|
||||
name = check_pointer(fp->return_addr) ? "<unknown>" : "<unmapped>";
|
||||
|
||||
util::format({message, sizeof(message)},
|
||||
" \e[0;33mframe %2d: <0x%016lx> \e[1;33m%s\n",
|
||||
count++, fp->return_addr, name);
|
||||
" \e[0;33mframe %2d: <0x%016lx> <0x%016lx> \e[1;33m%s\n",
|
||||
count++, fp, fp->return_addr, name);
|
||||
|
||||
out.write(message);
|
||||
|
||||
if (!ret) return;
|
||||
fp = fp->prev;
|
||||
}
|
||||
|
||||
const char *result = fp ? " <inaccessible>" : "";
|
||||
util::format({message, sizeof(message)}, " \e[0mfinal <0x%016lx>%s\n",
|
||||
fp, result);
|
||||
out.write(message);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# vim: ft=python
|
||||
|
||||
panic = module("panic.serial",
|
||||
targets = [ "kernel" ],
|
||||
target = "kernel",
|
||||
deps = [ "util", "elf", "kernel" ],
|
||||
static = True,
|
||||
includes = [ ".." ],
|
||||
description = "Serial panic handler",
|
||||
ld_script = "panic.serial.ld",
|
||||
skip_arches = [ "linux" ],
|
||||
sources = [
|
||||
"display.cpp",
|
||||
"entry.s",
|
||||
|
||||
@@ -339,5 +339,5 @@ scheduler::maybe_schedule(TCB *t)
|
||||
return;
|
||||
|
||||
current_cpu().apic->send_ipi(
|
||||
lapic::ipi::fixed, isr::ipiSchedule, cpu->id);
|
||||
lapic::ipi_fixed, isr::ipiSchedule, cpu->id);
|
||||
}
|
||||
|
||||
@@ -57,8 +57,11 @@ start(cpu_data &bsp, void *kpml4)
|
||||
// Copy the startup code somwhere the real mode trampoline can run
|
||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||
isr vector = static_cast<isr>(addr >> 12);
|
||||
obj::vm_area *vma = new obj::vm_area_fixed(addr, 0x1000, vm_flags::write);
|
||||
vm_space::kernel_space().add(addr, vma, obj::vm_flags::exact);
|
||||
|
||||
constexpr util::bitset32 flags = util::bitset32::of(vm_flags::write, vm_flags::exact);
|
||||
obj::vm_area *vma = new obj::vm_area_fixed(addr, 0x1000, flags);
|
||||
vm_space::kernel_space().add(addr, vma, flags);
|
||||
|
||||
memcpy(
|
||||
reinterpret_cast<void*>(addr),
|
||||
reinterpret_cast<void*>(&ap_startup),
|
||||
@@ -67,7 +70,7 @@ start(cpu_data &bsp, void *kpml4)
|
||||
size_t free_stack_count = 0;
|
||||
|
||||
lapic &apic = *bsp.apic;
|
||||
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
|
||||
util::bitset32 mode = lapic::ipi_init + lapic::ipi_flags::level + lapic::ipi_flags::assert;
|
||||
apic.send_ipi_broadcast(mode, false, static_cast<isr>(0));
|
||||
|
||||
for (uint8_t id : ids) {
|
||||
@@ -90,7 +93,7 @@ start(cpu_data &bsp, void *kpml4)
|
||||
size_t current_count = ap_startup_count;
|
||||
log::verbose(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
||||
|
||||
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
|
||||
util::bitset32 startup = lapic::ipi_sipi + lapic::ipi_flags::assert;
|
||||
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
|
||||
@@ -32,4 +32,13 @@ handle_clone(j6_handle_t orig, j6_handle_t *clone, uint32_t mask)
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
|
||||
j6_status_t
|
||||
handle_close(j6_handle_t hnd)
|
||||
{
|
||||
process &p = process::current();
|
||||
p.remove_handle(hnd);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -35,16 +35,17 @@ mailbox_call(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count)
|
||||
size_t *handles_count,
|
||||
size_t handles_size)
|
||||
{
|
||||
thread &cur = thread::current();
|
||||
|
||||
util::buffer data {in_data, data_in_len};
|
||||
util::buffer data {in_data, *data_len};
|
||||
util::counted<j6_handle_t> handles {in_handles, *handles_count};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message = new ipc::message {*tag, data, handles};
|
||||
cur.set_message_data(util::move(message));
|
||||
|
||||
j6_status_t s = self->call();
|
||||
@@ -52,19 +53,20 @@ mailbox_call(
|
||||
return s;
|
||||
|
||||
message = cur.get_message_data();
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
if (message->handle_count) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
memcpy(in_data, msg_data.pointer, *data_len);
|
||||
|
||||
size_t handles_min = *handles_count > message.handles.count ? message.handles.count : *handles_count;
|
||||
*handles_count = handles_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
@@ -75,18 +77,20 @@ mailbox_respond(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count,
|
||||
size_t handles_size,
|
||||
uint64_t *reply_tag,
|
||||
uint64_t flags)
|
||||
{
|
||||
util::buffer data {in_data, data_in_len};
|
||||
util::buffer data {in_data, *data_len};
|
||||
util::counted<j6_handle_t> handles {in_handles, *handles_count};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message;
|
||||
|
||||
if (*reply_tag) {
|
||||
message = new ipc::message {*tag, data, handles};
|
||||
j6_status_t s = self->reply(*reply_tag, util::move(message));
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -97,18 +101,20 @@ mailbox_respond(
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (msg_handles) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
memcpy(in_data, msg_data.pointer, *data_len);
|
||||
|
||||
size_t handles_min = *handles_count > message.handles.count ? message.handles.count : *handles_count;
|
||||
*handles_count = handles_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ using namespace obj;
|
||||
namespace syscalls {
|
||||
|
||||
j6_status_t
|
||||
process_create(j6_handle_t *self)
|
||||
process_create(j6_handle_t *self, const char *path)
|
||||
{
|
||||
process *p = construct_handle<process>(self);
|
||||
process *p = construct_handle<process>(self, path);
|
||||
log::info(logs::task, "Process <%02lx> created", p->obj_id());
|
||||
return j6_status_ok;
|
||||
}
|
||||
@@ -29,10 +29,10 @@ process_kill(process *self)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_exit(int32_t status)
|
||||
process_exit(int64_t status)
|
||||
{
|
||||
process &p = process::current();
|
||||
log::info(logs::task, "Process <%02lx> exiting with code %d", p.obj_id(), status);
|
||||
log::info(logs::task, "Process <%02lx> exiting with code %#lx", p.obj_id(), status);
|
||||
|
||||
p.exit(status);
|
||||
|
||||
|
||||
@@ -21,10 +21,12 @@ namespace syscalls {
|
||||
using system = class ::system;
|
||||
|
||||
j6_status_t
|
||||
log(const char *message)
|
||||
log(uint8_t area, uint8_t severity, const char *message)
|
||||
{
|
||||
const char *name = process::current().name();
|
||||
thread &th = thread::current();
|
||||
log::info(logs::syscall, "Message <%02lx:%02lx>: %s", th.parent().obj_id(), th.obj_id(), message);
|
||||
log::log(static_cast<logs>(area), static_cast<log::level>(severity),
|
||||
"<%02lx:%02lx> %s: %s", th.parent().obj_id(), th.obj_id(), name, message);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -66,12 +68,11 @@ system_map_phys(system *self, j6_handle_t * area, uintptr_t phys, size_t size, u
|
||||
{
|
||||
// TODO: check to see if frames are already used? How would that collide with
|
||||
// the bootloader's allocated pages already being marked used?
|
||||
if (!(flags & vm_flags::mmio))
|
||||
util::bitset32 f = flags & vm_driver_mask;
|
||||
if (!f.get(vm_flags::mmio))
|
||||
frame_allocator::get().used(phys, mem::page_count(size));
|
||||
|
||||
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask);
|
||||
construct_handle<vm_area_fixed>(area, phys, size, vmf);
|
||||
|
||||
construct_handle<vm_area_fixed>(area, phys, size, f);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
vma_create(j6_handle_t *self, size_t size, uint32_t flags)
|
||||
{
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
if (util::bits::has(f, vm_flags::ring))
|
||||
util::bitset32 f = flags & vm_user_mask;
|
||||
if (f.get(vm_flags::ring))
|
||||
construct_handle<vm_area_ring>(self, size, f);
|
||||
else
|
||||
construct_handle<vm_area_open>(self, size, f);
|
||||
@@ -26,8 +26,8 @@ j6_status_t
|
||||
vma_create_map(j6_handle_t *self, size_t size, uintptr_t *base, uint32_t flags)
|
||||
{
|
||||
vm_area *a = nullptr;
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
if (util::bits::has(f, vm_flags::ring))
|
||||
util::bitset32 f = flags & vm_user_mask;
|
||||
if (f.get(vm_flags::ring))
|
||||
a = construct_handle<vm_area_ring>(self, size, f);
|
||||
else
|
||||
a = construct_handle<vm_area_open>(self, size, f);
|
||||
@@ -40,7 +40,7 @@ j6_status_t
|
||||
vma_map(vm_area *self, process *proc, uintptr_t *base, uint32_t flags)
|
||||
{
|
||||
vm_space &space = proc ? proc->space() : process::current().space();
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
util::bitset32 f = flags & vm_user_mask;
|
||||
*base = space.add(*base, self, f);
|
||||
return *base ? j6_status_ok : j6_err_collision;
|
||||
}
|
||||
|
||||
@@ -91,3 +91,10 @@ _current_gsbase:
|
||||
mov rax, [gs:CPU_DATA.self]
|
||||
ret
|
||||
.end:
|
||||
|
||||
global _reload_cr3: function hidden (_reload_cr3.end - _reload_cr3)
|
||||
_reload_cr3:
|
||||
mov rax, cr3
|
||||
mov cr3, rax
|
||||
ret
|
||||
.end:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <arch/memory.h>
|
||||
|
||||
#include "apic.h"
|
||||
#include "kassert.h"
|
||||
#include "frame_allocator.h"
|
||||
#include "logger.h"
|
||||
@@ -55,9 +56,9 @@ vm_space::vm_space() :
|
||||
obj::vm_area *sysc = new obj::vm_area_fixed(
|
||||
g_sysconf_phys,
|
||||
sizeof(system_config),
|
||||
vm_flags::none);
|
||||
0);
|
||||
|
||||
add(sysconf_user_address, sysc, vm_flags::exact);
|
||||
add(sysconf_user_address, sysc, util::bitset32::of(vm_flags::exact));
|
||||
}
|
||||
|
||||
vm_space::~vm_space()
|
||||
@@ -81,32 +82,31 @@ vm_space::kernel_space()
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
vm_space::add(uintptr_t base, obj::vm_area *area, obj::vm_flags flags)
|
||||
vm_space::add(uintptr_t base, obj::vm_area *new_area, util::bitset32 flags)
|
||||
{
|
||||
if (!base)
|
||||
base = min_auto_address;
|
||||
|
||||
uintptr_t end = base + area->size();
|
||||
|
||||
//TODO: optimize find/insert
|
||||
bool exact = util::bits::has(flags, j6_vm_flag_exact);
|
||||
bool exact = flags.get(vm_flags::exact);
|
||||
for (size_t i = 0; i < m_areas.count(); ++i) {
|
||||
const vm_space::area &a = m_areas[i];
|
||||
uintptr_t aend = a.base + a.area->size();
|
||||
if (base >= aend)
|
||||
const vm_space::area &cur = m_areas[i];
|
||||
uintptr_t cur_end = cur.base + cur.area->size();
|
||||
if (base >= cur_end)
|
||||
continue;
|
||||
|
||||
if (end <= a.base)
|
||||
uintptr_t end = base + new_area->size();
|
||||
if (end <= cur.base)
|
||||
break;
|
||||
else if (exact)
|
||||
return 0;
|
||||
else
|
||||
base = aend;
|
||||
base = cur_end;
|
||||
}
|
||||
|
||||
m_areas.sorted_insert({base, area});
|
||||
area->add_to(this);
|
||||
area->handle_retain();
|
||||
m_areas.sorted_insert({base, new_area});
|
||||
new_area->add_to(this);
|
||||
new_area->handle_retain();
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ vm_space::copy_from(const vm_space &source, const obj::vm_area &vma)
|
||||
|
||||
while (count--) {
|
||||
uint64_t &e = dit.entry(page_table::level::pt);
|
||||
if (e & page_table::flag::present) {
|
||||
if (util::bitset64::from(e) & page_flags::present) {
|
||||
// TODO: handle clobbering mapping
|
||||
}
|
||||
e = sit.entry(page_table::level::pt);
|
||||
@@ -210,11 +210,11 @@ vm_space::page_in(const obj::vm_area &vma, uintptr_t offset, uintptr_t phys, siz
|
||||
return;
|
||||
|
||||
uintptr_t virt = base + offset;
|
||||
page_table::flag flags =
|
||||
page_table::flag::present |
|
||||
(m_kernel ? page_table::flag::none : page_table::flag::user) |
|
||||
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) |
|
||||
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none);
|
||||
util::bitset64 flags =
|
||||
page_flags::present |
|
||||
(m_kernel ? page_flags::none : page_flags::user) |
|
||||
(vma.flags().get(vm_flags::write) ? page_flags::write : page_flags::none) |
|
||||
(vma.flags().get(vm_flags::write_combine) ? page_flags::wc : page_flags::none);
|
||||
|
||||
page_table::iterator it {virt, m_pml4};
|
||||
|
||||
@@ -222,7 +222,7 @@ vm_space::page_in(const obj::vm_area &vma, uintptr_t offset, uintptr_t phys, siz
|
||||
uint64_t &entry = it.entry(page_table::level::pt);
|
||||
entry = (phys + i * frame_size) | flags;
|
||||
log::spam(logs::paging, "Setting entry for %016llx: %016llx [%04llx]",
|
||||
it.vaddress(), (phys + i * frame_size), flags);
|
||||
it.vaddress(), (phys + i * frame_size), flags.value());
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -247,14 +247,14 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
|
||||
while (count--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
util::bitset64 flags = e;
|
||||
|
||||
if (e & page_table::flag::present) {
|
||||
uint64_t orig = e;
|
||||
if (flags & page_flags::present) {
|
||||
e = 0;
|
||||
if (orig & page_table::flag::accessed) {
|
||||
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
|
||||
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
|
||||
}
|
||||
|
||||
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
|
||||
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
|
||||
|
||||
if (free_count && phys == free_start + (free_count * frame_size)) {
|
||||
++free_count;
|
||||
} else {
|
||||
@@ -268,6 +268,9 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
|
||||
++it;
|
||||
}
|
||||
|
||||
current_cpu().apic->send_ipi_broadcast(
|
||||
lapic::ipi_fixed, false, isr::ipiShootdown);
|
||||
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
}
|
||||
@@ -290,11 +293,11 @@ vm_space::lock(const obj::vm_area &vma, uintptr_t offset, size_t count)
|
||||
while (count--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
util::bitset64 flags = e;
|
||||
|
||||
if (e & page_table::flag::present) {
|
||||
uint64_t orig = e;
|
||||
if (flags & page_flags::present) {
|
||||
e = locked_page_tag;
|
||||
if (orig & page_table::flag::accessed) {
|
||||
if (flags & page_flags::accessed) {
|
||||
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
|
||||
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
|
||||
}
|
||||
@@ -338,10 +341,10 @@ vm_space::initialize_tcb(TCB &tcb)
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
||||
vm_space::handle_fault(uintptr_t addr, util::bitset8 fault)
|
||||
{
|
||||
// TODO: Handle more fult types
|
||||
if (fault && fault_type::present)
|
||||
if (fault.get(fault_type::present))
|
||||
return false;
|
||||
|
||||
uintptr_t page = (addr & ~0xfffull);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/flags.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/spinlock.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
/// \arg area The area to add
|
||||
/// \arg flags Flags for the operation (exact, clobber, etc)
|
||||
/// \returns The base address the area was added at
|
||||
uintptr_t add(uintptr_t base, obj::vm_area *area, obj::vm_flags flags);
|
||||
uintptr_t add(uintptr_t base, obj::vm_area *area, util::bitset32 flags);
|
||||
|
||||
/// Remove a virtual memory area from this address space
|
||||
/// \arg area The area to remove
|
||||
@@ -88,15 +88,6 @@ public:
|
||||
/// Set this space as the current active space
|
||||
void activate() const;
|
||||
|
||||
enum class fault_type : uint8_t {
|
||||
none = 0x00,
|
||||
present = 0x01,
|
||||
write = 0x02,
|
||||
user = 0x04,
|
||||
reserved = 0x08,
|
||||
fetch = 0x10
|
||||
};
|
||||
|
||||
/// Allocate pages into virtual memory. May allocate less than requested.
|
||||
/// \arg virt The virtual address at which to allocate
|
||||
/// \arg count The number of pages to allocate
|
||||
@@ -104,11 +95,13 @@ public:
|
||||
/// \returns The number of pages actually allocated
|
||||
size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys);
|
||||
|
||||
enum class fault_type { present, write, user, reserved, fetch };
|
||||
|
||||
/// Handle a page fault.
|
||||
/// \arg addr Address which caused the fault
|
||||
/// \arg ft Flags from the interrupt about the kind of fault
|
||||
/// \returns True if the fault was successfully handled
|
||||
bool handle_fault(uintptr_t addr, fault_type fault);
|
||||
bool handle_fault(uintptr_t addr, util::bitset8 fault);
|
||||
|
||||
/// Set up a TCB to operate in this address space.
|
||||
void initialize_tcb(TCB &tcb);
|
||||
@@ -155,6 +148,4 @@ private:
|
||||
util::vector<area> m_areas;
|
||||
|
||||
util::spinlock m_lock;
|
||||
};
|
||||
|
||||
is_bitfield(vm_space::fault_type);
|
||||
};
|
||||
79
src/libraries/acpi/acpi.cpp
Normal file
79
src/libraries/acpi/acpi.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/tables.h>
|
||||
#include <j6/syslog.hh>
|
||||
|
||||
namespace acpi {
|
||||
|
||||
system::system(const void* phys, const void *virt) :
|
||||
m_offset { util::get_offset(phys, virt) },
|
||||
m_root { reinterpret_cast<const rsdp2*>(virt) }
|
||||
{}
|
||||
|
||||
system::iterator
|
||||
system::begin() const
|
||||
{
|
||||
const xsdt *sdt =
|
||||
acpi::check_get_table<xsdt>(m_root->xsdt_address);
|
||||
|
||||
if (!sdt)
|
||||
return {nullptr, 0};
|
||||
|
||||
return {&sdt->headers[0], m_offset};
|
||||
}
|
||||
|
||||
|
||||
system::iterator
|
||||
system::end() const
|
||||
{
|
||||
const xsdt *sdt =
|
||||
acpi::check_get_table<xsdt>(m_root->xsdt_address);
|
||||
|
||||
if (!sdt)
|
||||
return {nullptr, 0};
|
||||
|
||||
size_t nheaders = table_entries<xsdt>(sdt, sizeof(table_header*));
|
||||
return {&sdt->headers[nheaders], m_offset};
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
load_acpi(j6_handle_t sys, const bootproto::module *mod)
|
||||
{
|
||||
const bootproto::acpi *info = mod->data<bootproto::acpi>();
|
||||
const util::const_buffer ®ion = info->region;
|
||||
|
||||
map_phys(sys, region.pointer, region.count);
|
||||
|
||||
const void *root_table = info->root;
|
||||
if (!root_table) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "null ACPI root table pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
const acpi::rsdp2 *acpi2 =
|
||||
reinterpret_cast<const acpi::rsdp2 *>(root_table);
|
||||
|
||||
const auto *xsdt =
|
||||
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
|
||||
|
||||
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi::table_header *header = xsdt->headers[i];
|
||||
if (!header->validate()) {
|
||||
j6::syslog(j6::logs::srv, j6::log_level::error, "ACPI table at %lx failed validation", header);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (header->type) {
|
||||
case acpi::mcfg::type_id:
|
||||
load_mcfg(sys, header);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace acpi
|
||||
12
src/libraries/acpi/acpi.module
Normal file
12
src/libraries/acpi/acpi.module
Normal file
@@ -0,0 +1,12 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("acpi",
|
||||
kind = "lib",
|
||||
deps = [ "util", "j6" ],
|
||||
sources = [
|
||||
"acpi.cpp",
|
||||
],
|
||||
public_headers = [
|
||||
"acpi/acpi.h",
|
||||
"acpi/tables.h",
|
||||
])
|
||||
53
src/libraries/acpi/include/acpi/acpi.h
Normal file
53
src/libraries/acpi/include/acpi/acpi.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
/// \file acpi.h
|
||||
/// Routines for loading and parsing ACPI tables
|
||||
|
||||
#include <stddef.h>
|
||||
#include <acpi/tables.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace acpi {
|
||||
|
||||
struct system
|
||||
{
|
||||
system(const void* phys, const void *virt);
|
||||
|
||||
/// Iterator for all tables in the system
|
||||
struct iterator
|
||||
{
|
||||
iterator(const table_header *const *addr, ptrdiff_t off) : m_addr {addr}, m_off {off} {};
|
||||
|
||||
operator const table_header *() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
const table_header * operator*() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
const table_header * operator->() const { return m_addr ? offset(*m_addr) : nullptr; }
|
||||
|
||||
iterator & operator++() { increment(); return *this; }
|
||||
iterator operator++(int) { iterator old = *this; increment(); return old; }
|
||||
|
||||
friend bool operator==(const iterator &a, const iterator &b) { return a.m_addr == b.m_addr; }
|
||||
friend bool operator!=(const iterator &a, const iterator &b) { return a.m_addr != b.m_addr; }
|
||||
|
||||
private:
|
||||
inline void increment() { if (m_addr) ++m_addr; }
|
||||
|
||||
table_header const * const * m_addr;
|
||||
ptrdiff_t m_off;
|
||||
|
||||
inline const table_header * offset(const table_header * addr) const {
|
||||
return util::offset_pointer(addr, m_off);
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
const xsdt * get_xsdt() {
|
||||
return check_get_table<xsdt>(util::offset_pointer(m_root->xsdt_address, m_offset));
|
||||
}
|
||||
|
||||
ptrdiff_t m_offset;
|
||||
const rsdp2* m_root;
|
||||
};
|
||||
|
||||
} // namespace acpi
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/misc.h> // for byteswap32
|
||||
|
||||
namespace acpi {
|
||||
@@ -21,6 +21,7 @@ struct table_header
|
||||
uint32_t creator_revision;
|
||||
|
||||
bool validate() const { return util::checksum(this, length) == 0; }
|
||||
bool validate(uint32_t sig) const { return type == sig && validate(); }
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
@@ -62,32 +63,31 @@ struct gas
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class fadt_flags : uint32_t
|
||||
enum class fadt_flags
|
||||
{
|
||||
wbinvd = 0x00000001,
|
||||
wbinvd_flush = 0x00000002,
|
||||
proc_c1 = 0x00000004,
|
||||
p_lvl2_up = 0x00000008,
|
||||
pwr_button = 0x00000010,
|
||||
slp_button = 0x00000020,
|
||||
fix_rtc = 0x00000040,
|
||||
rtc_s4 = 0x00000080,
|
||||
tmr_val_ext = 0x00000100,
|
||||
dck_cap = 0x00000200,
|
||||
reset_reg_sup = 0x00000400,
|
||||
sealed_case = 0x00000800,
|
||||
headless = 0x00001000,
|
||||
cpu_sw_slp = 0x00002000,
|
||||
pci_exp_wak = 0x00004000,
|
||||
use_plat_clock = 0x00008000,
|
||||
s4_rtc_sts_val = 0x00010000,
|
||||
remote_pwr_cap = 0x00020000,
|
||||
apic_cluster = 0x00040000,
|
||||
apic_physical = 0x00080000,
|
||||
hw_reduced_acpi = 0x00100000,
|
||||
low_pwr_s0_idle = 0x00200000
|
||||
wbinvd,
|
||||
wbinvd_flush,
|
||||
proc_c1,
|
||||
p_lvl2_up,
|
||||
pwr_button,
|
||||
slp_button,
|
||||
fix_rtc,
|
||||
rtc_s4,
|
||||
tmr_val_ext,
|
||||
dck_cap,
|
||||
reset_reg_sup,
|
||||
sealed_case,
|
||||
headless,
|
||||
cpu_sw_slp,
|
||||
pci_exp_wak,
|
||||
use_plat_clock,
|
||||
s4_rtc_sts_val,
|
||||
remote_pwr_cap,
|
||||
apic_cluster,
|
||||
apic_physical,
|
||||
hw_reduced_acpi,
|
||||
low_pwr_s0_idle
|
||||
};
|
||||
is_bitfield(fadt_flags);
|
||||
|
||||
struct fadt
|
||||
{
|
||||
@@ -134,7 +134,7 @@ struct fadt
|
||||
|
||||
uint16_t iapc_boot_arch;
|
||||
uint8_t reserved1;
|
||||
fadt_flags flags;
|
||||
util::bitset32 flags;
|
||||
|
||||
gas reset_reg;
|
||||
uint8_t reset_value;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user