47 Commits

Author SHA1 Message Date
Justin C. Miller
7da34dbffb WIP linux multiarch support 2025-11-23 23:50:13 -08:00
Justin C. Miller
90a0eb3c53 [build] first pass at multiarch support
Changing bonnibel to respect the --arch flag to configure. This requires
some reworking of modules, mostly in the addition of the ModuleList
class instead of just a dict of modules.
2025-11-23 23:37:37 -08:00
Justin C. Miller
d1ddbd2cf1 [pci] Move PCIe and ACPI code to their own libs
This pulls them out of srv.init (and src/include where the kernel uses
them) and puts them into their own libs for sharing.
2024-08-18 19:44:47 -07:00
Justin C. Miller
81b331e5da [kernel] Clean up includes in kernel logger
Drive-by include fixup as I was looking into how to teach the bootloader
to store its logs in the kernel's log memory.
2024-08-18 19:41:41 -07:00
Justin C. Miller
1ebee17880 [build] Explicitly add c++ standard headers to userspace
This helps language servers not freak out.
2024-08-18 19:40:16 -07:00
Justin C. Miller
4649d5f77b [kernel] Add a _very_ basic TLB shootdown mechanism
This TLB shootdown implementation is pretty janky: When the kernel
unmaps a mapping, it sends an IPI to all other cores and doesn't even
wait to ensure they've finished handling it. Upon getting one of these
IPIs, the core just re-writes cr3 to flush all TLBs.
2024-08-13 19:02:40 -07:00
Justin C. Miller
d3c1d6cc34 [kernel] Add debug names to processes
To make debugging easier, add names to processes. These are arbitrary
and supplied by the caller of process_create. The max is 31 characters
in debug configuration, and 0 in release.
2024-08-12 19:40:20 -07:00
Justin C. Miller
ee24ec8d5c [kernel] Ensure all VMA sizes are multiples of page size
Using a 0 address in vma_create_map or vma_map would run into issues if
VMAs had sizes that didn't end on page boundaries. Since any size that's
not a multiples of the page size is a lie, make vm_area's ctor enforce
page sizing.
2024-08-12 19:28:08 -07:00
Justin C. Miller
372325fab7 [scripts] Fix for missing path seperator in errno.h
Make sure to add a path seperator after the root in
codegen.arch_includes.
2024-08-12 19:26:56 -07:00
Justin C. Miller
8f036d9293 [kernel] Fix the mysterious paging bug!!
There has been a random bug (that occurs frequently outside the debugger
but rarely inside the debugger, of course) that seemed to be caused by
inconsistent page mappings. Sometimes loading an ELF would work. Other
times loading that same ELF, the loader would complain of missing
sections or invalid headers. Worse, occasionally program execution would
jump off into random memory for no reason I could see by examining the
disassembly. This issue has been plauging me FOR A YEAR and I've been
pulling my hair out trying to find it.

https://stackoverflow.com/a/28384866

Eventually this stack overflow answer to a different question about
INVLPG gave me a hint that the 'accessed' flag of page table entries
might not be set on pages even if they end up in the TLB.

Good riddance to this damned bug.
2024-08-11 12:40:13 -07:00
Justin C. Miller
e345cdd1a7 [util] Add util::bitset::set(i, val)
Allow for easier callsite code instead of having to switch calling
set(i) vs clear(i) - now set(i, boolean) is an option.
2024-08-10 23:33:26 -07:00
Justin C. Miller
fca570a163 [scripts] Make j6libc.py into the codegen package
j6libc.py was initially made for libc to generate stdint.h, but it
gained a few things that aren't libc-specific. Move it to a package and
split the int-types-specific code into codegen.int_types.
2024-08-10 23:29:21 -07:00
Justin C. Miller
ff64d1989f [docs] Update docs: kernel mem, process init
Updated documentation: Added documentation on the kernel address space
layout, process initialization, and rebuilt the syscall docs.
2024-08-10 23:11:14 -07:00
Justin C. Miller
d3f5db2479 [crt0] Actually pass argc, argv, envp to main()s
With the new SysV style process init args, it's a bit easier to finally
parse out argc, argv, and envp from the stack and pass them on to main
functions.
2024-08-10 17:37:34 -07:00
Justin C. Miller
fa587060f1 [libj6] Change to a more SysV style process init args
Pull out the old linked list of args structures in favor of doing things
the SysV ABI-specified way.
2024-08-09 19:14:44 -07:00
Justin C. Miller
b137c75eb2 [kernel] Use box-drawing characters in debugcon
Slight change to debugcon display format that replaces | with the
box-drawing character equivalent to seperate area/level from log
messages.
2024-08-08 19:46:32 -07:00
Justin C. Miller
05c1361283 [libc] Implement setjmp/longjmp
Add a very simple setjmp/longjmp implementation. No destructors or other
cleanup is handled.
2024-08-08 19:31:20 -07:00
Justin C. Miller
c6835dad70 [tools] Update telnet to nc in qemu.sh
The telnet utility seems to have stopped existing on my wsl
installation. That's fine, netcat is the more correct tool here anyways.
2024-08-04 12:14:42 -07:00
Justin C. Miller
e7fa1dde97 [ld.so] Mark main module to not run ctors
Forgot to include this in the change for ld.so to run global ctors for
all modules. The main module itself will have its ctors run last, by
crt0, so mark it as not needing ctors in ld.so.
2024-04-30 22:24:34 -07:00
Justin C. Miller
eb62588b79 [6s] Allow 6s to know about filesystems
Added a j6_proto_vfs_tag/_get_tag pair of messages to the VFS protocol
to allow filesystems to label themselves, and gave 6s the concept of
current fs and current working directory.
2024-04-30 22:23:04 -07:00
Justin C. Miller
29332cbd45 [libc] Update errno and strto* files for compatibility
Most of the strto* functions are only stubbed out, but they're there for
things that link to them but don't call them.
2024-04-30 22:20:41 -07:00
Justin C. Miller
172eb70551 [6s] Break out builtin commands into a list of structs
The 6s builtin commands are now in a separate file, with a list of name,
description, and function pointers.
2024-04-29 01:11:15 -07:00
Justin C. Miller
7322df98f5 [ld.so] Call all image global ctors, not just libc
With the move to dynamic executables, crt0's _start was only ever
calling libc's __init_libc, which only ran libc's init_array list. Now
make crt0 itself (which is statically linked into every executable) call
it's own init_array list and have ld.so call every other image's ctor
lists.
2024-04-29 01:07:18 -07:00
70af5a31cd [6s] Add simple command handling
Super simple little strncmp-based command lookup, adding 'help' and
'version' commands to 6s.
2024-04-28 19:36:27 -07:00
0ab2f59fe8 [libc] Fix a strncmp bug
The strncmp implementation too eagerly advanced the string pointers,
such that it would compare the next characters after the ones that
didn't match.
2024-04-28 19:35:12 -07:00
ab84cdb790 [edit] Change libedit API
The edit::line API now takes a source object for I/O, qnd allows getting
a single command from the editor.
2024-04-28 17:24:15 -07:00
da9041823b [edit] Add line-editing library, libedit
Now used by the 6s shell to enable backspace, return, etc. No cursor
movement yet or anything complex.
2024-04-27 17:53:18 -07:00
2d7cfc6c59 [scripts] Fix mkj6romfs for real
I overwrote it last time.
2024-04-27 16:20:23 -07:00
cee4fe67fc [tools] Let GDB quit easily if inferior already quit
I had defined hook-quit in the GDB script to kill the inferior. But if
the inferior had already terminated, then trying to quit GDB only
printed an error. Now hook-quit is smarter and checks if the inferior is
running before trying to kill it.
2024-04-27 15:41:05 -07:00
53f90f5a1d [scripts] Fix initrd directory compression
The mkj6romfs.py script always wrote uncompessed directory info to the
initrd image, even if compressed was smaller - but it would write the
metadata as if it had compressed it.
2024-04-27 13:55:05 -07:00
bab2dd5c69 [libc] Change exit status from int to long
Slightly breaking the C standard, but in a way that's unlikely to break
things - allow 64-bit process exit status codes.
2024-04-27 12:59:02 -07:00
3b9efc11d0 [drv.uart] Read everything from the channel in uartnnMake sure to keep reading until the channel is empty so output isn't behindnby a keystroke. 2024-04-26 00:32:06 -07:00
182d3b0a6a [kernel] Add the handle_close syscallnnAdd a generic handle_close syscall and use it in j6::channel when failing to open 2024-04-24 15:48:00 -07:00
Justin C. Miller
ed38e989b1 [srv.logger] Update logger for new channel/SLP
Update srv.logger to build correctly with new channel and service
locator changes.
2024-04-23 23:59:06 -07:00
Justin C. Miller
1bc6f422c5 [kernel] Fix a bug with auto-assigned VMA addresses
When calling vma_map or vma_create_map with an address of 0, the kernel would
return colliding addresses.
2024-04-23 23:47:52 -07:00
Justin C. Miller
4909d15c20 [boot] Fix panic handler not installing
The switch to util::bitset introduced a typo that caused panic handlers
to never load.
2024-04-23 23:46:31 -07:00
Justin C. Miller
e725795a17 [6s] Add 6s shell, make channels full-duplex
This commit adds the 6s shell, and a bunch of supporting work for it.
Major changes include:

- New shell.yaml manifest to give 6s control of the TTY instead of
  srv.logger
- Changes to mailbox syscalls to add max handles array size separate
  from input size. Also reversed the meaning of the similar data size
  argument in those syscalls. (Using the second arg as the max array
  size and the first as the current valid size allows for the auto
  verify code to verify handles properly, and simplifies user-side
  code.)
- New util::unique_ptr smart pointer class similar to std::unique_ptr
- New ipc::message format that uses util::unique_ptr to manage ownership
  and lifetimes and avoid extra copying.
- The service locator protocol now supports multiple handles per entry
- Channels got a major overhaul. They are now split into two VMAs, each
  containing a mutex, a condition, and a util::bip_buffer. The order of
  the VMAs determines which end of the pipe you're on. (ie, the creator
  swaps them before handing them to the other thread.) Their API also
  changed to be similar to that of util::bip_buffer, to avoid extra
  copies.
- util::bip_buffer now keeps its state and its buffer together, so that
  there are no pointers. This allows multiple processes to share them in
  shared memory, like in channels.
- The UART driver changed from keeping buffers for the serial ports to
  just keeping a channel, and the serial port objects read/write
  directly from/to the channel.

Known issues:

- The shell doesn't actually do anything yet. It echos its input back to
  the serial line and injects a prompt on new lines.
- The shell is one character behind in printing back to the serial line.
2024-04-23 23:32:28 -07:00
Justin C. Miller
d8a21608c3 [docs] Set up github pages workflow
First pass at a GitHub Actions workflow to deploy the docs site.
2024-03-08 01:12:17 -08:00
Justin C. Miller
396fc131e0 [docs] Add sphinx documentation
Add a first pass at documentation with sphinx.
2024-03-07 21:48:25 -08:00
Justin C. Miller
40130076b2 [uart] Fix UART driver hangs
The UART driver would constantly hang in unpredictable spots. Turns out
it could get into a situation where it was stuck in a loop unable to
read more from the receive channel, and/or write to the serial port
buffer. Now we use a ring buffer to read as much as possible from the
receive channel, write as much as possible to the serial port buffer,
and move on without looping.
2024-03-04 19:48:16 -08:00
Justin C. Miller
9f54927a82 [util] Remove enum_bitfields
The enum_bitfields system never worked quite right, and always had edge cases where name
resolution for the SFINAE would fail. Move everything over to use util::bitset, which can
be constexpr and boils down to inline integer bitops in release mode.

Improved util::bitset itself, moving the array-backed base implementation into a new
util::sized_bitset, and making the single-inttype backed implementation the base case.
Also added a distinction between | or |= (which work with real bit values) and + or +=
(which work with bit indexes).
2024-02-25 23:40:14 -08:00
Justin C. Miller
f7ea46e49e [build] Get release mode optimizations working
Added a release config, and fixed a few spots where optimizations broke things:

- Clang was generating incorrect code for run_ctor_list in libc's init.cpp (it
  ignored a check for the end of the list)
- my rep movsb memcpy implementation used incorrect inline asm constraints, so
  it was returning a pointer to the end of the copied range instead of the start.
  Since this function was just inline asm anyway, I rewrote it in asm by hand in
  a new memutils.s file.
2024-02-25 17:09:04 -08:00
Justin C. Miller
bc46c9a7d5 [libj6] Add log area and severity to j6::syslog()
User code can now set the log area and severity of log messages. This also updates the j6_log
syscall to take these parameters, but removes all calls to it except through j6::syslog().
2024-02-24 13:39:24 -08:00
Justin C. Miller
a1e2c58afc [kernel] Add spam logging to trace mailbox calls
Added a new IPC log category, and logging of mailbox calls in it. Also added
some logs in init's service locator to help track down the bug fixed in the
previous commit.
2024-02-21 19:40:28 -08:00
Justin C. Miller
4e73e933db [libj6] Update how init args are passed and used
Now the init args are a linked list - this also means ld.so can use its
own plus those of the program (eg, SLP and VFS handles). __init_libj6
now adds the head of the list to its global init_args structure, and the
j6_find_init_handle function can be used to find a handle in those args
for a given proto.

This fixes situations like the logger using the wrong mailbox for the
service locator and never finding the uart driver.
2024-02-20 20:51:14 -08:00
Justin C. Miller
9f8e75f680 [ld.so] Properly handle stack alignment in ld.so args
Now when loading a process, init will push all the loader args, align
the stack so that the next pointer will have correct alignment, then
push a pointer to the loader args.

Also:
* _ldso_start will pop all loader args off of the stack before jumping
  to the loaded program's entrypoint, as if it had never run.
* The sentinel arg structures have a size instead of being all zeros, so
  that they can be popped off properly when done.
2024-02-20 19:42:12 -08:00
Justin C. Miller
e17119254b [libc] Add enough stubs to support new LLVM 16 sysroot
time.h and wctype.h had "#error not yet implemented" in them. Now time.h is correct (though the functions
are only declared), and wctype.h exists enough to define its types. Also a dlsym stub was added that just
returns 0.
2024-02-19 16:53:36 -08:00
216 changed files with 5467 additions and 2060 deletions

56
.github/workflows/sphinx_deploy.yml vendored Normal file
View 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
View File

@@ -16,3 +16,4 @@ compile_commands.json
buddy_allocs.txt buddy_allocs.txt
frame_allocs.txt frame_allocs.txt
heap_allocs.txt heap_allocs.txt
/docs/_build

View File

@@ -0,0 +1,3 @@
ccflags: [
"-O3",
]

View 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",
]

View 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",
]

View File

@@ -0,0 +1,9 @@
---
ccflags: [
"-g3",
"-ggdb",
]
ldflags: [
"-g",
]

View File

@@ -0,0 +1,3 @@
ccflags: [
"-O3",
]

View 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" ]

View 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

View 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",
]

View File

@@ -0,0 +1,7 @@
---
ccflags: [
]
ldflags: [
"-shared",
]

View File

@@ -501,6 +501,18 @@ class LinkedListPrinter:
return self.items 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(): def build_pretty_printers():
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix") pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
pp.add_printer("cap table", '^cap_table$', CapTablePrinter) pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
@@ -520,9 +532,10 @@ GetThreadsCommand()
PrintProfilesCommand() PrintProfilesCommand()
DumpLogCommand() DumpLogCommand()
ShowCurrentProcessCommand() ShowCurrentProcessCommand()
IsRunning()
gdb.execute("display/i $rip") 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: if not gdb.selected_inferior().was_attached:
gdb.execute("add-symbol-file build/panic.serial.elf") gdb.execute("add-symbol-file build/panic.serial.elf")
gdb.execute("target remote :1234") gdb.execute("target remote :1234")

View 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

View 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
View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
def generate(output, config, manifest): def generate(output, config, arch, manifest):
from os import makedirs from os import makedirs
from glob import iglob from glob import iglob
from pathlib import Path from pathlib import Path
from bonnibel.module import Module from bonnibel.module import Module, ModuleList
from bonnibel.project import Project from bonnibel.project import Project
root = Path(__file__).parent.resolve() root = Path(__file__).parent.resolve()
@@ -18,16 +18,17 @@ def generate(output, config, manifest):
str(root / "external/*.module"), str(root / "external/*.module"),
] ]
modules = {} modules = ModuleList(arch)
for source in sources: for source in sources:
for modfile in iglob(source, recursive=True): for modfile in iglob(source, recursive=True):
path = Path(modfile).parent modfile = Path(modfile)
path = modfile.parent
def module_init(name, **kwargs): def module_init(name, **kwargs):
if not "root" in kwargs: if not "root" in kwargs:
kwargs["root"] = path kwargs["root"] = path
m = Module(name, modfile, **kwargs) m = Module(name, modfile, **kwargs)
modules[m.name] = m modules.add(m)
return m return m
glo = { glo = {
@@ -36,18 +37,16 @@ def generate(output, config, manifest):
"build_root": output, "build_root": output,
"module_root": path, "module_root": path,
"config": config, "config": config,
"arch": arch,
} }
code = compile(open(modfile, 'r').read(), modfile, "exec") code = compile(open(modfile, 'r').read(), modfile, "exec")
loc = {} loc = {}
exec(code, glo, loc) exec(code, glo, loc)
Module.update(modules)
makedirs(output.resolve(), exist_ok=True) makedirs(output.resolve(), exist_ok=True)
project.generate(root, output, modules, config, manifest) project.generate(root, output, modules, config, arch, manifest)
for mod in modules.values(): modules.generate(output)
mod.generate(output)
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
@@ -57,18 +56,25 @@ if __name__ == "__main__":
from argparse import ArgumentParser from argparse import ArgumentParser
from bonnibel import BonnibelError from bonnibel import BonnibelError
default_arch = "amd64"
p = ArgumentParser(description="Generate jsix build files") p = ArgumentParser(description="Generate jsix build files")
p.add_argument("--manifest", "-m", metavar="FILE", default="assets/manifests/default.yaml", p.add_argument("--manifest", "-m", metavar="FILE", default="assets/manifests/default.yaml",
help="File to use as the system manifest") 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')") 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") help="Where to create the build root")
args = p.parse_args() args = p.parse_args()
output = args.output or f"build.{args.arch}"
try: try:
generate(args.output, args.config, args.manifest) generate(output, args.conf, args.arch, args.manifest)
except BonnibelError as be: except BonnibelError as be:
import sys import sys

View File

@@ -1,29 +1,38 @@
--- ---
- name: linear - name: linear
desc: Linearly-mapped physical memory
size: 64T size: 64T
shared: true shared: true
- name: bitmap - name: bitmap
desc: Used/free page tracking bitmap
size: 1T size: 1T
shared: true shared: true
- name: heapmap - name: heapmap
desc: Kernel heap accounting structures
size: 32G size: 32G
- name: heap - name: heap
desc: Kernel heap
size: 32G size: 32G
- name: capsmap - name: capsmap
desc: Capabilities accounting structures
size: 32G size: 32G
- name: caps - name: caps
desc: Capabilities
size: 32G size: 32G
- name: stacks - name: stacks
desc: Kernel thread stacks
size: 64G size: 64G
- name: buffers - name: buffers
desc: Kernel buffers
size: 64G size: 64G
- name: logs - name: logs
desc: Kernel logs circular buffer
size: 2G size: 2G

View File

@@ -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 { object event : object {
uid f441e03da5516b1a uid f441e03da5516b1a

View File

@@ -20,8 +20,9 @@ object mailbox : object {
method call [cap:send] { method call [cap:send] {
param tag uint64 [inout] param tag uint64 [inout]
param data buffer [optional 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 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 # Respond to a message sent using call, and wait for another
@@ -31,8 +32,9 @@ object mailbox : object {
method respond [cap:receive] { method respond [cap:receive] {
param tag uint64 [inout] param tag uint64 [inout]
param data buffer [optional 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 ref object [optional inout handle list]
param handles_size size # total size of handles buffer
param reply_tag uint64 [inout] param reply_tag uint64 [inout]
param flags uint64 param flags uint64
} }

View File

@@ -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] { object object [virtual] {
uid 667f61fb2cd57bb4 uid 667f61fb2cd57bb4
cname kobject cname kobject

View File

@@ -1,7 +1,7 @@
import "objects/object.def" import "objects/object.def"
# Processes are a collection of handles and a virtual memory # A ``process`` object represents a process running on the system, and allows
# space inside which threads are run. # control over the threads, handles, and virtual memory space of that process.
object process : object { object process : object {
uid 0c69ee0b7502ba31 uid 0c69ee0b7502ba31
@@ -12,14 +12,16 @@ object process : object {
] ]
# Create a new empty process # Create a new empty process
method create [constructor] method create [constructor] {
param name string
}
# Stop all threads and exit the given process # Stop all threads and exit the given process
method kill [destructor cap:kill] method kill [destructor cap:kill]
# Stop all threads and exit the current process # Stop all threads and exit the current process
method exit [static noreturn] { 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 # Give the given process a handle that points to the same

View File

@@ -1,5 +1,6 @@
# The system object represents a handle to kernel functionality # The singular ``system`` object represents a handle to kernel functionality
# needed by drivers and other priviledged services # needed by drivers and other priviledged services.
object system : object { object system : object {
uid fa72506a2cf71a30 uid fa72506a2cf71a30

View File

@@ -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 { object thread : object {
uid 11f23e593d5761bd uid 11f23e593d5761bd

View File

@@ -1,5 +1,9 @@
import "objects/process.def" 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 { object vma : object {
uid d6a12b63b3ed3937 uid d6a12b63b3ed3937
cname vm_area cname vm_area

View File

@@ -24,6 +24,8 @@ interface syscalls [syscall] {
# Write a message to the kernel log # Write a message to the kernel log
function log { function log {
param area uint8
param severity uint8
param message string param message string
} }
@@ -42,6 +44,11 @@ interface syscalls [syscall] {
param mask uint32 # The capability bitmask 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 # Block waiting on a futex
function futex_wait [static] { function futex_wait [static] {
param address uint32* # Address of the futex value param address uint32* # Address of the futex value

23
docs/Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
** !_build/** ../definitions/**.def {
prep: rm -rf _build; make html
}
_build/html/** {
daemon: devd -m _build/html
}

View 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
View 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
View 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)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
root=$(dirname $0) root=$(dirname $0)
build="${root}/build" build="${root}/build.amd64"
assets="${root}/assets" assets="${root}/assets"
no_build="" no_build=""
@@ -41,7 +41,7 @@ while true; do
-r | --remote) -r | --remote)
shift shift
vnchost="${1:-${VNCHOST:-"localhost:5500"}}" vnchost="${1:-${VNCHOST:-"localhost:5500"}}"
gfx="-vnc ${vnchost},reverse" gfx="-vnc ${vnchost},reverse=on"
vga="" vga=""
shift shift
;; ;;
@@ -117,13 +117,13 @@ if [[ -n $TMUX ]]; then
tmux split-window -h -l $log_width "$debugcon_cmd" tmux split-window -h -l $log_width "$debugcon_cmd"
tmux last-pane tmux last-pane
fi fi
tmux split-window -l 10 "sleep 1; telnet localhost 45454" tmux split-window -l 10 "sleep 1; nc localhost 45454"
fi fi
elif [[ $DESKTOP_SESSION = "i3" ]]; then elif [[ $DESKTOP_SESSION = "i3" ]]; then
if [[ -n $debug ]]; then if [[ -n $debug ]]; then
i3-msg exec i3-sensible-terminal -- -e "gdb ${debugtarget}" & i3-msg exec i3-sensible-terminal -- -e "gdb ${debugtarget}" &
else else
i3-msg exec i3-sensible-terminal -- -e 'telnet localhost 45454' & i3-msg exec i3-sensible-terminal -- -e 'nc localhost 45454' &
fi fi
fi fi

View File

@@ -7,3 +7,5 @@ pure-cdb == 4
pyzstd == 0.15 pyzstd == 0.15
pyelftools pyelftools
iced-x86 iced-x86
sphinx
renku-sphinx-theme

View File

@@ -43,10 +43,10 @@ def _make_ninja_config(outfile, config, files):
build.variable(k, v) 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" assets = root / "assets" / "build" / arch
base = ["global.yaml", f"config.{config}.yaml"] base = ["global.yaml", f"config.{buildconfig}.yaml"]
depfiles = set() depfiles = set()

View File

@@ -37,9 +37,15 @@ class Manifest:
self.drivers = [self.__build_entry(modules, i) self.drivers = [self.__build_entry(modules, i)
for i in config.get("drivers", tuple())] for i in config.get("drivers", tuple())]
libs = set(config.get("libs", tuple())) def get_libdeps(names):
libs.update(self.__libdeps([modules[e.module] for e in self.services])) libmods = modules.get_mods(names)
libs.update(self.__libdeps([modules[e.module] for e in self.drivers])) 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) self.libs = [self.__build_entry(modules, i)
for i in libs] for i in libs]
@@ -80,13 +86,6 @@ class Manifest:
return Manifest.Entry(name, target, mod.get_output(), flags) return Manifest.Entry(name, target, mod.get_output(), flags)
def __libdeps(self, modules):
deps = set([m.name for m in modules if m.kind == "lib"])
for m in modules:
if m.static: continue
deps.update(self.__libdeps(m.depmods))
return deps
def add_data(self, output, desc, flags=tuple()): def add_data(self, output, desc, flags=tuple()):
e = Manifest.Entry(None, None, output, flags) e = Manifest.Entry(None, None, output, flags)
self.data.append(e) self.data.append(e)

View File

@@ -39,7 +39,7 @@ class Module:
"kind": (str, "exe"), "kind": (str, "exe"),
"outfile": (str, None), "outfile": (str, None),
"basename": (str, None), "basename": (str, None),
"targets": (set, ()), "target": (str, None),
"deps": (set, ()), "deps": (set, ()),
"public_headers": (set, ()), "public_headers": (set, ()),
"copy_headers": (bool, False), "copy_headers": (bool, False),
@@ -53,6 +53,8 @@ class Module:
"no_libc": (bool, False), "no_libc": (bool, False),
"ld_script": (str, None), "ld_script": (str, None),
"static": (bool, False), "static": (bool, False),
"arch_source": (dict, ()),
"skip_arches": (tuple, ()),
} }
def __init__(self, name, modfile, root, **kwargs): def __init__(self, name, modfile, root, **kwargs):
@@ -81,15 +83,14 @@ class Module:
# Turn strings into real Source objects # Turn strings into real Source objects
self.sources = [make_source(root, f) for f in self.sources] 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) header_source = lambda f: make_source(root, Path("include") / f)
if self.copy_headers: if self.copy_headers:
header_source = lambda f: make_copy_source(root, f, "include") header_source = lambda f: make_copy_source(root, f, "include")
self.public_headers = [header_source(f) for f in self.public_headers] self.public_headers = [header_source(f) for f in self.public_headers]
# Filled by Module.update
self.depmods = set()
def __repr__(self): def __repr__(self):
return f"<Module {self.kind} {self.name}>" return f"<Module {self.kind} {self.name}>"
@@ -115,31 +116,101 @@ class Module:
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so")) ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
return self.basename + ext.get(self.kind, "") return self.basename + ext.get(self.kind, "")
@classmethod def add_input(self, path, **kwargs):
def update(cls, mods): from .source import make_source
from . import BonnibelError s = make_source(self.root, path, **kwargs)
self.sources.append(s)
return s.outputs
def resolve(source, modlist): def add_depends(self, paths, deps):
resolved = set() for source in self.sources:
for dep in modlist: if source.path in paths:
if not dep in mods: source.add_deps(deps)
raise BonnibelError(f"module '{source.name}' references unknown module '{dep}'")
mod = mods[dep]
resolved.add(mod)
return resolved
for mod in mods.values(): for source in self.public_headers:
mod.depmods = resolve(mod, mod.deps) if source.path in paths:
source.add_deps(deps)
target_mods = [mod for mod in mods.values() if mod.targets]
for mod in target_mods: class ModuleList:
closed = set() def __init__(self, arch):
children = set(mod.depmods) self.__arch = arch
while children: self.__mods = {}
child = children.pop() self.__used = {}
closed.add(child) self.__targets = frozenset()
child.targets |= mod.targets self.__kinds = frozenset()
children |= {m for m in child.depmods if not m in closed}
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): def generate(self, output):
from pathlib import Path from pathlib import Path
@@ -147,13 +218,16 @@ class Module:
from ninja.ninja_syntax import Writer from ninja.ninja_syntax import Writer
def walk_deps(deps, static, results): def walk_deps(deps, static, results):
for mod in deps: for modname in deps:
if static or mod.name not in results: mod = self.used.get(modname)
results[mod.name] = (mod, static) if not mod: continue # skipped
walk_deps(mod.depmods, static or mod.static, results) 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 = {} all_deps = {}
walk_deps(self.depmods, self.static, all_deps) walk_deps(mod.deps, mod.static, all_deps)
all_deps = all_deps.values() all_deps = all_deps.values()
def gather_phony(build, deps, child_rel): def gather_phony(build, deps, child_rel):
@@ -168,41 +242,41 @@ class Module:
order_only = list(map(mod_rel, deps)), order_only = list(map(mod_rel, deps)),
) )
filename = str(output / f"module.{self.name}.ninja") filename = str(output / f"module.{mod.name}.ninja")
with open(filename, "w") as buildfile: with open(filename, "w") as buildfile:
build = Writer(buildfile) build = Writer(buildfile)
build.comment("This file is automatically generated by bonnibel") build.comment("This file is automatically generated by bonnibel")
build.newline() build.newline()
build.variable("module_dir", target_rel(self.name + ".dir")) build.variable("module_dir", target_rel(mod.name + ".dir"))
build.variable("module_kind", self.kind) build.variable("module_kind", mod.kind)
build.newline() build.newline()
build.include(f"${{target_dir}}/config.{self.kind}.ninja") build.include(f"${{target_dir}}/config.{mod.kind}.ninja")
build.newline() build.newline()
modopts = BuildOptions( modopts = BuildOptions(
local = [self.root, "${module_dir}"], local = [mod.root, "${module_dir}"],
ld_script = self.ld_script and self.root / self.ld_script, ld_script = mod.ld_script and mod.root / mod.ld_script,
) )
if self.public_headers: if mod.public_headers:
modopts.includes += [ modopts.includes += [
self.root / "include", mod.root / "include",
f"${{target_dir}}/{self.name}.dir/include", f"${{target_dir}}/{mod.name}.dir/include",
] ]
for key, value in self.variables.items(): for key, value in mod.variables.items():
build.variable(key, value) build.variable(key, value)
build.newline() build.newline()
for include in self.includes: for include in mod.includes:
p = Path(include) p = Path(include)
if p.is_absolute(): if p.is_absolute():
if not p in modopts.includes: if not p in modopts.includes:
modopts.includes.append(str(p.resolve())) modopts.includes.append(str(p.resolve()))
elif include != ".": elif include != ".":
incpath = self.root / p incpath = mod.root / p
destpath = mod_rel(p) destpath = mod_rel(p)
for header in incpath.rglob("*.h"): for header in incpath.rglob("*.h"):
dest_header = f"{destpath}/" + str(header.relative_to(incpath)) dest_header = f"{destpath}/" + str(header.relative_to(incpath))
@@ -251,7 +325,7 @@ class Module:
header_deps = [] header_deps = []
inputs = [] inputs = []
headers = set(self.public_headers) headers = set(mod.public_headers)
while headers: while headers:
source = headers.pop() source = headers.pop()
headers.update(source.next) headers.update(source.next)
@@ -269,7 +343,8 @@ class Module:
build.newline() build.newline()
inputs = [] inputs = []
sources = set(self.sources) sources = set(mod.sources)
sources.update(set(mod.arch_source.get(self.__arch, tuple())))
while sources: while sources:
source = sources.pop() source = sources.pop()
sources.update(source.next) sources.update(source.next)
@@ -286,40 +361,40 @@ class Module:
gather_phony(build, header_deps, target_rel) gather_phony(build, header_deps, target_rel)
if self.kind == "headers": if mod.kind == "headers":
# Header-only, don't output a build rule # Header-only, don't output a build rule
return continue
output = target_rel(self.get_output()) mod_output = target_rel(mod.get_output())
build.newline() build.newline()
build.build( build.build(
rule = self.kind, rule = mod.kind,
outputs = output, outputs = mod_output,
inputs = inputs, inputs = inputs,
implicit = modopts.implicit, implicit = modopts.implicit,
order_only = modopts.order_only, order_only = modopts.order_only,
variables = {"name": self.name, variables = {"name": mod.name,
"soname": self.get_output()}, "soname": mod.get_output()},
) )
dump = output + ".dump" dump = mod_output + ".dump"
build.newline() build.newline()
build.build( build.build(
rule = "dump", rule = "dump",
outputs = dump, outputs = dump,
inputs = output, inputs = mod_output,
variables = {"name": self.name}, variables = {"name": mod.name},
) )
s_output = target_rel(self.get_output(static=True)) s_output = target_rel(mod.get_output(static=True))
if s_output != output: if s_output != mod_output:
build.newline() build.newline()
build.build( build.build(
rule = self.kind + "_static", rule = mod.kind + "_static",
outputs = s_output, outputs = s_output,
inputs = inputs, inputs = inputs,
order_only = modopts.order_only, order_only = modopts.order_only,
variables = {"name": self.name}, variables = {"name": mod.name},
) )
dump = s_output + ".dump" dump = s_output + ".dump"
@@ -328,25 +403,12 @@ class Module:
rule = "dump", rule = "dump",
outputs = dump, outputs = dump,
inputs = s_output, inputs = s_output,
variables = {"name": self.name}, variables = {"name": mod.name},
) )
if self.default: if mod.default:
build.newline() build.newline()
build.default(output) build.default(mod_output)
build.default(dump) build.default(dump)
def add_input(self, path, **kwargs):
from .source import make_source
s = make_source(self.root, path, **kwargs)
self.sources.append(s)
return s.outputs
def add_depends(self, paths, deps):
for source in self.sources:
if source.path in paths:
source.add_deps(deps)
for source in self.public_headers:
if source.path in paths:
source.add_deps(deps)

View File

@@ -10,22 +10,17 @@ class Project:
def __str__(self): def __str__(self):
return f"{self.name} {self.version.major}.{self.version.minor}.{self.version.patch}-{self.version.sha}" 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 import sys
from os.path import join from os.path import join
from ninja.ninja_syntax import Writer 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 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: with open(output / "build.ninja", "w") as buildfile:
build = Writer(buildfile) build = Writer(buildfile)
default_builds = []
build.comment("This file is automatically generated by bonnibel") build.comment("This file is automatically generated by bonnibel")
build.variable("ninja_required_version", "1.3") build.variable("ninja_required_version", "1.3")
@@ -34,7 +29,7 @@ class Project:
build.variable("build_config", config) build.variable("build_config", config)
build.newline() build.newline()
build.include(root / "assets/build/rules.ninja") build.include(root / "assets" / "build" / arch / "rules.ninja")
build.newline() build.newline()
build.variable("version_major", self.version.major) build.variable("version_major", self.version.major)
@@ -49,7 +44,7 @@ class Project:
]) ])
build.newline() build.newline()
for target in targets: for target in modules.targets:
build.subninja(output / target / "target.ninja") build.subninja(output / target / "target.ninja")
build.newline() build.newline()
@@ -57,7 +52,7 @@ class Project:
rule = "touch", rule = "touch",
outputs = "${build_root}/.all_headers", outputs = "${build_root}/.all_headers",
implicit = [f"${{build_root}}/include/{m.name}/.headers.phony" 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( build.build(
rule = "phony", rule = "phony",
@@ -78,56 +73,24 @@ class Project:
initrdroot = output / "initrd_root" initrdroot = output / "initrd_root"
initrdroot.mkdir(exist_ok=True) initrdroot.mkdir(exist_ok=True)
fatroot_content = [] image_content = {'initrd_root': [], 'fatroot': []}
initrd_content = []
def add_fatroot(source, name): def add_image_content(image, path, name):
output = join(manifest.location, name) output = join(path, name)
fatroot_output = f"${{build_root}}/fatroot/{output}" image_output = f"${{build_root}}/{image}/{output}"
build.build( build.build(
rule = "cp", rule = "cp",
outputs = [fatroot_output], outputs = [image_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],
inputs = [f"${{build_root}}/{name}"], inputs = [f"${{build_root}}/{name}"],
variables = { variables = {
"description": f"Installing {name}", "description": f"Installing {name}",
}) })
initrd_content.append(initrd_output) image_content[image].append(image_output)
build.newline() 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}" input_path = f"${{build_root}}/{entry.target}/{entry.output}"
intermediary = f"${{build_root}}/{entry.output}" intermediary = f"${{build_root}}/{entry.output}"
@@ -141,21 +104,15 @@ class Project:
"debug": f"${{build_root}}/.debug/{entry.output}.debug", "debug": f"${{build_root}}/.debug/{entry.output}.debug",
}) })
add_initrd_content(root, entry.output) build.build(
rule = "phony",
outputs = [entry.output],
inputs = [intermediary])
add_fatroot_exe(manifest.kernel) add_image_content(image, path, entry.output)
add_fatroot_exe(manifest.init)
for program in manifest.panics:
add_fatroot_exe(program)
for program in manifest.services: if 'kernel' in modules.used:
add_initrd_stripped("jsix/services", program) add_image_content_exe("fatroot", manifest.location, manifest.kernel)
for program in manifest.drivers:
add_initrd_stripped("jsix/drivers", program)
for program in manifest.libs:
add_initrd_stripped("jsix/lib", program)
syms = manifest.add_data("symbol_table.dat", syms = manifest.add_data("symbol_table.dat",
"Symbol table", ("symbols",)) "Symbol table", ("symbols",))
@@ -168,9 +125,35 @@ class Project:
outputs = [syms_out], outputs = [syms_out],
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"], inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
) )
fatroot_content.append(syms_out) image_content['fatroot'].append(syms_out)
manifest.symbols = syms_file 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")
for program in manifest.panics:
try_add_manifest_module(program, "panic", "fatroot")
for program in manifest.services:
try_add_manifest_module(program, "services", "initrd_root", "jsix/services")
for program in manifest.drivers:
try_add_manifest_module(program, "drivers", "initrd_root", "jsix/drivers")
for program in manifest.libs:
try_add_manifest_module(program, "libs", "initrd_root", "jsix/lib")
extra_regen_outputs = []
if 'boot' in modules.used:
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi" bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
build.build( build.build(
rule = "cp", rule = "cp",
@@ -183,24 +166,25 @@ class Project:
boot_config = join(fatroot, "jsix", "boot.conf") boot_config = join(fatroot, "jsix", "boot.conf")
manifest.write_boot_config(boot_config) manifest.write_boot_config(boot_config)
extra_regen_outputs.append(boot_config)
initrd = str(fatroot / manifest.location / manifest.initrd["name"]) initrd = str(fatroot / manifest.location / manifest.initrd["name"])
build.build( build.build(
rule = "makeinitrd", rule = "makeinitrd",
outputs = [initrd], outputs = [initrd],
inputs = [str(initrdroot)], inputs = [str(initrdroot)],
implicit = initrd_content + ["${source_root}/scripts/mkj6romfs.py"], implicit = image_content['initrd_root'] + ["${source_root}/scripts/mkj6romfs.py"],
variables = {"format": manifest.initrd["format"]}, variables = {"format": manifest.initrd["format"]},
) )
build.newline() build.newline()
fatroot_content.append(initrd) image_content['fatroot'].append(initrd)
build.build( build.build(
rule = "makefat", rule = "makefat",
outputs = ["${build_root}/jsix.img"], outputs = ["${build_root}/jsix.img"],
inputs = ["${source_root}/assets/diskbase.img"], inputs = ["${source_root}/assets/diskbase.img"],
implicit = fatroot_content + [bootloader], implicit = image_content['fatroot'] + [bootloader],
variables = {"name": "jsix.img"}, variables = {"name": "jsix.img"},
) )
build.newline() build.newline()
@@ -219,8 +203,8 @@ class Project:
inputs = [str(p)], inputs = [str(p)],
variables = {"name": name}, variables = {"name": name},
) )
build.default([out])
build.newline() build.newline()
default_builds.append(out)
compdb = "${source_root}/compile_commands.json" compdb = "${source_root}/compile_commands.json"
@@ -233,7 +217,7 @@ class Project:
regen_implicits = \ regen_implicits = \
[f"{self.root}/configure", str(manifest_file)] + \ [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)) regen_implicits += list(map(str, config_deps))
@@ -242,24 +226,24 @@ class Project:
outputs = [compdb], outputs = [compdb],
implicit = regen_implicits, implicit = regen_implicits,
) )
build.default([compdb])
build.newline() build.newline()
default_builds.append(compdb)
build.build( build.build(
rule = "regen", rule = "regen",
outputs = ['build.ninja'], outputs = ['build.ninja'],
implicit = regen_implicits, implicit = regen_implicits,
implicit_outputs = implicit_outputs =
[f"module.{mod.name}.ninja" for mod in modules.values()] + [f"module.{mod.name}.ninja" for mod in modules.used.values()] +
[f"{target}/target.ninja" for target in targets] + [f"{target}/target.ninja" for target in modules.targets] +
[boot_config], extra_regen_outputs,
) )
build.newline() build.newline()
build.default(["${build_root}/jsix.img"]) build.default(default_builds)
for target in targets: for target in modules.targets:
mods = [m.name for m in modules.values() if target in m.targets] mods = modules.target_mods(target)
targetdir = output / target targetdir = output / target
targetdir.mkdir(exist_ok=True) targetdir.mkdir(exist_ok=True)
@@ -290,4 +274,4 @@ class Project:
build.newline() build.newline()
for mod in mods: for mod in mods:
build.subninja(f"module.{mod}.ninja") build.subninja(f"module.{mod.name}.ninja")

View 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")

View File

@@ -1,18 +1,5 @@
import cog 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_widths = (8, 16, 32, 64)
int_mods = ("fast", "least") int_mods = ("fast", "least")

View File

@@ -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: class Layout:
from collections import namedtuple 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} sizes = {'G': 1024 ** 3, 'T': 1024 ** 4}
@@ -26,7 +38,7 @@ class Layout:
for r in data: for r in data:
size = Layout.get_size(r["size"]) size = Layout.get_size(r["size"])
addr -= 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))) r.get("shared", False)))
self.regions = tuple(regions) self.regions = tuple(regions)

View File

@@ -104,7 +104,7 @@ def add_dir(path, files, dirs, inode_data, dir_inodes, output, compress):
compressed = uncompressed compressed = uncompressed
comp_size = uncomp_size comp_size = uncomp_size
output.write(uncompressed) output.write(compressed)
inode_data.append((inode_type_dir, offset, comp_size, uncomp_size)) inode_data.append((inode_type_dir, offset, comp_size, uncomp_size))

View File

@@ -3,9 +3,10 @@
boot = module("boot", boot = module("boot",
kind = "exe", kind = "exe",
outfile = "boot.efi", outfile = "boot.efi",
targets = [ "boot" ], target = "boot",
deps = [ "cpu", "elf", "util", "bootproto" ], deps = [ "cpu", "elf", "util", "bootproto" ],
static = True, static = True,
skip_arches = [ "linux" ],
sources = [ sources = [
"allocator.cpp", "allocator.cpp",
"bootconfig.cpp", "bootconfig.cpp",

View File

@@ -22,7 +22,7 @@ read_string(util::buffer &data)
static void static void
read_descriptor(descriptor &e, util::buffer &data) 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); e.path = read_string(data);
} }

View File

@@ -3,6 +3,7 @@
#pragma once #pragma once
#include <bootproto/bootconfig.h> #include <bootproto/bootconfig.h>
#include <util/bitset.h>
#include <util/counted.h> #include <util/counted.h>
namespace uefi { namespace uefi {
@@ -14,7 +15,7 @@ namespace boot {
using desc_flags = bootproto::desc_flags; using desc_flags = bootproto::desc_flags;
struct descriptor { struct descriptor {
desc_flags flags; util::bitset16 flags;
wchar_t const *path; wchar_t const *path;
}; };
@@ -27,7 +28,7 @@ public:
/// Constructor. Loads bootconfig from the given buffer. /// Constructor. Loads bootconfig from the given buffer.
bootconfig(util::buffer data, uefi::boot_services *bs); 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 & kernel() const { return m_kernel; }
inline const descriptor & init() const { return m_init; } inline const descriptor & init() const { return m_init; }
inline const wchar_t * initrd() const { return m_initrd; } inline const wchar_t * initrd() const { return m_initrd; }
@@ -35,7 +36,7 @@ public:
inline const descriptors & panics() { return m_panics; } inline const descriptors & panics() { return m_panics; }
private: private:
uint16_t m_flags; util::bitset16 m_flags;
descriptor m_kernel; descriptor m_kernel;
descriptor m_init; descriptor m_init;
descriptors m_panics; descriptors m_panics;

View File

@@ -110,7 +110,7 @@ parse_program(const wchar_t *name, util::const_buffer data, bootproto::program &
section.phys_addr = elf.base() + seg.offset; section.phys_addr = elf.base() + seg.offset;
section.virt_addr = seg.vaddr; section.virt_addr = seg.vaddr;
section.size = seg.mem_size; 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) if (seg.mem_size != seg.file_size)
section.phys_addr = allocate_bss(seg); section.phys_addr = allocate_bss(seg);
@@ -128,8 +128,6 @@ load_program(
paging::pager &pager, paging::pager &pager,
bool verify) bool verify)
{ {
using util::bits::has;
status_line status(L"Loading program", name); status_line status(L"Loading program", name);
elf::file elf {data}; elf::file elf {data};
@@ -155,8 +153,8 @@ load_program(
pager.map_pages(phys_addr, seg.vaddr, pager.map_pages(phys_addr, seg.vaddr,
memory::bytes_to_pages(seg.mem_size), memory::bytes_to_pages(seg.mem_size),
has(seg.flags, elf::segment_flags::write), seg.flags.get(elf::segment_flags::write),
has(seg.flags, elf::segment_flags::exec)); seg.flags.get(elf::segment_flags::exec));
} }
return elf.entrypoint(); return elf.entrypoint();

View File

@@ -82,9 +82,8 @@ load_resources(
util::buffer kernel = loader::load_file(disk, bc.kernel().path); util::buffer kernel = loader::load_file(disk, bc.kernel().path);
uintptr_t kentry = loader::load_program(kernel, L"jsix kernel", pager, true); 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; using bootproto::desc_flags;
bool has_panic = false; bool has_panic = false;
@@ -96,7 +95,7 @@ load_resources(
// Find the screen-specific panic handler first to // Find the screen-specific panic handler first to
// give it priority // give it priority
for (const descriptor &d : bc.panics()) { 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); panic = loader::load_file(disk, d.path);
has_panic = true; has_panic = true;
break; break;
@@ -106,7 +105,7 @@ load_resources(
if (!has_panic) { if (!has_panic) {
for (const descriptor &d : bc.panics()) { 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); panic = loader::load_file(disk, d.path);
has_panic = true; has_panic = true;
break; break;

View File

@@ -71,21 +71,19 @@ lapic::get_id()
} }
void 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 // Wait until the APIC is ready to send
ipi_wait(); ipi_wait();
uint32_t command = uint32_t command = util::bitset32::from(vector) | mode;
static_cast<uint32_t>(vector) |
static_cast<uint32_t>(mode);
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24); apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
apic_write(m_base, lapic_icr_low, command); apic_write(m_base, lapic_icr_low, command);
} }
void 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 // Wait until the APIC is ready to send
ipi_wait(); ipi_wait();

View File

@@ -3,7 +3,7 @@
/// Classes to control both local and I/O APICs. /// Classes to control both local and I/O APICs.
#include <stdint.h> #include <stdint.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
#include "interrupts.h" #include "interrupts.h"
@@ -33,33 +33,30 @@ public:
/// Get the local APIC's ID /// Get the local APIC's ID
uint8_t get_id(); uint8_t get_id();
enum class ipi : uint32_t enum class ipi_flags
{ {
// Delivery modes logical = 11,
fixed = 0x0000, pending = 12,
smi = 0x0200, assert = 14,
nmi = 0x0400, level = 15,
init = 0x0500,
startup = 0x0600,
// Flags
deassert = 0x0000,
assert = 0x4000,
edge = 0x0000, ///< edge-triggered
level = 0x8000, ///< level-triggered
}; };
// 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. /// Send an inter-processor interrupt.
/// \arg mode The sending mode /// \arg mode The sending mode
/// \arg vector The interrupt vector /// \arg vector The interrupt vector
/// \arg dest The APIC ID of the destination /// \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 /// Send an inter-processor broadcast interrupt to all other CPUs
/// \arg mode The sending mode /// \arg mode The sending mode
/// \arg self If true, include this CPU in the broadcast /// \arg self If true, include this CPU in the broadcast
/// \arg vector The interrupt vector /// \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 /// Wait for an IPI to finish sending. This is done automatically
/// before sending another IPI with send_ipi(). /// before sending another IPI with send_ipi().
@@ -145,6 +142,3 @@ private:
uint8_t m_id; uint8_t m_id;
uint8_t m_version; uint8_t m_version;
}; };
is_bitfield(lapic::ipi);

View File

@@ -118,7 +118,7 @@ cpu_early_init(cpu_data *cpu)
set_xcr0(xcr0_val); set_xcr0(xcr0_val);
// Set initial floating point state // Set initial floating point state
const util::bitset32 mxcsr_val { const util::bitset32 mxcsr_val = util::bitset32::of(
mxcsr::DAZ, mxcsr::DAZ,
mxcsr::IM, mxcsr::IM,
mxcsr::DM, mxcsr::DM,
@@ -126,8 +126,7 @@ cpu_early_init(cpu_data *cpu)
mxcsr::OM, mxcsr::OM,
mxcsr::UM, mxcsr::UM,
mxcsr::PM, mxcsr::PM,
mxcsr::FTZ, mxcsr::FTZ);
};
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) ); asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
// Install the GS base pointint to the cpu_data // Install the GS base pointint to the cpu_data

View File

@@ -44,7 +44,7 @@ void
output(j6_log_entry *entry) output(j6_log_entry *entry)
{ {
char buffer [256]; 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], level_colors[entry->severity],
area_names[entry->area], area_names[entry->area],
level_names[entry->severity], level_names[entry->severity],

View File

@@ -2,7 +2,7 @@
#include <stdint.h> #include <stdint.h>
#include <util/misc.h> // for checksum #include <util/misc.h> // for checksum
#include <util/pointers.h> #include <util/pointers.h>
#include <arch/acpi/tables.h> #include <acpi/tables.h>
#include "kassert.h" #include "kassert.h"
#include "apic.h" #include "apic.h"

View File

@@ -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; static util::no_construct<GDT> __g_bsp_gdt_storage;
GDT &g_bsp_gdt = __g_bsp_gdt_storage.value; 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) : GDT::GDT(TSS *tss) :
m_tss(tss) m_tss(tss)
@@ -32,13 +35,13 @@ GDT::GDT(TSS *tss) :
m_ptr.base = &m_entries[0]; m_ptr.base = &m_entries[0];
// Kernel CS/SS - always 64bit // Kernel CS/SS - always 64bit
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute); set(kern_cs_index, 0, 0xfffff, true, rwx);
set(kern_ss_index, 0, 0xfffff, true, type::read_write); set(kern_ss_index, 0, 0xfffff, true, rw);
// User CS32/SS/CS64 - layout expected by SYSRET // User CS32/SS/CS64 - layout expected by SYSRET
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute); set(user_cs32_index, 0, 0xfffff, false, ring3 | rwx);
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write); set(user_ss_index, 0, 0xfffff, true, ring3 | rw);
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute); set(user_cs64_index, 0, 0xfffff, true, ring3 | rwx);
set_tss(tss); set_tss(tss);
} }
@@ -61,7 +64,7 @@ GDT::install() const
} }
void 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].limit_low = limit & 0xffff;
m_entries[i].size = (limit >> 16) & 0xf; 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_mid = (base >> 16) & 0xff;
m_entries[i].base_high = (base >> 24) & 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 struct tss_descriptor
@@ -79,7 +84,7 @@ struct tss_descriptor
uint16_t limit_low; uint16_t limit_low;
uint16_t base_00; uint16_t base_00;
uint8_t base_16; uint8_t base_16;
GDT::type type; util::bitset8 type;
uint8_t size; uint8_t size;
uint8_t base_24; uint8_t base_24;
uint32_t base_32; uint32_t base_32;
@@ -102,11 +107,9 @@ GDT::set_tss(TSS *tss)
tssd.base_32 = (base >> 32) & 0xffffffff; tssd.base_32 = (base >> 32) & 0xffffffff;
tssd.reserved = 0; tssd.reserved = 0;
tssd.type = static constexpr util::bitset8 tss_mark =
type::accessed | util::bitset8::of(type::accessed, type::execute, type::present);
type::execute | tssd.type = ring3 | tss_mark;
type::ring3 |
type::present;
memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor)); memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
} }

View File

@@ -3,7 +3,7 @@
/// Definitions relating to a CPU's GDT table /// Definitions relating to a CPU's GDT table
#include <stdint.h> #include <stdint.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
class TSS; class TSS;
@@ -25,21 +25,20 @@ public:
/// \arg index Which entry to print, or -1 for all entries /// \arg index Which entry to print, or -1 for all entries
void dump(unsigned index = -1) const; void dump(unsigned index = -1) const;
enum class type : uint8_t enum class type
{ {
accessed = 0x01, accessed,
read_write = 0x02, read_write,
conforming = 0x04, conforming,
execute = 0x08, execute,
system = 0x10, system,
ring1 = 0x20, ring1,
ring2 = 0x40, ring2,
ring3 = 0x60, present
present = 0x80
}; };
private: 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); void set_tss(TSS *tss);
struct descriptor struct descriptor
@@ -47,7 +46,7 @@ private:
uint16_t limit_low; uint16_t limit_low;
uint16_t base_low; uint16_t base_low;
uint8_t base_mid; uint8_t base_mid;
type type; util::bitset8 type;
uint8_t size; uint8_t size;
uint8_t base_high; uint8_t base_high;
} __attribute__ ((packed, align(8))); } __attribute__ ((packed, align(8)));
@@ -63,5 +62,3 @@ private:
ptr m_ptr; ptr m_ptr;
}; };
is_bitfield(GDT::type);

View File

@@ -117,4 +117,5 @@ ISR (0xe1, 0, isrLINT0)
ISR (0xe2, 0, isrLINT1) ISR (0xe2, 0, isrLINT1)
ISR (0xe3, 0, isrAPICError) ISR (0xe3, 0, isrAPICError)
ISR (0xe4, 0, ipiSchedule) ISR (0xe4, 0, ipiSchedule)
ISR (0xe5, 0, ipiShootdown)

View File

@@ -21,6 +21,7 @@ constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + mem::linear_offset;
extern "C" { extern "C" {
void isr_handler(cpu_state*); void isr_handler(cpu_state*);
void irq_handler(cpu_state*); void irq_handler(cpu_state*);
void _reload_cr3();
} }
uint8_t uint8_t
@@ -123,9 +124,10 @@ isr_handler(cpu_state *regs)
uintptr_t cr2 = 0; uintptr_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
// The zero page is always invalid
if (cr2 > mem::frame_size) {
bool user = cr2 < mem::kernel_offset; bool user = cr2 < mem::kernel_offset;
vm_space::fault_type ft = util::bitset8 ft = regs->errorcode;
static_cast<vm_space::fault_type>(regs->errorcode);
vm_space &space = user vm_space &space = user
? obj::process::current().space() ? obj::process::current().space()
@@ -133,6 +135,7 @@ isr_handler(cpu_state *regs)
if (cr2 && space.handle_fault(cr2, ft)) if (cr2 && space.handle_fault(cr2, ft))
break; break;
}
util::format({message, sizeof(message)}, util::format({message, sizeof(message)},
"Page fault: %016lx%s%s%s%s%s", cr2, "Page fault: %016lx%s%s%s%s%s", cr2,
@@ -185,6 +188,11 @@ isr_handler(cpu_state *regs)
scheduler::get().schedule(); scheduler::get().schedule();
break; break;
case isr::ipiShootdown:
// TODO: Real shootdown algorithm
_reload_cr3();
break;
default: default:
util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt); util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt);
kassert(false, message, regs); kassert(false, message, regs);

View File

@@ -1,49 +1,142 @@
#include <j6/memutils.h> #include <j6/memutils.h>
#include <util/basic_types.h> #include <util/basic_types.h>
#include "kassert.h"
#include "ipc_message.h" #include "ipc_message.h"
#include "j6/types.h"
namespace ipc { 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( message::message(
uint64_t in_tag, uint64_t in_tag,
const util::buffer &in_data, const util::buffer &in_data,
const util::counted<j6_handle_t> &in_handles) : 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) { set(in_tag, in_data, in_handles);
data.pointer = new uint8_t [data.count]; }
memcpy(data.pointer, in_data.pointer, data.count);
}
message::message(message &&other) {
if (handles.count) { *this = util::move(other);
handles.pointer = new j6_handle_t [handles.count];
memcpy(handles.pointer, in_handles.pointer, handles.count * sizeof(j6_handle_t));
}
} }
message::message(message &&other) { *this = util::move(other); }
message::~message() message::~message()
{ {
delete [] reinterpret_cast<uint8_t*>(data.pointer); clear_oob();
delete [] handles.pointer;
} }
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 &
message::operator=(message &&other) message::operator=(message &&other)
{ {
clear_oob();
tag = other.tag; tag = other.tag;
other.tag = 0; other.tag = 0;
data = other.data; data_size = other.data_size;
other.data = {nullptr, 0}; other.data_size = 0;
handles = other.handles; handle_count = other.handle_count;
other.handles = {nullptr, 0}; other.handle_count = 0;
out_of_band = other.out_of_band;
other.out_of_band = 0;
memcpy(content, other.content, sizeof(content));
return *this; return *this;
} }
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 } // namespace ipc

View File

@@ -5,14 +5,29 @@
#include <stdint.h> #include <stdint.h>
#include <j6/types.h> #include <j6/types.h>
#include <util/counted.h> #include <util/counted.h>
#include <util/pointers.h>
namespace ipc { namespace ipc {
static constexpr size_t message_size = 64;
struct message struct message
{ {
uint64_t tag; uint64_t tag;
util::buffer data; uint16_t data_size;
util::counted<j6_handle_t> handles;
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();
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles); 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();
message & operator=(message &&other); 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();
}; };
using message_ptr = util::unique_ptr<message>;
} // namespace ipc } // namespace ipc

View File

@@ -3,11 +3,12 @@
kernel = module("kernel", kernel = module("kernel",
default = True, default = True,
basename = "jsix", basename = "jsix",
targets = [ "kernel" ], target = "kernel",
description = "jsix kernel", description = "jsix kernel",
deps = [ "util", "cpu", "bootproto", "j6" ], deps = [ "util", "cpu", "bootproto", "j6", "acpi" ],
static = True, static = True,
ld_script = "kernel.ld", ld_script = "kernel.ld",
skip_arches = [ "linux" ],
sources = [ sources = [
"apic.cpp", "apic.cpp",
"kassert.cpp", "kassert.cpp",

View File

@@ -1,4 +1,3 @@
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <j6/memutils.h> #include <j6/memutils.h>
@@ -54,7 +53,7 @@ kernel_main(bootproto::args *args)
bsp_late_init(); bsp_late_init();
using bootproto::boot_flags; 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); syscall_initialize(enable_test);
device_manager &devices = device_manager::get(); 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 bootproto::section_flags;
using obj::vm_flags; using obj::vm_flags;
obj::process *p = new obj::process; obj::process *p = new obj::process {"srv.init"};
j6_handle_t sys_handle = j6_handle_t sys_handle =
g_cap_table.create(&obj::system::get(), obj::system::init_caps); 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(); vm_space &space = p->space();
for (const auto &sect : program.sections) { for (const auto &sect : program.sections) {
vm_flags flags = util::bitset32 flags = util::bitset32::of(vm_flags::exact);
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) | if (sect.type.get(section_flags::execute))
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none); 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); 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); uint64_t iopl = (3ull << 12);

View File

@@ -4,8 +4,6 @@
#include "kassert.h" #include "kassert.h"
#include "logger.h" #include "logger.h"
#include "objects/system.h"
#include "objects/thread.h"
// The logger is initialized _before_ global constructors are called, // The logger is initialized _before_ global constructors are called,
// so that we can start log output immediately. Keep its constructor // 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(warn);
LOG_LEVEL_FUNCTION(error); LOG_LEVEL_FUNCTION(error);
void fatal(logs area, const char *fmt, ...) void
fatal(logs area, const char *fmt, ...)
{ {
logger *l = logger::s_log; logger *l = logger::s_log;
if (!l) return; if (!l) return;
@@ -137,4 +136,19 @@ void fatal(logs area, const char *fmt, ...)
kassert(false, "log::fatal"); 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 } // namespace log

View File

@@ -5,10 +5,11 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <j6/types.h>
#include <util/counted.h> #include <util/counted.h>
#include <util/spinlock.h> #include <util/spinlock.h>
#include "objects/event.h" #include "wait_queue.h"
enum class logs : uint8_t { enum class logs : uint8_t {
#define LOG(name, lvl) name, #define LOG(name, lvl) name,
@@ -77,6 +78,7 @@ private:
friend void warn (logs area, const char *fmt, ...); friend void warn (logs area, const char *fmt, ...);
friend void error (logs area, const char *fmt, ...); friend void error (logs area, const char *fmt, ...);
friend void fatal (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); 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 error (logs area, const char *fmt, ...);
void fatal (logs area, const char *fmt, ...); void fatal (logs area, const char *fmt, ...);
void log (logs area, level severity, const char *fmt, ...);
} // namespace log } // namespace log
extern log::logger &g_logger; extern log::logger &g_logger;

View File

@@ -20,7 +20,8 @@ extern "C" {
using bootproto::allocation_register; 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, // These objects are initialized _before_ global constructors are called,
// so we don't want them to have global constructors at all, lest they // 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::buffers_offset,
mem::kernel_buffer_pages, mem::kernel_buffer_pages,
mem::buffers_size, 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); }
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 // Create the heap space and heap allocator
obj::vm_area *heap = new (&g_kernel_heap_area) 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 *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::heap_offset, heap, vm_flag_exact);
vm.add(mem::heapmap_offset, heap_map, vm_flags::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}; new (&g_kernel_heap) heap_allocator {mem::heap_offset, mem::heap_size, mem::heapmap_offset};
// Set up the log area and logger // Set up the log area and logger
size_t log_buffer_size = log::log_pages * arch::frame_size; size_t log_buffer_size = log::log_pages * arch::frame_size;
obj::vm_area *logs = new (&g_kernel_log_area) obj::vm_area *logs = new (&g_kernel_log_area)
obj::vm_area_ring(log_buffer_size, vm_flags::write); obj::vm_area_ring(log_buffer_size, vm_flag_write);
vm.add(mem::logs_offset, logs, vm_flags::exact); vm.add(mem::logs_offset, logs, vm_flag_exact);
new (&g_logger) log::logger( new (&g_logger) log::logger(
util::buffer::from(mem::logs_offset, log_buffer_size)); util::buffer::from(mem::logs_offset, log_buffer_size));
// Set up the capability tables // Set up the capability tables
obj::vm_area *caps = new (&g_cap_table_area) 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}; new (&g_cap_table) cap_table {mem::caps_offset};
@@ -128,8 +129,8 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
mem::stacks_offset, mem::stacks_offset,
mem::kernel_stack_pages, mem::kernel_stack_pages,
mem::stacks_size, mem::stacks_size,
vm_flags::write}; vm_flag_write};
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flags::exact); vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flag_exact);
// Clean out any remaning bootloader page table entries // Clean out any remaning bootloader page table entries
for (unsigned i = 0; i < arch::kernel_root_index; ++i) for (unsigned i = 0; i < arch::kernel_root_index; ++i)
@@ -140,7 +141,7 @@ void
memory_initialize_post_ctors(bootproto::args &kargs) memory_initialize_post_ctors(bootproto::args &kargs)
{ {
vm_space &vm = vm_space::kernel_space(); 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( g_frame_allocator.free(
get_physical_page(kargs.page_tables.pointer), get_physical_page(kargs.page_tables.pointer),

View File

@@ -2,6 +2,7 @@
#include <util/counted.h> #include <util/counted.h>
#include <j6/memutils.h> #include <j6/memutils.h>
#include "logger.h"
#include "objects/mailbox.h" #include "objects/mailbox.h"
#include "objects/thread.h" #include "objects/thread.h"
@@ -26,6 +27,8 @@ mailbox::close()
bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL); bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL);
if (was_closed) return; if (was_closed) return;
log::spam(logs::ipc, "mbx[%2x] closing...", obj_id());
m_callers.clear(j6_status_closed); m_callers.clear(j6_status_closed);
m_responders.clear(j6_status_closed); m_responders.clear(j6_status_closed);
@@ -44,14 +47,20 @@ mailbox::call()
m_callers.add_thread(&current); m_callers.add_thread(&current);
thread *responder = m_responders.pop_next(); 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); 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(); return current.block();
} }
j6_status_t 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()) if (closed())
return j6_status_closed; return j6_status_closed;
@@ -67,6 +76,9 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
if (!block) if (!block)
return j6_status_would_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(&current); m_responders.add_thread(&current);
j6_status_t s = current.block(); j6_status_t s = current.block();
if (s != j6_status_ok) 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 }); m_reply_map.insert({ reply_tag, caller });
lock.release(); 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(); data = caller->get_message_data();
return j6_status_ok; return j6_status_ok;
} }
j6_status_t 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()) if (closed())
return j6_status_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); m_reply_map.erase(reply_tag);
lock.release(); lock.release();
thread &current = 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->set_message_data(util::move(data));
caller->wake(j6_status_ok); caller->wake(j6_status_ok);
return j6_status_ok; return j6_status_ok;

View File

@@ -22,7 +22,6 @@ class mailbox :
public kobject public kobject
{ {
public: public:
using reply_tag_t = uint64_t; using reply_tag_t = uint64_t;
/// Capabilities on a newly constructed mailbox handle /// Capabilities on a newly constructed mailbox handle
@@ -48,18 +47,18 @@ public:
j6_status_t call(); j6_status_t call();
/// Receive the next available message, optionally blocking if no messages are available. /// 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 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. /// \arg block True if this call should block when no messages are available.
/// \returns j6_status_ok if a message was received /// \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 /// Find a given pending message to be responded to. Returns a replyer object, which will
/// wake the calling read upon destruction. /// wake the calling read upon destruction.
/// \arg reply_tag The reply tag in the original message /// \arg reply_tag The reply tag in the original message
/// \arg data Message data to pass on to the caller /// \arg data Message data to pass on to the caller
/// \returns j6_status_ok if the reply was successfully sent /// \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: private:
wait_queue m_callers; wait_queue m_callers;

View File

@@ -1,3 +1,4 @@
#include <j6/memutils.h>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "kassert.h" #include "kassert.h"
@@ -18,10 +19,18 @@ obj::process &g_kernel_process = __g_kernel_process_storage.value;
namespace obj { namespace obj {
process::process() : process::process(const char *name) :
kobject {kobject::type::process}, kobject {kobject::type::process},
m_state {state::running} 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 // The "kernel process"-only constructor
@@ -30,6 +39,11 @@ process::process(page_table *kpml4) :
m_space {kpml4}, m_space {kpml4},
m_state {state::running} 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() process::~process()
@@ -46,7 +60,7 @@ process::create_kernel_process(page_table *pml4)
} }
void void
process::exit(int32_t code) process::exit(int64_t code)
{ {
if (m_state == state::exited) if (m_state == state::exited)
return; return;

View File

@@ -13,6 +13,14 @@
namespace obj { namespace obj {
static constexpr bool __use_process_names =
#ifdef __jsix_config_debug
true;
#else
false;
#endif
class process : class process :
public kobject public kobject
{ {
@@ -32,7 +40,7 @@ public:
static constexpr kobject::type type = kobject::type::process; static constexpr kobject::type type = kobject::type::process;
/// Constructor. /// Constructor.
process(); process(const char *name);
/// Destructor. /// Destructor.
virtual ~process(); virtual ~process();
@@ -42,11 +50,14 @@ public:
/// Terminate this process. /// Terminate this process.
/// \arg code The return code to exit with. /// \arg code The return code to exit with.
void exit(int32_t code); void exit(int64_t code);
/// Get the process' virtual memory space /// Get the process' virtual memory space
vm_space & space() { return m_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 /// Create a new thread in this process
/// \args rsp3 If non-zero, sets the ring3 stack pointer to this value /// \args rsp3 If non-zero, sets the ring3 stack pointer to this value
/// \args priority The new thread's scheduling priority /// \args priority The new thread's scheduling priority
@@ -95,7 +106,7 @@ private:
// This constructor is called by create_kernel_process // This constructor is called by create_kernel_process
process(page_table *kpml4); process(page_table *kpml4);
int32_t m_return_code; int64_t m_return_code;
vm_space m_space; vm_space m_space;
@@ -107,6 +118,15 @@ private:
enum class state : uint8_t { running, exited }; enum class state : uint8_t { running, exited };
state m_state; 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 } // namespace obj

View File

@@ -111,9 +111,6 @@ thread::wake_only()
set_state(state::ready); 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 void
thread::exit() thread::exit()
{ {

View File

@@ -3,7 +3,6 @@
/// Definition of thread kobject types /// Definition of thread kobject types
#include <j6/cap_flags.h> #include <j6/cap_flags.h>
#include <util/enum_bitfields.h>
#include <util/linked_list.h> #include <util/linked_list.h>
#include <util/spinlock.h> #include <util/spinlock.h>
@@ -122,8 +121,8 @@ public:
/// \returns The clock time at which to wake. 0 for no timeout. /// \returns The clock time at which to wake. 0 for no timeout.
inline uint64_t wake_timeout() const { return m_wake_timeout; } inline uint64_t wake_timeout() const { return m_wake_timeout; }
void set_message_data(ipc::message &&md); inline void set_message_data(ipc::message_ptr md) { m_message = util::move(md); }
ipc::message && get_message_data(); inline ipc::message_ptr get_message_data() { return util::move(m_message); }
inline bool has_state(state s) const { inline bool has_state(state s) const {
return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) & 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_value;
uint64_t m_wake_timeout; uint64_t m_wake_timeout;
ipc::message m_message; ipc::message_ptr m_message;
wait_queue m_join_queue; wait_queue m_join_queue;
}; };

View File

@@ -9,8 +9,8 @@ namespace obj {
using mem::frame_size; using mem::frame_size;
vm_area::vm_area(size_t size, vm_flags flags) : vm_area::vm_area(size_t size, util::bitset32 flags) :
m_size {size}, m_size {mem::page_count(size) * mem::frame_size},
m_flags {flags}, m_flags {flags},
m_spaces {m_vector_static, 0, static_size}, m_spaces {m_vector_static, 0, static_size},
kobject {kobject::type::vma} kobject {kobject::type::vma}
@@ -34,6 +34,10 @@ void
vm_area::remove_from(vm_space *space) vm_area::remove_from(vm_space *space)
{ {
m_spaces.remove_swap(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()) if (!m_spaces.count() && !handle_count())
delete this; delete this;
} }
@@ -62,7 +66,7 @@ vm_area::can_resize(size_t size)
return true; 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}, m_start {start},
vm_area {size, flags} 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() vm_area_fixed::~vm_area_fixed()
{ {
if (m_flags && vm_flags::mmio) if (m_flags.get(vm_flags::mmio))
return; return;
size_t pages = mem::page_count(m_size); 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; 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} 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}, m_mapped {nullptr},
vm_area {size, flags} 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_pages {buf_pages + 1}, // Sections are N+1 pages for the leading guard page
m_stacks {start, m_pages*mem::frame_size}, m_stacks {start, m_pages*mem::frame_size},
vm_area_open {size, flags} 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); 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}, vm_area_open {size * 2, flags},
m_bufsize {size} m_bufsize {size}
{ {

View File

@@ -6,8 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <j6/cap_flags.h> #include <j6/cap_flags.h>
#include <util/bitset.h>
#include <util/vector.h> #include <util/vector.h>
#include <util/enum_bitfields.h>
#include "block_allocator.h" #include "block_allocator.h"
#include "objects/kobject.h" #include "objects/kobject.h"
@@ -17,16 +17,15 @@ class vm_space;
namespace obj { namespace obj {
enum class vm_flags : uint32_t enum class vm_flags
{ {
#define VM_FLAG(name, v) name = v, #define VM_FLAG(name, v) name = v,
#include <j6/tables/vm_flags.inc> #include <j6/tables/vm_flags.inc>
#undef VM_FLAG #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 /// Virtual memory areas allow control over memory allocation
class vm_area : class vm_area :
@@ -40,7 +39,7 @@ public:
/// Constructor. /// Constructor.
/// \arg size Initial virtual size of the memory area /// \arg size Initial virtual size of the memory area
/// \arg flags Flags for this 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(); virtual ~vm_area();
@@ -48,7 +47,7 @@ public:
inline size_t size() const { return m_size; } inline size_t size() const { return m_size; }
/// Get the flags set for this area /// 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 /// Track that this area was added to a vm_space
/// \arg space The space to add this area to /// \arg space The space to add this area to
@@ -83,7 +82,7 @@ protected:
bool can_resize(size_t size); bool can_resize(size_t size);
size_t m_size; size_t m_size;
vm_flags m_flags; util::bitset32 m_flags;
util::vector<vm_space*> m_spaces; util::vector<vm_space*> m_spaces;
// Initial static space for m_spaces - most areas will never grow // 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 start Starting physical address of this area
/// \arg size Size of the physical memory area /// \arg size Size of the physical memory area
/// \arg flags Flags for this 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 ~vm_area_fixed();
virtual size_t resize(size_t size) override; virtual size_t resize(size_t size) override;
@@ -122,7 +121,7 @@ public:
/// Constructor. /// Constructor.
/// \arg size Initial virtual size of the memory area /// \arg size Initial virtual size of the memory area
/// \arg flags Flags for this 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 ~vm_area_open();
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override; virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
@@ -144,7 +143,7 @@ public:
/// Constructor. /// Constructor.
/// \arg size Initial virtual size of the memory area /// \arg size Initial virtual size of the memory area
/// \arg flags Flags for this 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 ~vm_area_untracked();
virtual bool add_to(vm_space *space) override; virtual bool add_to(vm_space *space) override;
@@ -166,7 +165,7 @@ public:
uintptr_t start, uintptr_t start,
size_t sec_pages, size_t sec_pages,
size_t size, size_t size,
vm_flags flags); util::bitset32 flags);
virtual ~vm_area_guarded(); virtual ~vm_area_guarded();
@@ -194,7 +193,7 @@ public:
/// \arg size Virtual size of the ring buffer. Note that /// \arg size Virtual size of the ring buffer. Note that
/// the VMA size will be double this value. /// the VMA size will be double this value.
/// \arg flags Flags for this memory area /// \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 ~vm_area_ring();
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override; virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;

View File

@@ -14,10 +14,7 @@ free_page_header * page_table::s_page_cache = nullptr;
util::spinlock page_table::s_lock; util::spinlock page_table::s_lock;
constexpr size_t page_table::entry_sizes[4]; constexpr size_t page_table::entry_sizes[4];
inline constexpr util::bitset64 table_flags = page_flags::present | page_flags::write;
constexpr page_table::flag table_flags =
page_table::flag::present |
page_table::flag::write;
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) : 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; uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~linear_offset;
uint64_t &parent = entry(l - 1); uint64_t &parent = entry(l - 1);
flag flags = table_flags; util::bitset64 flags = table_flags;
if (m_index[0] < arch::kernel_root_index) if (m_index[0] < arch::kernel_root_index)
flags |= flag::user; flags.set(flag::user);
m_table[unsigned(l)] = table; m_table[unsigned(l)] = table;
parent = (phys & ~0xfffull) | flags; parent = (phys & ~0xfffull) | flags;

View File

@@ -4,11 +4,33 @@
#include <stdint.h> #include <stdint.h>
#include <arch/memory.h> #include <arch/memory.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
#include <util/spinlock.h> #include <util/spinlock.h>
struct free_page_header; 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 to allow easy accessing of a memory page being used as a page table.
struct page_table struct page_table
{ {
@@ -16,27 +38,19 @@ struct page_table
enum class level : unsigned { pml4, pdp, pd, pt, page }; enum class level : unsigned { pml4, pdp, pd, pt, page };
/// Page entry flags /// Page entry flags
enum class flag : uint64_t enum class flag
{ {
none = 0x0000, present = 0, /// Entry is present in the table
present = 0x0001, /// Entry is present in the table write = 1, /// Section may be written
write = 0x0002, /// Section may be written user = 2, /// User-accessible
user = 0x0004, /// User-accessible pat0 = 3, /// PAT selector bit 0
pat0 = 0x0008, /// PAT selector bit 0 pat1 = 4, /// PAT selector bit 1
pat1 = 0x0010, /// PAT selector bit 1 accessed = 5, /// Entry has been accessed
accessed = 0x0020, /// Entry has been accessed dirty = 6, /// Page has been written to
dirty = 0x0040, /// Page has been written to page = 7, /// Entry is a large page
page = 0x0080, /// Entry is a large page pat2 = 7, /// PAT selector bit 2 on PT entries
pat2 = 0x0080, /// PAT selector bit 2 on PT entries global = 8, /// Entry is not PCID-specific
global = 0x0100, /// Entry is not PCID-specific pat2_lg = 12, /// PAT selector bit 2 on large/huge pages
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,
}; };
/// Helper for getting the next level value /// 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; }
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);

View File

@@ -2,6 +2,7 @@
#include "cpu.h" #include "cpu.h"
#include "display.h" #include "display.h"
#include "kernel.dir/memory.h"
#include "objects/process.h" #include "objects/process.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "serial.h" #include "serial.h"
@@ -9,6 +10,44 @@
namespace panicking { 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"; const char *clear = "\e[0m\n";
void 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 process = cpu.process ? cpu.process->obj_id() : 0;
uint32_t thread = cpu.thread ? cpu.thread->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: "); out.write("\n \e[0;31m==[ CPU: ");
char buffer[64]; char buffer[64];
util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx>", size_t len = util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx> ]: %s ",
cpu.id + 1, process, thread); cpu.id + 1, process, thread, name);
out.write(buffer); out.write(buffer);
out.write(" ]=============================================================\n"); for (size_t i = 0; i < (74-len); ++i) out.write("=");
} 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;
} }
void void
@@ -65,18 +98,26 @@ print_callstack(serial_port &out, symbol_table &syms, frame const *fp)
char message[512]; char message[512];
unsigned count = 0; unsigned count = 0;
while (canonical(fp) && fp && fp->return_addr) { while (fp && check_pointer(fp)) {
char const *name = syms.find_symbol(fp->return_addr); const uintptr_t ret = fp->return_addr;
char const *name = ret ? syms.find_symbol(ret) : "<END>";
if (!name) if (!name)
name = canonical(fp->return_addr) ? "<unknown>" : "<corrupt>"; name = check_pointer(fp->return_addr) ? "<unknown>" : "<unmapped>";
util::format({message, sizeof(message)}, util::format({message, sizeof(message)},
" \e[0;33mframe %2d: <0x%016lx> \e[1;33m%s\n", " \e[0;33mframe %2d: <0x%016lx> <0x%016lx> \e[1;33m%s\n",
count++, fp->return_addr, name); count++, fp, fp->return_addr, name);
out.write(message); out.write(message);
if (!ret) return;
fp = fp->prev; 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 static void

View File

@@ -1,12 +1,13 @@
# vim: ft=python # vim: ft=python
panic = module("panic.serial", panic = module("panic.serial",
targets = [ "kernel" ], target = "kernel",
deps = [ "util", "elf", "kernel" ], deps = [ "util", "elf", "kernel" ],
static = True, static = True,
includes = [ ".." ], includes = [ ".." ],
description = "Serial panic handler", description = "Serial panic handler",
ld_script = "panic.serial.ld", ld_script = "panic.serial.ld",
skip_arches = [ "linux" ],
sources = [ sources = [
"display.cpp", "display.cpp",
"entry.s", "entry.s",

View File

@@ -339,5 +339,5 @@ scheduler::maybe_schedule(TCB *t)
return; return;
current_cpu().apic->send_ipi( current_cpu().apic->send_ipi(
lapic::ipi::fixed, isr::ipiSchedule, cpu->id); lapic::ipi_fixed, isr::ipiSchedule, cpu->id);
} }

View File

@@ -57,8 +57,11 @@ start(cpu_data &bsp, void *kpml4)
// Copy the startup code somwhere the real mode trampoline can run // Copy the startup code somwhere the real mode trampoline can run
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
isr vector = static_cast<isr>(addr >> 12); 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( memcpy(
reinterpret_cast<void*>(addr), reinterpret_cast<void*>(addr),
reinterpret_cast<void*>(&ap_startup), reinterpret_cast<void*>(&ap_startup),
@@ -67,7 +70,7 @@ start(cpu_data &bsp, void *kpml4)
size_t free_stack_count = 0; size_t free_stack_count = 0;
lapic &apic = *bsp.apic; 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)); apic.send_ipi_broadcast(mode, false, static_cast<isr>(0));
for (uint8_t id : ids) { for (uint8_t id : ids) {
@@ -90,7 +93,7 @@ start(cpu_data &bsp, void *kpml4)
size_t current_count = ap_startup_count; size_t current_count = ap_startup_count;
log::verbose(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end); 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); apic.send_ipi(startup, vector, id);
for (unsigned i = 0; i < 20; ++i) { for (unsigned i = 0; i < 20; ++i) {

View File

@@ -32,4 +32,13 @@ handle_clone(j6_handle_t orig, j6_handle_t *clone, uint32_t mask)
return j6_status_ok; 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 } // namespace syscalls

View File

@@ -35,16 +35,17 @@ mailbox_call(
uint64_t *tag, uint64_t *tag,
void *in_data, void *in_data,
size_t *data_len, size_t *data_len,
size_t data_in_len, size_t data_size,
j6_handle_t *in_handles, j6_handle_t *in_handles,
size_t *handles_count) size_t *handles_count,
size_t handles_size)
{ {
thread &cur = thread::current(); 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}; 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)); cur.set_message_data(util::move(message));
j6_status_t s = self->call(); j6_status_t s = self->call();
@@ -52,19 +53,20 @@ mailbox_call(
return s; return s;
message = cur.get_message_data(); message = cur.get_message_data();
util::counted<j6_handle_t> msg_handles = message->handles();
util::buffer msg_data = message->data();
if (message.handles) { if (message->handle_count) {
for (unsigned i = 0; i < message.handles.count; ++i) for (unsigned i = 0; i < msg_handles.count; ++i)
process::current().add_handle(message.handles[i]); process::current().add_handle(msg_handles[i]);
} }
*tag = message.tag; *tag = message->tag;
*data_len = *data_len > message.data.count ? message.data.count : *data_len; *data_len = data_size > msg_data.count ? msg_data.count : data_size;
memcpy(in_data, message.data.pointer, *data_len); 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_size > msg_handles.count ? msg_handles.count : handles_size;
*handles_count = handles_min; memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
return j6_status_ok; return j6_status_ok;
} }
@@ -75,18 +77,20 @@ mailbox_respond(
uint64_t *tag, uint64_t *tag,
void *in_data, void *in_data,
size_t *data_len, size_t *data_len,
size_t data_in_len, size_t data_size,
j6_handle_t *in_handles, j6_handle_t *in_handles,
size_t *handles_count, size_t *handles_count,
size_t handles_size,
uint64_t *reply_tag, uint64_t *reply_tag,
uint64_t flags) 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}; util::counted<j6_handle_t> handles {in_handles, *handles_count};
ipc::message message(*tag, data, handles); ipc::message_ptr message;
if (*reply_tag) { if (*reply_tag) {
message = new ipc::message {*tag, data, handles};
j6_status_t s = self->reply(*reply_tag, util::move(message)); j6_status_t s = self->reply(*reply_tag, util::move(message));
if (s != j6_status_ok) if (s != j6_status_ok)
return s; return s;
@@ -97,18 +101,20 @@ mailbox_respond(
if (s != j6_status_ok) if (s != j6_status_ok)
return s; return s;
if (message.handles) { util::counted<j6_handle_t> msg_handles = message->handles();
for (unsigned i = 0; i < message.handles.count; ++i) util::buffer msg_data = message->data();
process::current().add_handle(message.handles[i]);
if (msg_handles) {
for (unsigned i = 0; i < msg_handles.count; ++i)
process::current().add_handle(msg_handles[i]);
} }
*tag = message.tag; *tag = message->tag;
*data_len = *data_len > message.data.count ? message.data.count : *data_len; *data_len = data_size > msg_data.count ? msg_data.count : data_size;
memcpy(in_data, message.data.pointer, *data_len); 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_size > msg_handles.count ? msg_handles.count : handles_size;
*handles_count = handles_min; memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
return j6_status_ok; return j6_status_ok;
} }

View File

@@ -10,9 +10,9 @@ using namespace obj;
namespace syscalls { namespace syscalls {
j6_status_t 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()); log::info(logs::task, "Process <%02lx> created", p->obj_id());
return j6_status_ok; return j6_status_ok;
} }
@@ -29,10 +29,10 @@ process_kill(process *self)
} }
j6_status_t j6_status_t
process_exit(int32_t status) process_exit(int64_t status)
{ {
process &p = process::current(); 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); p.exit(status);

View File

@@ -21,10 +21,12 @@ namespace syscalls {
using system = class ::system; using system = class ::system;
j6_status_t 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(); 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; 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 // TODO: check to see if frames are already used? How would that collide with
// the bootloader's allocated pages already being marked used? // 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)); 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, f);
construct_handle<vm_area_fixed>(area, phys, size, vmf);
return j6_status_ok; return j6_status_ok;
} }

View File

@@ -14,8 +14,8 @@ namespace syscalls {
j6_status_t j6_status_t
vma_create(j6_handle_t *self, size_t size, uint32_t flags) vma_create(j6_handle_t *self, size_t size, uint32_t flags)
{ {
vm_flags f = vm_flags::user_mask & flags; util::bitset32 f = flags & vm_user_mask;
if (util::bits::has(f, vm_flags::ring)) if (f.get(vm_flags::ring))
construct_handle<vm_area_ring>(self, size, f); construct_handle<vm_area_ring>(self, size, f);
else else
construct_handle<vm_area_open>(self, size, f); 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) vma_create_map(j6_handle_t *self, size_t size, uintptr_t *base, uint32_t flags)
{ {
vm_area *a = nullptr; vm_area *a = nullptr;
vm_flags f = vm_flags::user_mask & flags; util::bitset32 f = flags & vm_user_mask;
if (util::bits::has(f, vm_flags::ring)) if (f.get(vm_flags::ring))
a = construct_handle<vm_area_ring>(self, size, f); a = construct_handle<vm_area_ring>(self, size, f);
else else
a = construct_handle<vm_area_open>(self, size, f); 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) vma_map(vm_area *self, process *proc, uintptr_t *base, uint32_t flags)
{ {
vm_space &space = proc ? proc->space() : process::current().space(); 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); *base = space.add(*base, self, f);
return *base ? j6_status_ok : j6_err_collision; return *base ? j6_status_ok : j6_err_collision;
} }

View File

@@ -91,3 +91,10 @@ _current_gsbase:
mov rax, [gs:CPU_DATA.self] mov rax, [gs:CPU_DATA.self]
ret ret
.end: .end:
global _reload_cr3: function hidden (_reload_cr3.end - _reload_cr3)
_reload_cr3:
mov rax, cr3
mov cr3, rax
ret
.end:

View File

@@ -1,6 +1,7 @@
#include <j6/memutils.h> #include <j6/memutils.h>
#include <arch/memory.h> #include <arch/memory.h>
#include "apic.h"
#include "kassert.h" #include "kassert.h"
#include "frame_allocator.h" #include "frame_allocator.h"
#include "logger.h" #include "logger.h"
@@ -55,9 +56,9 @@ vm_space::vm_space() :
obj::vm_area *sysc = new obj::vm_area_fixed( obj::vm_area *sysc = new obj::vm_area_fixed(
g_sysconf_phys, g_sysconf_phys,
sizeof(system_config), 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() vm_space::~vm_space()
@@ -81,32 +82,31 @@ vm_space::kernel_space()
} }
uintptr_t 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) if (!base)
base = min_auto_address; base = min_auto_address;
uintptr_t end = base + area->size();
//TODO: optimize find/insert //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) { for (size_t i = 0; i < m_areas.count(); ++i) {
const vm_space::area &a = m_areas[i]; const vm_space::area &cur = m_areas[i];
uintptr_t aend = a.base + a.area->size(); uintptr_t cur_end = cur.base + cur.area->size();
if (base >= aend) if (base >= cur_end)
continue; continue;
if (end <= a.base) uintptr_t end = base + new_area->size();
if (end <= cur.base)
break; break;
else if (exact) else if (exact)
return 0; return 0;
else else
base = aend; base = cur_end;
} }
m_areas.sorted_insert({base, area}); m_areas.sorted_insert({base, new_area});
area->add_to(this); new_area->add_to(this);
area->handle_retain(); new_area->handle_retain();
return base; return base;
} }
@@ -192,7 +192,7 @@ vm_space::copy_from(const vm_space &source, const obj::vm_area &vma)
while (count--) { while (count--) {
uint64_t &e = dit.entry(page_table::level::pt); 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 // TODO: handle clobbering mapping
} }
e = sit.entry(page_table::level::pt); 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; return;
uintptr_t virt = base + offset; uintptr_t virt = base + offset;
page_table::flag flags = util::bitset64 flags =
page_table::flag::present | page_flags::present |
(m_kernel ? page_table::flag::none : page_table::flag::user) | (m_kernel ? page_flags::none : page_flags::user) |
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) | (vma.flags().get(vm_flags::write) ? page_flags::write : page_flags::none) |
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none); (vma.flags().get(vm_flags::write_combine) ? page_flags::wc : page_flags::none);
page_table::iterator it {virt, m_pml4}; 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); uint64_t &entry = it.entry(page_table::level::pt);
entry = (phys + i * frame_size) | flags; entry = (phys + i * frame_size) | flags;
log::spam(logs::paging, "Setting entry for %016llx: %016llx [%04llx]", 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; ++it;
} }
} }
@@ -247,14 +247,14 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
while (count--) { while (count--) {
uint64_t &e = it.entry(page_table::level::pt); uint64_t &e = it.entry(page_table::level::pt);
uintptr_t phys = e & ~0xfffull; uintptr_t phys = e & ~0xfffull;
util::bitset64 flags = e;
if (e & page_table::flag::present) { if (flags & page_flags::present) {
uint64_t orig = e;
e = 0; e = 0;
if (orig & page_table::flag::accessed) {
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress()); auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
asm ( "invlpg %0" :: "m"(*addr) : "memory" ); asm ( "invlpg %0" :: "m"(*addr) : "memory" );
}
if (free_count && phys == free_start + (free_count * frame_size)) { if (free_count && phys == free_start + (free_count * frame_size)) {
++free_count; ++free_count;
} else { } else {
@@ -268,6 +268,9 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
++it; ++it;
} }
current_cpu().apic->send_ipi_broadcast(
lapic::ipi_fixed, false, isr::ipiShootdown);
if (free && free_count) if (free && free_count)
fa.free(free_start, 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--) { while (count--) {
uint64_t &e = it.entry(page_table::level::pt); uint64_t &e = it.entry(page_table::level::pt);
uintptr_t phys = e & ~0xfffull; uintptr_t phys = e & ~0xfffull;
util::bitset64 flags = e;
if (e & page_table::flag::present) { if (flags & page_flags::present) {
uint64_t orig = e;
e = locked_page_tag; e = locked_page_tag;
if (orig & page_table::flag::accessed) { if (flags & page_flags::accessed) {
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress()); auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
asm ( "invlpg %0" :: "m"(*addr) : "memory" ); asm ( "invlpg %0" :: "m"(*addr) : "memory" );
} }
@@ -338,10 +341,10 @@ vm_space::initialize_tcb(TCB &tcb)
} }
bool 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 // TODO: Handle more fult types
if (fault && fault_type::present) if (fault.get(fault_type::present))
return false; return false;
uintptr_t page = (addr & ~0xfffull); uintptr_t page = (addr & ~0xfffull);

View File

@@ -5,7 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <j6/flags.h> #include <j6/flags.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
#include <util/spinlock.h> #include <util/spinlock.h>
#include <util/vector.h> #include <util/vector.h>
@@ -39,7 +39,7 @@ public:
/// \arg area The area to add /// \arg area The area to add
/// \arg flags Flags for the operation (exact, clobber, etc) /// \arg flags Flags for the operation (exact, clobber, etc)
/// \returns The base address the area was added at /// \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 /// Remove a virtual memory area from this address space
/// \arg area The area to remove /// \arg area The area to remove
@@ -88,15 +88,6 @@ public:
/// Set this space as the current active space /// Set this space as the current active space
void activate() const; 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. /// Allocate pages into virtual memory. May allocate less than requested.
/// \arg virt The virtual address at which to allocate /// \arg virt The virtual address at which to allocate
/// \arg count The number of pages to allocate /// \arg count The number of pages to allocate
@@ -104,11 +95,13 @@ public:
/// \returns The number of pages actually allocated /// \returns The number of pages actually allocated
size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys); 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. /// Handle a page fault.
/// \arg addr Address which caused the fault /// \arg addr Address which caused the fault
/// \arg ft Flags from the interrupt about the kind of fault /// \arg ft Flags from the interrupt about the kind of fault
/// \returns True if the fault was successfully handled /// \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. /// Set up a TCB to operate in this address space.
void initialize_tcb(TCB &tcb); void initialize_tcb(TCB &tcb);
@@ -156,5 +149,3 @@ private:
util::spinlock m_lock; util::spinlock m_lock;
}; };
is_bitfield(vm_space::fault_type);

View 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 &region = 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

View 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",
])

View 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

View File

@@ -3,7 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
#include <util/misc.h> // for byteswap32 #include <util/misc.h> // for byteswap32
namespace acpi { namespace acpi {
@@ -21,6 +21,7 @@ struct table_header
uint32_t creator_revision; uint32_t creator_revision;
bool validate() const { return util::checksum(this, length) == 0; } bool validate() const { return util::checksum(this, length) == 0; }
bool validate(uint32_t sig) const { return type == sig && validate(); }
} __attribute__ ((packed)); } __attribute__ ((packed));
@@ -62,32 +63,31 @@ struct gas
} __attribute__ ((packed)); } __attribute__ ((packed));
enum class fadt_flags : uint32_t enum class fadt_flags
{ {
wbinvd = 0x00000001, wbinvd,
wbinvd_flush = 0x00000002, wbinvd_flush,
proc_c1 = 0x00000004, proc_c1,
p_lvl2_up = 0x00000008, p_lvl2_up,
pwr_button = 0x00000010, pwr_button,
slp_button = 0x00000020, slp_button,
fix_rtc = 0x00000040, fix_rtc,
rtc_s4 = 0x00000080, rtc_s4,
tmr_val_ext = 0x00000100, tmr_val_ext,
dck_cap = 0x00000200, dck_cap,
reset_reg_sup = 0x00000400, reset_reg_sup,
sealed_case = 0x00000800, sealed_case,
headless = 0x00001000, headless,
cpu_sw_slp = 0x00002000, cpu_sw_slp,
pci_exp_wak = 0x00004000, pci_exp_wak,
use_plat_clock = 0x00008000, use_plat_clock,
s4_rtc_sts_val = 0x00010000, s4_rtc_sts_val,
remote_pwr_cap = 0x00020000, remote_pwr_cap,
apic_cluster = 0x00040000, apic_cluster,
apic_physical = 0x00080000, apic_physical,
hw_reduced_acpi = 0x00100000, hw_reduced_acpi,
low_pwr_s0_idle = 0x00200000 low_pwr_s0_idle
}; };
is_bitfield(fadt_flags);
struct fadt struct fadt
{ {
@@ -134,7 +134,7 @@ struct fadt
uint16_t iapc_boot_arch; uint16_t iapc_boot_arch;
uint8_t reserved1; uint8_t reserved1;
fadt_flags flags; util::bitset32 flags;
gas reset_reg; gas reset_reg;
uint8_t reset_value; uint8_t reset_value;

Some files were not shown because too many files have changed in this diff Show More