23 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
130 changed files with 2883 additions and 1044 deletions

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

32
configure vendored
View File

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

View File

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

View File

@@ -12,7 +12,9 @@ object process : object {
]
# Create a new empty process
method create [constructor]
method create [constructor] {
param name string
}
# Stop all threads and exit the given process
method kill [destructor cap:kill]

View File

@@ -19,4 +19,5 @@ help:
# "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)

View File

@@ -71,6 +71,8 @@ The jsix kernel is quite far along now, but the userland systems are still lacki
:caption: Site Contents:
syscall_interface
kernel_memory
process_initialization
* :ref:`genindex`

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`

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`

View File

@@ -1,5 +1,5 @@
.. jsix syscall interface.
.. Automatically update from the definition files using cog!
.. Automatically updated from the definition files using cog!
.. [[[cog code generation
.. from textwrap import indent
@@ -14,11 +14,11 @@
.. ]]]
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
The jsix syscall interface
==========================
Syscall interface
=================
The jsix kernel's syscall design is based around object handles. Object handles
are also a collections capabilities, encoding certain rights over the object
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
@@ -133,7 +133,7 @@ as shared memory channels, but more flexible.
: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_in_len, j6_handle_t * handles, size_t * handles_count)
.. 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
@@ -144,10 +144,11 @@ as shared memory channels, but more flexible.
:param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input
: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_in_len, j6_handle_t * handles, size_t * handles_count, uint64_t * reply_tag, uint64_t flags)
.. 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
@@ -159,8 +160,9 @@ as shared memory channels, but more flexible.
:param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input
: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
@@ -185,7 +187,7 @@ control over the threads, handles, and virtual memory space of that process.
:param self: Handle to the process object
.. cpp:function:: j6_result_t j6_process_exit (int32_t result)
.. cpp:function:: j6_result_t j6_process_exit (int64_t result)
Stop all threads and exit the current process
@@ -353,7 +355,7 @@ necessarily mean that it is mapped into that process' virtual memory space.
: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: fe448e541c009a1bcf8bdc44f4760e32)
.. [[[end]]] (checksum: cb17f54e443d1d3b85995870f3e8dbf2)
Non-object syscalls
-------------------
@@ -411,6 +413,12 @@ either do not require an object handle, or operate generically on handles.
: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
@@ -432,5 +440,5 @@ either do not require an object handle, or operate generically on handles.
:param exit_code: Undocumented
.. [[[end]]] (checksum: b8b12e99a4a00c99b3859f05000a7bfd)
.. [[[end]]] (checksum: 0b9d051972abcbb6de408f411331785f)

View File

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

View File

@@ -43,10 +43,10 @@ def _make_ninja_config(outfile, config, files):
build.variable(k, v)
def generate_configs(root, output, config, targets, kinds):
def generate_configs(root, output, buildconfig, arch, targets, kinds):
assets = root / "assets" / "build"
base = ["global.yaml", f"config.{config}.yaml"]
assets = root / "assets" / "build" / arch
base = ["global.yaml", f"config.{buildconfig}.yaml"]
depfiles = set()

View File

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

View File

@@ -39,7 +39,7 @@ class Module:
"kind": (str, "exe"),
"outfile": (str, None),
"basename": (str, None),
"targets": (set, ()),
"target": (str, None),
"deps": (set, ()),
"public_headers": (set, ()),
"copy_headers": (bool, False),
@@ -53,6 +53,8 @@ class Module:
"no_libc": (bool, False),
"ld_script": (str, None),
"static": (bool, False),
"arch_source": (dict, ()),
"skip_arches": (tuple, ()),
}
def __init__(self, name, modfile, root, **kwargs):
@@ -81,15 +83,14 @@ class Module:
# Turn strings into real Source objects
self.sources = [make_source(root, f) for f in self.sources]
for arch in self.arch_source:
self.arch_source[arch] = [make_source(root, f) for f in self.arch_source[arch]]
header_source = lambda f: make_source(root, Path("include") / f)
if self.copy_headers:
header_source = lambda f: make_copy_source(root, f, "include")
self.public_headers = [header_source(f) for f in self.public_headers]
# Filled by Module.update
self.depmods = set()
def __repr__(self):
return f"<Module {self.kind} {self.name}>"
@@ -115,227 +116,6 @@ class Module:
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
return self.basename + ext.get(self.kind, "")
@classmethod
def update(cls, mods):
from . import BonnibelError
def resolve(source, modlist):
resolved = set()
for dep in modlist:
if not dep in mods:
raise BonnibelError(f"module '{source.name}' references unknown module '{dep}'")
mod = mods[dep]
resolved.add(mod)
return resolved
for mod in mods.values():
mod.depmods = resolve(mod, mod.deps)
target_mods = [mod for mod in mods.values() if mod.targets]
for mod in target_mods:
closed = set()
children = set(mod.depmods)
while children:
child = children.pop()
closed.add(child)
child.targets |= mod.targets
children |= {m for m in child.depmods if not m in closed}
def generate(self, output):
from pathlib import Path
from collections import defaultdict
from ninja.ninja_syntax import Writer
def walk_deps(deps, static, results):
for mod in deps:
if static or mod.name not in results:
results[mod.name] = (mod, static)
walk_deps(mod.depmods, static or mod.static, results)
all_deps = {}
walk_deps(self.depmods, self.static, all_deps)
all_deps = all_deps.values()
def gather_phony(build, deps, child_rel):
phony = ".headers.phony"
child_phony = [child_rel(phony, module=c.name)
for c, _ in all_deps]
build.build(
rule = "touch",
outputs = [mod_rel(phony)],
implicit = child_phony,
order_only = list(map(mod_rel, deps)),
)
filename = str(output / f"module.{self.name}.ninja")
with open(filename, "w") as buildfile:
build = Writer(buildfile)
build.comment("This file is automatically generated by bonnibel")
build.newline()
build.variable("module_dir", target_rel(self.name + ".dir"))
build.variable("module_kind", self.kind)
build.newline()
build.include(f"${{target_dir}}/config.{self.kind}.ninja")
build.newline()
modopts = BuildOptions(
local = [self.root, "${module_dir}"],
ld_script = self.ld_script and self.root / self.ld_script,
)
if self.public_headers:
modopts.includes += [
self.root / "include",
f"${{target_dir}}/{self.name}.dir/include",
]
for key, value in self.variables.items():
build.variable(key, value)
build.newline()
for include in self.includes:
p = Path(include)
if p.is_absolute():
if not p in modopts.includes:
modopts.includes.append(str(p.resolve()))
elif include != ".":
incpath = self.root / p
destpath = mod_rel(p)
for header in incpath.rglob("*.h"):
dest_header = f"{destpath}/" + str(header.relative_to(incpath))
modopts.includes.append(str(incpath))
modopts.includes.append(destpath)
for dep, static in all_deps:
if dep.public_headers:
if dep.include_phase == "normal":
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
elif dep.include_phase == "late":
modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
else:
from . import BonnibelError
raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}")
if dep.kind == "headers":
continue
elif dep.kind == "lib":
modopts.libs.append((target_rel(dep.get_output(static)), static))
else:
modopts.order_only.append(target_rel(dep.get_output(static)))
cc_includes = []
if modopts.local:
cc_includes += [f"-iquote{i}" for i in modopts.local]
if modopts.includes:
cc_includes += [f"-I{i}" for i in modopts.includes]
if modopts.late:
cc_includes += [f"-idirafter{i}" for i in modopts.late]
if cc_includes:
build.variable("ccflags", ["${ccflags}"] + cc_includes)
as_includes = [f"-I{d}" for d in modopts.local + modopts.includes + modopts.late]
if as_includes:
build.variable("asflags", ["${asflags}"] + as_includes)
if modopts.libs:
build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
if modopts.ld_script:
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
header_deps = []
inputs = []
headers = set(self.public_headers)
while headers:
source = headers.pop()
headers.update(source.next)
if source.action:
build.newline()
build.build(rule=source.action, **source.args)
if source.gather:
header_deps += list(source.outputs)
if source.input:
inputs.extend(map(mod_rel, source.outputs))
build.newline()
inputs = []
sources = set(self.sources)
while sources:
source = sources.pop()
sources.update(source.next)
if source.action:
build.newline()
build.build(rule=source.action, **source.args)
if source.gather:
header_deps += list(source.outputs)
if source.input:
inputs.extend(map(mod_rel, source.outputs))
gather_phony(build, header_deps, target_rel)
if self.kind == "headers":
# Header-only, don't output a build rule
return
output = target_rel(self.get_output())
build.newline()
build.build(
rule = self.kind,
outputs = output,
inputs = inputs,
implicit = modopts.implicit,
order_only = modopts.order_only,
variables = {"name": self.name,
"soname": self.get_output()},
)
dump = output + ".dump"
build.newline()
build.build(
rule = "dump",
outputs = dump,
inputs = output,
variables = {"name": self.name},
)
s_output = target_rel(self.get_output(static=True))
if s_output != output:
build.newline()
build.build(
rule = self.kind + "_static",
outputs = s_output,
inputs = inputs,
order_only = modopts.order_only,
variables = {"name": self.name},
)
dump = s_output + ".dump"
build.newline()
build.build(
rule = "dump",
outputs = dump,
inputs = s_output,
variables = {"name": self.name},
)
if self.default:
build.newline()
build.default(output)
build.default(dump)
def add_input(self, path, **kwargs):
from .source import make_source
s = make_source(self.root, path, **kwargs)
@@ -350,3 +130,285 @@ class Module:
for source in self.public_headers:
if source.path in paths:
source.add_deps(deps)
class ModuleList:
def __init__(self, arch):
self.__arch = arch
self.__mods = {}
self.__used = {}
self.__targets = frozenset()
self.__kinds = frozenset()
def __getitem__(self, name):
return self.__mods.get(name)
def __contains__(self, name):
"""Return if the module name is known."""
return name in self.__mods
def __iter__(self):
"""Iterate over _non-skipped_ modules."""
return self.used.__iter__()
def __skip(self, mod):
if self.__arch in mod.skip_arches: return True
return False
@property
def used(self):
if self.__used is None:
self.__used = {n:m for n,m in self.__mods.items() if not self.__skip(m)}
return self.__used
@property
def targets(self):
if self.__targets is None:
self.__targets = frozenset([m.target for m in self.used.values() if m.target])
return self.__targets
@property
def kinds(self):
if self.__kinds is None:
self.__kinds = frozenset([m.kind for m in self.used.values()])
return self.__kinds
def get(self, name):
return self.__mods.get(name)
def add(self, mod):
if mod.name in self.__mods:
raise BonnibelError(f"re-adding module '{mod.name}' to this ModuleList")
self.__mods[mod.name] = mod
self.__used = None
self.__targets = None
self.__kinds = None
def get_mods(self, names, filt=None):
return {self[n] for n in names
if n in self.used
and ((not filt) or filt(self[n]))}
def all_deps(self, mods, stop_at_static=False):
search = set(mods)
closed = list()
while search:
mod = search.pop()
if mod in closed: continue
closed.append(mod)
if stop_at_static and mod.static:
continue
for dep in mod.deps:
if not dep in self.__mods:
raise BonnibelError(f"module '{mod.name}' references unknown module '{dep}'")
if dep in self.used:
search.add(self.used[dep])
return closed
def target_mods(self, target):
return self.all_deps([m for m in self.used.values() if m.target == target])
def generate(self, output):
from pathlib import Path
from collections import defaultdict
from ninja.ninja_syntax import Writer
def walk_deps(deps, static, results):
for modname in deps:
mod = self.used.get(modname)
if not mod: continue # skipped
if static or modname not in results:
results[modname] = (mod, static)
walk_deps(mod.deps, static or mod.static, results)
for mod in self.used.values():
all_deps = {}
walk_deps(mod.deps, mod.static, all_deps)
all_deps = all_deps.values()
def gather_phony(build, deps, child_rel):
phony = ".headers.phony"
child_phony = [child_rel(phony, module=c.name)
for c, _ in all_deps]
build.build(
rule = "touch",
outputs = [mod_rel(phony)],
implicit = child_phony,
order_only = list(map(mod_rel, deps)),
)
filename = str(output / f"module.{mod.name}.ninja")
with open(filename, "w") as buildfile:
build = Writer(buildfile)
build.comment("This file is automatically generated by bonnibel")
build.newline()
build.variable("module_dir", target_rel(mod.name + ".dir"))
build.variable("module_kind", mod.kind)
build.newline()
build.include(f"${{target_dir}}/config.{mod.kind}.ninja")
build.newline()
modopts = BuildOptions(
local = [mod.root, "${module_dir}"],
ld_script = mod.ld_script and mod.root / mod.ld_script,
)
if mod.public_headers:
modopts.includes += [
mod.root / "include",
f"${{target_dir}}/{mod.name}.dir/include",
]
for key, value in mod.variables.items():
build.variable(key, value)
build.newline()
for include in mod.includes:
p = Path(include)
if p.is_absolute():
if not p in modopts.includes:
modopts.includes.append(str(p.resolve()))
elif include != ".":
incpath = mod.root / p
destpath = mod_rel(p)
for header in incpath.rglob("*.h"):
dest_header = f"{destpath}/" + str(header.relative_to(incpath))
modopts.includes.append(str(incpath))
modopts.includes.append(destpath)
for dep, static in all_deps:
if dep.public_headers:
if dep.include_phase == "normal":
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
elif dep.include_phase == "late":
modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
else:
from . import BonnibelError
raise BonnibelError(f"Module {dep.name} has invalid include_phase={dep.include_phase}")
if dep.kind == "headers":
continue
elif dep.kind == "lib":
modopts.libs.append((target_rel(dep.get_output(static)), static))
else:
modopts.order_only.append(target_rel(dep.get_output(static)))
cc_includes = []
if modopts.local:
cc_includes += [f"-iquote{i}" for i in modopts.local]
if modopts.includes:
cc_includes += [f"-I{i}" for i in modopts.includes]
if modopts.late:
cc_includes += [f"-idirafter{i}" for i in modopts.late]
if cc_includes:
build.variable("ccflags", ["${ccflags}"] + cc_includes)
as_includes = [f"-I{d}" for d in modopts.local + modopts.includes + modopts.late]
if as_includes:
build.variable("asflags", ["${asflags}"] + as_includes)
if modopts.libs:
build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
if modopts.ld_script:
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
header_deps = []
inputs = []
headers = set(mod.public_headers)
while headers:
source = headers.pop()
headers.update(source.next)
if source.action:
build.newline()
build.build(rule=source.action, **source.args)
if source.gather:
header_deps += list(source.outputs)
if source.input:
inputs.extend(map(mod_rel, source.outputs))
build.newline()
inputs = []
sources = set(mod.sources)
sources.update(set(mod.arch_source.get(self.__arch, tuple())))
while sources:
source = sources.pop()
sources.update(source.next)
if source.action:
build.newline()
build.build(rule=source.action, **source.args)
if source.gather:
header_deps += list(source.outputs)
if source.input:
inputs.extend(map(mod_rel, source.outputs))
gather_phony(build, header_deps, target_rel)
if mod.kind == "headers":
# Header-only, don't output a build rule
continue
mod_output = target_rel(mod.get_output())
build.newline()
build.build(
rule = mod.kind,
outputs = mod_output,
inputs = inputs,
implicit = modopts.implicit,
order_only = modopts.order_only,
variables = {"name": mod.name,
"soname": mod.get_output()},
)
dump = mod_output + ".dump"
build.newline()
build.build(
rule = "dump",
outputs = dump,
inputs = mod_output,
variables = {"name": mod.name},
)
s_output = target_rel(mod.get_output(static=True))
if s_output != mod_output:
build.newline()
build.build(
rule = mod.kind + "_static",
outputs = s_output,
inputs = inputs,
order_only = modopts.order_only,
variables = {"name": mod.name},
)
dump = s_output + ".dump"
build.newline()
build.build(
rule = "dump",
outputs = dump,
inputs = s_output,
variables = {"name": mod.name},
)
if mod.default:
build.newline()
build.default(mod_output)
build.default(dump)

View File

@@ -10,22 +10,17 @@ class Project:
def __str__(self):
return f"{self.name} {self.version.major}.{self.version.minor}.{self.version.patch}-{self.version.sha}"
def generate(self, root, output, modules, config, manifest_file):
def generate(self, root, output, modules, config, arch, manifest_file):
import sys
from os.path import join
from ninja.ninja_syntax import Writer
targets = set()
kinds = set()
for mod in modules.values():
targets.update(mod.targets)
kinds.add(mod.kind)
from .config import generate_configs
config_deps = generate_configs(root, output, config, targets, kinds)
config_deps = generate_configs(root, output, config, arch, modules.targets, modules.kinds)
with open(output / "build.ninja", "w") as buildfile:
build = Writer(buildfile)
default_builds = []
build.comment("This file is automatically generated by bonnibel")
build.variable("ninja_required_version", "1.3")
@@ -34,7 +29,7 @@ class Project:
build.variable("build_config", config)
build.newline()
build.include(root / "assets/build/rules.ninja")
build.include(root / "assets" / "build" / arch / "rules.ninja")
build.newline()
build.variable("version_major", self.version.major)
@@ -49,7 +44,7 @@ class Project:
])
build.newline()
for target in targets:
for target in modules.targets:
build.subninja(output / target / "target.ninja")
build.newline()
@@ -57,7 +52,7 @@ class Project:
rule = "touch",
outputs = "${build_root}/.all_headers",
implicit = [f"${{build_root}}/include/{m.name}/.headers.phony"
for m in modules.values() if m.public_headers],
for m in modules.used.values() if m.public_headers],
)
build.build(
rule = "phony",
@@ -78,56 +73,24 @@ class Project:
initrdroot = output / "initrd_root"
initrdroot.mkdir(exist_ok=True)
fatroot_content = []
initrd_content = []
image_content = {'initrd_root': [], 'fatroot': []}
def add_fatroot(source, name):
output = join(manifest.location, name)
fatroot_output = f"${{build_root}}/fatroot/{output}"
def add_image_content(image, path, name):
output = join(path, name)
image_output = f"${{build_root}}/{image}/{output}"
build.build(
rule = "cp",
outputs = [fatroot_output],
inputs = [source],
variables = {
"description": f"Installing {output}",
})
fatroot_content.append(fatroot_output)
build.newline()
def add_fatroot_exe(entry):
input_path = f"${{build_root}}/{entry.target}/{entry.output}"
intermediary = f"${{build_root}}/{entry.output}"
build.build(
rule = "strip",
outputs = [intermediary],
inputs = [input_path],
implicit = [f"{input_path}.dump"],
variables = {
"name": f"Stripping {entry.module}",
"debug": f"${{build_root}}/.debug/{entry.output}.debug",
})
add_fatroot(intermediary, entry.output)
def add_initrd_content(root, name):
output = join(root, name)
initrd_output = f"${{build_root}}/initrd_root/{output}"
build.build(
rule = "cp",
outputs = [initrd_output],
outputs = [image_output],
inputs = [f"${{build_root}}/{name}"],
variables = {
"description": f"Installing {name}",
})
initrd_content.append(initrd_output)
image_content[image].append(image_output)
build.newline()
def add_initrd_stripped(root, entry):
def add_image_content_exe(image, path, entry):
input_path = f"${{build_root}}/{entry.target}/{entry.output}"
intermediary = f"${{build_root}}/{entry.output}"
@@ -141,87 +104,108 @@ class Project:
"debug": f"${{build_root}}/.debug/{entry.output}.debug",
})
add_initrd_content(root, entry.output)
build.build(
rule = "phony",
outputs = [entry.output],
inputs = [intermediary])
add_image_content(image, path, entry.output)
if 'kernel' in modules.used:
add_image_content_exe("fatroot", manifest.location, manifest.kernel)
syms = manifest.add_data("symbol_table.dat",
"Symbol table", ("symbols",))
syms_file = "jsix.symbols"
syms_path = join(manifest.location, syms_file );
syms_out = join(fatroot, syms_path)
build.build(
rule = "makest",
outputs = [syms_out],
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
)
image_content['fatroot'].append(syms_out)
manifest.symbols = syms_file
build.newline()
default_builds.append("${build_root}/jsix.img")
def try_add_manifest_module(entry, section, image, path="jsix"):
if entry.module in modules.used:
add_image_content_exe(image, path, entry)
elif entry.module not in modules:
raise BonnibelError(f'unknown {section} module in manifest: {entry.module}')
try_add_manifest_module(manifest.init, "init", "fatroot")
add_fatroot_exe(manifest.kernel)
add_fatroot_exe(manifest.init)
for program in manifest.panics:
add_fatroot_exe(program)
try_add_manifest_module(program, "panic", "fatroot")
for program in manifest.services:
add_initrd_stripped("jsix/services", program)
try_add_manifest_module(program, "services", "initrd_root", "jsix/services")
for program in manifest.drivers:
add_initrd_stripped("jsix/drivers", program)
try_add_manifest_module(program, "drivers", "initrd_root", "jsix/drivers")
for program in manifest.libs:
add_initrd_stripped("jsix/lib", program)
try_add_manifest_module(program, "libs", "initrd_root", "jsix/lib")
syms = manifest.add_data("symbol_table.dat",
"Symbol table", ("symbols",))
extra_regen_outputs = []
syms_file = "jsix.symbols"
syms_path = join(manifest.location, syms_file );
syms_out = join(fatroot, syms_path)
build.build(
rule = "makest",
outputs = [syms_out],
inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
)
fatroot_content.append(syms_out)
manifest.symbols = syms_file
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
build.build(
rule = "cp",
outputs = [bootloader],
inputs = ["${build_root}/boot/boot.efi"],
variables = {
"description": "Installing bootloader",
})
build.newline()
boot_config = join(fatroot, "jsix", "boot.conf")
manifest.write_boot_config(boot_config)
initrd = str(fatroot / manifest.location / manifest.initrd["name"])
build.build(
rule = "makeinitrd",
outputs = [initrd],
inputs = [str(initrdroot)],
implicit = initrd_content + ["${source_root}/scripts/mkj6romfs.py"],
variables = {"format": manifest.initrd["format"]},
)
build.newline()
fatroot_content.append(initrd)
build.build(
rule = "makefat",
outputs = ["${build_root}/jsix.img"],
inputs = ["${source_root}/assets/diskbase.img"],
implicit = fatroot_content + [bootloader],
variables = {"name": "jsix.img"},
)
build.newline()
default_assets = {
"UEFI Variables": ("ovmf/x64/ovmf_vars.fd", "ovmf_vars.fd"),
"GDB Debug Helpers": ("debugging/jsix.elf-gdb.py", "jsix.elf-gdb.py"),
}
for name, assets in default_assets.items():
p = root / "assets" / assets[0]
out = f"${{build_root}}/{assets[1]}"
if 'boot' in modules.used:
bootloader = "${build_root}/fatroot/efi/boot/bootx64.efi"
build.build(
rule = "cp",
outputs = [out],
inputs = [str(p)],
variables = {"name": name},
)
build.default([out])
outputs = [bootloader],
inputs = ["${build_root}/boot/boot.efi"],
variables = {
"description": "Installing bootloader",
})
build.newline()
boot_config = join(fatroot, "jsix", "boot.conf")
manifest.write_boot_config(boot_config)
extra_regen_outputs.append(boot_config)
initrd = str(fatroot / manifest.location / manifest.initrd["name"])
build.build(
rule = "makeinitrd",
outputs = [initrd],
inputs = [str(initrdroot)],
implicit = image_content['initrd_root'] + ["${source_root}/scripts/mkj6romfs.py"],
variables = {"format": manifest.initrd["format"]},
)
build.newline()
image_content['fatroot'].append(initrd)
build.build(
rule = "makefat",
outputs = ["${build_root}/jsix.img"],
inputs = ["${source_root}/assets/diskbase.img"],
implicit = image_content['fatroot'] + [bootloader],
variables = {"name": "jsix.img"},
)
build.newline()
default_assets = {
"UEFI Variables": ("ovmf/x64/ovmf_vars.fd", "ovmf_vars.fd"),
"GDB Debug Helpers": ("debugging/jsix.elf-gdb.py", "jsix.elf-gdb.py"),
}
for name, assets in default_assets.items():
p = root / "assets" / assets[0]
out = f"${{build_root}}/{assets[1]}"
build.build(
rule = "cp",
outputs = [out],
inputs = [str(p)],
variables = {"name": name},
)
build.newline()
default_builds.append(out)
compdb = "${source_root}/compile_commands.json"
build.rule("regen",
@@ -233,7 +217,7 @@ class Project:
regen_implicits = \
[f"{self.root}/configure", str(manifest_file)] + \
[str(mod.modfile) for mod in modules.values()]
[str(mod.modfile) for mod in modules.used.values()]
regen_implicits += list(map(str, config_deps))
@@ -242,24 +226,24 @@ class Project:
outputs = [compdb],
implicit = regen_implicits,
)
build.default([compdb])
build.newline()
default_builds.append(compdb)
build.build(
rule = "regen",
outputs = ['build.ninja'],
implicit = regen_implicits,
implicit_outputs =
[f"module.{mod.name}.ninja" for mod in modules.values()] +
[f"{target}/target.ninja" for target in targets] +
[boot_config],
[f"module.{mod.name}.ninja" for mod in modules.used.values()] +
[f"{target}/target.ninja" for target in modules.targets] +
extra_regen_outputs,
)
build.newline()
build.default(["${build_root}/jsix.img"])
build.default(default_builds)
for target in targets:
mods = [m.name for m in modules.values() if target in m.targets]
for target in modules.targets:
mods = modules.target_mods(target)
targetdir = output / target
targetdir.mkdir(exist_ok=True)
@@ -290,4 +274,4 @@ class Project:
build.newline()
for mod in mods:
build.subninja(f"module.{mod}.ninja")
build.subninja(f"module.{mod.name}.ninja")

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
supported_architectures = {
"amd64": "__amd64__",
}
def arch_includes(header):
prefix = "if"
for arch, define in supported_architectures.items():
cog.outl(f"#{prefix} defined({define})")
cog.outl(f"#include <__j6libc/arch/{arch}/{header}>")
prefix = "elif"
cog.outl("#endif")
int_widths = (8, 16, 32, 64)
int_mods = ("fast", "least")

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

View File

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

View File

@@ -44,7 +44,7 @@ void
output(j6_log_entry *entry)
{
char buffer [256];
size_t dlen = util::format({buffer, sizeof(buffer)}, "\e[38;5;%dm%7s %7s\e[38;5;14m|\e[38;5;%dm ",
size_t dlen = util::format({buffer, sizeof(buffer)}, "\e[38;5;%dm%7s %7s\e[38;5;14m\u2502\e[38;5;%dm ",
level_colors[entry->severity],
area_names[entry->area],
level_names[entry->severity],

View File

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

View File

@@ -117,4 +117,5 @@ ISR (0xe1, 0, isrLINT0)
ISR (0xe2, 0, isrLINT1)
ISR (0xe3, 0, isrAPICError)
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" {
void isr_handler(cpu_state*);
void irq_handler(cpu_state*);
void _reload_cr3();
}
uint8_t
@@ -187,6 +188,11 @@ isr_handler(cpu_state *regs)
scheduler::get().schedule();
break;
case isr::ipiShootdown:
// TODO: Real shootdown algorithm
_reload_cr3();
break;
default:
util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt);
kassert(false, message, regs);

View File

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

View File

@@ -86,7 +86,7 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
using bootproto::section_flags;
using obj::vm_flags;
obj::process *p = new obj::process;
obj::process *p = new obj::process {"srv.init"};
j6_handle_t sys_handle =
g_cap_table.create(&obj::system::get(), obj::system::init_caps);

View File

@@ -4,8 +4,6 @@
#include "kassert.h"
#include "logger.h"
#include "objects/system.h"
#include "objects/thread.h"
// The logger is initialized _before_ global constructors are called,
// so that we can start log output immediately. Keep its constructor

View File

@@ -5,10 +5,11 @@
#include <stdarg.h>
#include <stdint.h>
#include <j6/types.h>
#include <util/counted.h>
#include <util/spinlock.h>
#include "objects/event.h"
#include "wait_queue.h"
enum class logs : uint8_t {
#define LOG(name, lvl) name,

View File

@@ -1,3 +1,4 @@
#include <j6/memutils.h>
#include <util/no_construct.h>
#include "kassert.h"
@@ -18,10 +19,18 @@ obj::process &g_kernel_process = __g_kernel_process_storage.value;
namespace obj {
process::process() :
process::process(const char *name) :
kobject {kobject::type::process},
m_state {state::running}
{
if constexpr(__use_process_names) {
memset(m_name, 0, sizeof(m_name));
if (name) {
static constexpr size_t charlen = sizeof(m_name) - 1;
for (size_t i = 0; i < charlen && name[i]; ++i)
m_name[i] = name[i];
}
}
}
// The "kernel process"-only constructor
@@ -30,6 +39,11 @@ process::process(page_table *kpml4) :
m_space {kpml4},
m_state {state::running}
{
if constexpr(__use_process_names) {
static constexpr char kernel[] = "KERNEL";
memcpy(m_name, kernel, sizeof(kernel));
memset(m_name + sizeof(kernel), 0, sizeof(m_name) - sizeof(kernel));
}
}
process::~process()

View File

@@ -13,6 +13,14 @@
namespace obj {
static constexpr bool __use_process_names =
#ifdef __jsix_config_debug
true;
#else
false;
#endif
class process :
public kobject
{
@@ -32,7 +40,7 @@ public:
static constexpr kobject::type type = kobject::type::process;
/// Constructor.
process();
process(const char *name);
/// Destructor.
virtual ~process();
@@ -47,6 +55,9 @@ public:
/// Get the process' virtual memory space
vm_space & space() { return m_space; }
/// Get the debugging name of the process
const char *name() { if constexpr(__use_process_names) return m_name; else return nullptr; }
/// Create a new thread in this process
/// \args rsp3 If non-zero, sets the ring3 stack pointer to this value
/// \args priority The new thread's scheduling priority
@@ -107,6 +118,15 @@ private:
enum class state : uint8_t { running, exited };
state m_state;
static constexpr size_t max_name_len =
#ifdef __jsix_config_debug
32;
#else
0;
#endif
char m_name[max_name_len];
};
} // namespace obj

View File

@@ -10,7 +10,7 @@ namespace obj {
using mem::frame_size;
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_spaces {m_vector_static, 0, static_size},
kobject {kobject::type::vma}
@@ -34,6 +34,10 @@ void
vm_area::remove_from(vm_space *space)
{
m_spaces.remove_swap(space);
// If we were keeping this space around after its refcount
// dropped to zero because it was mapped, check if we should
// clean it up now.
if (!m_spaces.count() && !handle_count())
delete this;
}

View File

@@ -2,6 +2,7 @@
#include "cpu.h"
#include "display.h"
#include "kernel.dir/memory.h"
#include "objects/process.h"
#include "objects/thread.h"
#include "serial.h"
@@ -9,6 +10,44 @@
namespace panicking {
template <typename T> inline bool
check_pointer(T p)
{
static constexpr uint64_t large_flag = (1<<7);
static constexpr uint64_t pointer_mask = 0x0000fffffffff000;
static constexpr uintptr_t canon_mask = 0xffff800000000000;
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
uintptr_t canon_bits = addr & canon_mask;
if (canon_bits && canon_bits != canon_mask)
return false;
uintptr_t pml4 = 0;
asm volatile ( "mov %%cr3, %0" : "=r" (pml4) );
pml4 += mem::linear_offset;
uint64_t *table = reinterpret_cast<uint64_t*>(pml4);
unsigned shift = 39;
while (table) {
unsigned index = (addr >> shift) & 0x1ffull;
uint64_t entry = table[index];
if ((entry & 0x1) == 0)
return false;
if ((entry & large_flag) || shift <= 12)
return true;
uint64_t next = (entry & pointer_mask) + mem::linear_offset;
table = reinterpret_cast<uint64_t*>(next);
shift -= 9;
}
return false;
}
const char *clear = "\e[0m\n";
void
@@ -40,23 +79,17 @@ print_cpu(serial_port &out, cpu_data &cpu)
{
uint32_t process = cpu.process ? cpu.process->obj_id() : 0;
uint32_t thread = cpu.thread ? cpu.thread->obj_id() : 0;
const char *name = cpu.process ? cpu.process->name() : "<Unknown>";
out.write("\n \e[0;31m==[ CPU: ");
char buffer[64];
util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx>",
cpu.id + 1, process, thread);
size_t len = util::format({buffer, sizeof(buffer)}, "%4d <%02lx:%02lx> ]: %s ",
cpu.id + 1, process, thread, name);
out.write(buffer);
out.write(" ]=============================================================\n");
}
template <typename T> inline bool
canonical(T p)
{
static constexpr uintptr_t mask = 0xffff800000000000;
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return (addr & mask) == mask || (addr & mask) == 0;
for (size_t i = 0; i < (74-len); ++i) out.write("=");
out.write("\n");
}
void
@@ -65,18 +98,26 @@ print_callstack(serial_port &out, symbol_table &syms, frame const *fp)
char message[512];
unsigned count = 0;
while (canonical(fp) && fp && fp->return_addr) {
char const *name = syms.find_symbol(fp->return_addr);
while (fp && check_pointer(fp)) {
const uintptr_t ret = fp->return_addr;
char const *name = ret ? syms.find_symbol(ret) : "<END>";
if (!name)
name = canonical(fp->return_addr) ? "<unknown>" : "<corrupt>";
name = check_pointer(fp->return_addr) ? "<unknown>" : "<unmapped>";
util::format({message, sizeof(message)},
" \e[0;33mframe %2d: <0x%016lx> \e[1;33m%s\n",
count++, fp->return_addr, name);
" \e[0;33mframe %2d: <0x%016lx> <0x%016lx> \e[1;33m%s\n",
count++, fp, fp->return_addr, name);
out.write(message);
if (!ret) return;
fp = fp->prev;
}
const char *result = fp ? " <inaccessible>" : "";
util::format({message, sizeof(message)}, " \e[0mfinal <0x%016lx>%s\n",
fp, result);
out.write(message);
}
static void

View File

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

View File

@@ -10,9 +10,9 @@ using namespace obj;
namespace syscalls {
j6_status_t
process_create(j6_handle_t *self)
process_create(j6_handle_t *self, const char *path)
{
process *p = construct_handle<process>(self);
process *p = construct_handle<process>(self, path);
log::info(logs::task, "Process <%02lx> created", p->obj_id());
return j6_status_ok;
}

View File

@@ -23,9 +23,10 @@ using system = class ::system;
j6_status_t
log(uint8_t area, uint8_t severity, const char *message)
{
const char *name = process::current().name();
thread &th = thread::current();
log::log(static_cast<logs>(area), static_cast<log::level>(severity),
"<%02lx:%02lx>: %s", th.parent().obj_id(), th.obj_id(), message);
"<%02lx:%02lx> %s: %s", th.parent().obj_id(), th.obj_id(), name, message);
return j6_status_ok;
}

View File

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

View File

@@ -1,6 +1,7 @@
#include <j6/memutils.h>
#include <arch/memory.h>
#include "apic.h"
#include "kassert.h"
#include "frame_allocator.h"
#include "logger.h"
@@ -81,7 +82,7 @@ vm_space::kernel_space()
}
uintptr_t
vm_space::add(uintptr_t base, obj::vm_area *area, util::bitset32 flags)
vm_space::add(uintptr_t base, obj::vm_area *new_area, util::bitset32 flags)
{
if (!base)
base = min_auto_address;
@@ -89,23 +90,23 @@ vm_space::add(uintptr_t base, obj::vm_area *area, util::bitset32 flags)
//TODO: optimize find/insert
bool exact = flags.get(vm_flags::exact);
for (size_t i = 0; i < m_areas.count(); ++i) {
const vm_space::area &a = m_areas[i];
uintptr_t aend = a.base + a.area->size();
if (base >= aend)
const vm_space::area &cur = m_areas[i];
uintptr_t cur_end = cur.base + cur.area->size();
if (base >= cur_end)
continue;
uintptr_t end = base + area->size();
if (end <= a.base)
uintptr_t end = base + new_area->size();
if (end <= cur.base)
break;
else if (exact)
return 0;
else
base = aend;
base = cur_end;
}
m_areas.sorted_insert({base, area});
area->add_to(this);
area->handle_retain();
m_areas.sorted_insert({base, new_area});
new_area->add_to(this);
new_area->handle_retain();
return base;
}
@@ -250,10 +251,10 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
if (flags & page_flags::present) {
e = 0;
if (flags & page_flags::accessed) {
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
}
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
if (free_count && phys == free_start + (free_count * frame_size)) {
++free_count;
} else {
@@ -267,6 +268,9 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
++it;
}
current_cpu().apic->send_ipi_broadcast(
lapic::ipi_fixed, false, isr::ipiShootdown);
if (free && free_count)
fa.free(free_start, free_count);
}

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

@@ -21,6 +21,7 @@ struct table_header
uint32_t creator_revision;
bool validate() const { return util::checksum(this, length) == 0; }
bool validate(uint32_t sig) const { return type == sig && validate(); }
} __attribute__ ((packed));

View File

@@ -12,39 +12,42 @@
extern "C" {
#endif
#ifdef __cplusplus
#define add_header(name) \
static constexpr j6_arg_type type_id = j6_arg_type_ ## name; \
j6_arg_header header;
#else
#define add_header(name) \
j6_arg_header header;
#endif
enum j6_aux_type {
// The SysV ABI-specified aux vector types
j6_aux_null, // AT_NULL
j6_aux_ignore, // AT_IGNORE
j6_aux_execfd, // AD_EXECFD - File descriptor of the exe to load
j6_aux_phdr, // AD_PHDR - Program headers pointer for the exe to load
j6_aux_phent, // AD_PHENT - Size of a program header entry
j6_aux_phnum, // AD_PHNUM - Number of program header entries
j6_aux_pagesz, // AD_PAGESZ - System page size
j6_aux_base, // AD_BASE - Base address of dynamic loader
j6_aux_flags, // AD_FLAGS - Flags
j6_aux_entry, // AD_ENTRY - Entrypoint for the exe to load
j6_aux_notelf, // AD_NOTELF - If non-zero, this program is not ELF
j6_aux_uid, // AD_UID - User ID
j6_aux_euid, // AD_EUID - Effective User ID
j6_aux_gid, // AD_GID - Group ID
j6_aux_egid, // AD_EGID - Effective Group ID
enum j6_arg_type {
j6_arg_type_none,
j6_arg_type_sysv_init,
j6_arg_type_loader,
j6_arg_type_driver,
j6_arg_type_handles,
j6_aux_start = 0xf000,
j6_aux_handles, // Pointer to a j6_arg_handles structure
j6_aux_device, // Pointer to a j6_arg_driver structure
j6_aux_loader, // Pointer to a j6_arg_loader structure
};
struct j6_arg_header
struct j6_aux
{
uint32_t size;
uint16_t type;
uint16_t reserved;
j6_arg_header *next;
};
struct j6_arg_none
{
add_header(none);
uint64_t type;
union {
uint64_t value;
void *pointer;
void (*func)();
};
};
struct j6_arg_loader
{
add_header(loader);
uintptr_t loader_base;
uintptr_t image_base;
uintptr_t *got;
@@ -54,8 +57,8 @@ struct j6_arg_loader
struct j6_arg_driver
{
add_header(driver);
uint64_t device;
uint32_t size;
uint8_t data [0];
};
@@ -67,32 +70,13 @@ struct j6_arg_handle_entry
struct j6_arg_handles
{
add_header(handles);
size_t nhandles;
j6_arg_handle_entry handles[0];
};
struct j6_init_args
{
uint64_t argv[2];
j6_arg_header *args;
};
/// Find the first handle of the given type held by this process
j6_handle_t API j6_find_first_handle(j6_object_type obj_type);
/// Find the first handle tagged with the given proto in the process init args
j6_handle_t API j6_find_init_handle(uint64_t proto);
/// Get the init args
const j6_init_args * j6_get_init_args();
/// Drivers may use driver_main instead of main
int driver_main(unsigned, const char **, const char **, const j6_init_args *);
#ifdef __cplusplus
} // extern "C"
#endif
#undef add_header

View File

@@ -8,4 +8,6 @@ enum j6_proto_vfs_tag
{
j6_proto_vfs_load = j6_proto_base_first_proto_id,
j6_proto_vfs_file,
j6_proto_vfs_get_tag,
j6_proto_vfs_tag,
};

View File

@@ -13,7 +13,13 @@ class API client
public:
/// Constructor.
/// \arg vfs_mb Handle to the VFS service's mailbox
client(j6_handle_t vfs_mb);
client(j6_handle_t vfs_mb = 0);
/// Copy constructor
client(const client& c);
/// Check if this client's handle is valid
inline bool valid() const { return m_service != j6_handle_invalid; }
/// Load a file into a VMA
/// \arg path Path of the file to load
@@ -21,6 +27,11 @@ public:
/// \arg size [out] Size of the file
j6_status_t load_file(char *path, j6_handle_t &vma, size_t &size);
/// Get fs tag
/// \arg tag [out] The filesystem's tag
/// \arg size [inout] Size of the input buffer, length of the returned string
j6_status_t get_tag(char *tag, size_t &len);
private:
j6_handle_t m_service;
};

View File

@@ -1,5 +1,7 @@
// The kernel depends on libj6 for some shared code,
// but should not include the user-specific code.
#include "j6/init.h"
#include "j6/types.h"
#ifndef __j6kernel
#include <stddef.h>
@@ -10,65 +12,45 @@
#include <j6/types.h>
namespace {
constexpr size_t static_arr_count = 32;
j6_handle_descriptor handle_array[static_arr_count];
j6_init_args init_args = { 0, 0, 0 };
} // namespace
char const * const *envp = nullptr;
const j6_aux *aux = nullptr;
j6_handle_t
j6_find_first_handle(j6_object_type obj_type)
{
size_t count = static_arr_count;
j6_handle_descriptor *handles = handle_array;
j6_status_t s = j6_handle_list(handles, &count);
if (s != j6_err_insufficient && s != j6_status_ok)
return j6_handle_invalid;
if (count > static_arr_count)
count = static_arr_count;
for (size_t i = 0; i < count; ++i) {
j6_handle_descriptor &desc = handle_array[i];
if (desc.type == obj_type) return desc.handle;
const j6_aux * find_aux(uint64_t type) {
if (!aux) return nullptr;
for (j6_aux const *p = aux; p->type; ++p)
if (p->type == type) return p;
return nullptr;
}
return j6_handle_invalid;
}
} // namespace
j6_handle_t
j6_find_init_handle(uint64_t proto)
{
j6_arg_header *arg = init_args.args;
while (arg) {
if (arg->type == j6_arg_type_handles) {
j6_arg_handles *harg = reinterpret_cast<j6_arg_handles*>(arg);
for (unsigned i = 0; i < harg->nhandles; ++i) {
j6_arg_handle_entry &ent = harg->handles[i];
if (ent.proto == proto)
return ent.handle;
}
}
arg = arg->next;
const j6_aux *aux_handles = find_aux(j6_aux_handles);
if (!aux_handles)
return j6_handle_invalid;
const j6_arg_handles *arg = reinterpret_cast<const j6_arg_handles*>(aux_handles->pointer);
for (unsigned i = 0; i < arg->nhandles; ++i) {
const j6_arg_handle_entry &ent = arg->handles[i];
if (ent.proto == proto)
return ent.handle;
}
return j6_handle_invalid;
}
const j6_init_args * API
j6_get_init_args()
{
return &init_args;
}
extern "C" void API
__init_libj6(uint64_t argv0, uint64_t argv1, j6_arg_header *args)
__init_libj6(const uint64_t *stack)
{
init_args.argv[0] = argv0;
init_args.argv[1] = argv1;
init_args.args = args;
}
// Walk the stack to get the aux vector
uint64_t argc = *stack++;
stack += argc + 1; // Skip argv's and sentinel
envp = reinterpret_cast<char const * const *>(stack);
while (*stack++); // Skip envp's and sentinel
aux = reinterpret_cast<const j6_aux*>(stack);
}
#endif // __j6kernel

View File

@@ -1,6 +0,0 @@
extern driver_main
global main:function weak (main.end - main)
main:
jmp driver_main
main.end:

View File

@@ -14,10 +14,17 @@ j6 = module("j6",
"protocols/service_locator.cpp",
"protocols/vfs.cpp",
"ring_buffer.cpp",
"syscalls.s.cog",
"sysconf.cpp.cog",
"syslog.cpp",
],
arch_source = {
"amd64": [
"syscalls.s.cog",
],
"linux": [
"linux/syscalls.s.cog",
],
},
public_headers = [
"j6/cap_flags.h.cog",
"j6/channel.hh",

View File

@@ -0,0 +1,49 @@
; vim: ft=asm
%macro define_syscall 2
global j6_%1: function (j6_%1.end - j6_%1)
j6_%1:
push rbp
mov rbp, rsp
; if the syscall has more than 6 arguments, the rest
; will be pushed on the stack. in that case, we'd need
; to pass this stack pointer to the kernel, so stash
; off rbx (callee-saved) and pass the pointer to the
; arguments there.
push rbx
mov rbx, rbp
add rbx, 16 ; account for stack frame
; args should already be in rdi, etc, but rcx will
; get stomped, so stash it in r10, which isn't a
; callee-saved register, but also isn't used in the
; function call ABI.
mov r10, rcx
mov rax, %2
syscall
; result is now already in rax, so just return
pop rbx
pop rbp
ret
.end:
%endmacro
; [[[cog code generation
; from definitions.context import Context
;
; ctx = Context(definitions_path)
; ctx.parse("syscalls.def")
; syscalls = ctx.interfaces['syscalls']
;
; if target != "kernel":
; for id, scope, method in syscalls.methods:
; if scope:
; name = f"{scope.name}_{method.name}"
; else:
; name = method.name
; cog.outl(f"define_syscall {name:20}, {id}")
; ]]]
; [[[end]]]

View File

@@ -1,3 +1,4 @@
#include "j6/types.h"
#include <j6/errors.h>
#include <j6/protocols/vfs.hh>
#include <j6/syscalls.h>
@@ -12,6 +13,11 @@ client::client(j6_handle_t vfs_mb) :
{
}
client::client(const client& c) :
m_service {c.m_service}
{
}
inline size_t simple_strlen(const char *s) { size_t n = 0; while (s && *s) s++, n++; return n; }
j6_status_t
@@ -61,5 +67,31 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
}
j6_status_t
client::get_tag(char *tag, size_t &len)
{
if (len < sizeof(j6_status_t))
return j6_err_insufficient;
uint64_t message_tag = j6_proto_vfs_get_tag;
size_t handle_count = 0;
size_t in_len = 0;
j6_status_t s = j6_mailbox_call(m_service, &message_tag,
tag, &in_len, len, nullptr, &handle_count, 0);
if (s != j6_status_ok)
return s;
if (message_tag == j6_proto_base_status)
return *reinterpret_cast<j6_status_t*>(tag); // contains a status
if (message_tag != j6_proto_vfs_tag)
return j6_err_unexpected;
len = in_len;
return j6_status_ok; // data is now in `tag` and `len`
}
} // namespace j6::proto::vfs
#endif // __j6kernel

View File

@@ -1,3 +1,10 @@
; This file is part of the C standard library for the jsix operating
; system.
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at https://mozilla.org/MPL/2.0/.
extern _GLOBAL_OFFSET_TABLE_
extern main
@@ -5,6 +12,52 @@ extern exit
extern __init_libj6
extern __init_libc
extern __preinit_array_start
extern __preinit_array_end
extern __init_array_start
extern __init_array_end
global __run_ctor_list:function hidden (__run_ctor_list.end - __run_ctor_list)
__run_ctor_list:
push rbp
mov rbp, rsp
push rbx
push r12
mov rbx, rdi
mov r12, rsi
.start:
cmp rbx, r12
je .fin
mov rax, [rbx]
call rax
add rbx, 8
jmp .start
.fin:
pop r12
pop rbx
pop rbp
ret
.end:
global __run_global_ctors:function hidden (__run_global_ctors.end - __run_global_ctors)
__run_global_ctors:
push rbp
mov rbp, rsp
lea rdi, [rel __preinit_array_start]
lea rsi, [rel __preinit_array_end]
call __run_ctor_list
lea rdi, [rel __init_array_start]
lea rsi, [rel __init_array_end]
call __run_ctor_list
pop rbp
ret
.end:
; Put the address of the given symbol in rax
; This macro is the same as in util/got.inc,
; but crt0 can't have a dep on libutil
@@ -18,12 +71,13 @@ global _libc_crt0_start:function (_libc_crt0_start.end - _libc_crt0_start)
_start:
_libc_crt0_start:
mov rdx, [rsp] ; grab args pointer
mov r15, rsp ; grab initial stack pointer
push 0 ; Add null frame
push 0
mov rbp, rsp
mov rdi, r15
lookup_GOT __init_libj6
call rax
mov rbx, rax
@@ -31,10 +85,18 @@ _libc_crt0_start:
lookup_GOT __init_libc
call rax
mov rdi, 0
mov rsi, rsp
mov rdx, 0 ; TODO: actually parse stack for argc, argv, envp
mov rcx, rbx
call __run_global_ctors
; argc
mov rdi, [r15]
; argv
mov rsi, r15
add rsi, 8
; envp
lea rdx, [rsi + rdi*8 + 8]
lookup_GOT main
call rax

View File

@@ -0,0 +1,10 @@
#include <arch/amd64/errno.h>
static int errno_value = 0;
int *
__errno_location()
{
// TODO: thread-local errno
return &errno_value;
}

View File

@@ -1,33 +1,6 @@
#include <stddef.h>
using cb = void (*)(void);
extern cb __preinit_array_start;
extern cb __preinit_array_end;
extern cb __init_array_start;
extern cb __init_array_end;
namespace {
void
run_ctor_list(cb *p, cb *end)
{
while (p && end && p < end) {
if (p) (*p)();
++p;
}
}
void
run_global_ctors()
{
run_ctor_list(&__preinit_array_start, &__preinit_array_end);
run_ctor_list(&__init_array_start, &__init_array_end);
}
} // namespace
extern "C" void
__init_libc()
{
run_global_ctors();
}

View File

@@ -2,4 +2,13 @@
/// \file arch/amd64/errno.h
/// errno implementation for amd64
extern int errno;
#ifdef __cplusplus
extern "C" {
#endif
int * __errno_location();
#define errno (*__errno_location())
#ifdef __cplusplus
} // extern C
#endif

View File

@@ -12,9 +12,9 @@
/**[[[cog code generation
from j6libc import arch_includes
from codegen import arch_includes
arch_includes("errno.h")
arch_includes("errno.h", root="__j6libc")
]]]*/
/*[[[end]]]*/

View File

@@ -12,7 +12,7 @@
/**[[[cog code generation
import cog
from j6libc import definition, int_widths, int_mods
from codegen.int_types import definition, int_widths, int_mods
for width in int_widths:
definition("#define", f"PRId{width}", f"__INT{width}_FMTd__")

View File

@@ -10,4 +10,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#error setjmp.h is not yet implemented.
#include <stdint.h>
#include <stdnoreturn.h>
typedef uint64_t jmp_buf[9];
int setjmp(jmp_buf env);
_Noreturn void longjmp(jmp_buf env, int val);

View File

@@ -18,7 +18,7 @@ extern "C" {
#endif
/**[[[cog code generation
from j6libc import atomic_types
from codegen.int_types import atomic_types
deftypes = ["BOOL", "CHAR16", "CHAR32", "CHAR", "INT",
"LLONG", "SHORT", "WCHAR_T", "POINTER"]

View File

@@ -0,0 +1,76 @@
; This file is part of the C standard library for the jsix operating
; system.
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at https://mozilla.org/MPL/2.0/.
struc JMPBUF
.rbx: resq 1
.rsp: resq 1
.rbp: resq 1
.r12: resq 1
.r13: resq 1
.r14: resq 1
.r15: resq 1
.retaddr: resq 1
.mxcsr: resd 1
.fcw: resw 1
endstruc
global setjmp: function (setjmp.end - setjmp)
setjmp:
push rbp
mov rbp, rsp
mov rax, [rsp + 8]
mov [rdi + JMPBUF.retaddr], rax
mov [rdi + JMPBUF.rbx], rbx
mov [rdi + JMPBUF.rsp], rsp
mov [rdi + JMPBUF.rbp], rbp
mov [rdi + JMPBUF.r12], r12
mov [rdi + JMPBUF.r13], r13
mov [rdi + JMPBUF.r14], r14
mov [rdi + JMPBUF.r15], r15
stmxcsr [rdi + JMPBUF.mxcsr]
fnstcw [rdi + JMPBUF.fcw]
mov rax, 0 ; actual setjmp returns 0
pop rbp
ret
.end:
global longjmp: function (longjmp.end - longjmp)
longjmp:
push rbp
mov rbp, rsp
mov rbx, [rdi + JMPBUF.rbx]
mov rbp, [rdi + JMPBUF.rbp]
mov r12, [rdi + JMPBUF.r12]
mov r13, [rdi + JMPBUF.r13]
mov r14, [rdi + JMPBUF.r14]
mov r15, [rdi + JMPBUF.r15]
ldmxcsr [rdi + JMPBUF.mxcsr]
fnclex
fldcw [rdi + JMPBUF.fcw]
mov rsp, [rdi + JMPBUF.rsp]
mov rax, rsi
cmp rax, 0
jne .done
mov rax, 1
.done:
pop rbp
ret
.end:

View File

@@ -0,0 +1,18 @@
/** \file strtod.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
double strtod( const char * restrict nptr, char ** restrict endptr )
{
assert(!"strtod() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtof.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
float strtof( const char * restrict nptr, char ** restrict endptr )
{
assert(!"strtof() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtol.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
long strtol( const char * restrict nptr, char ** restrict endptr, int base )
{
assert(!"strtol() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtold.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
long double strtold( const char * restrict nptr, char ** restrict endptr )
{
assert(!"strtold() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtoll.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
long long strtoll( const char * restrict nptr, char ** restrict endptr, int base )
{
assert(!"strtoll() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtoul.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
unsigned long strtoul( const char * restrict nptr, char ** restrict endptr, int base )
{
assert(!"strtoul() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file strtoull.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <stdlib.h>
unsigned long long strtoull( const char * restrict nptr, char ** restrict endptr, int base )
{
assert(!"strtoull() NYI");
return 0;
}

View File

@@ -23,3 +23,5 @@ int memcmp(const void *s1, const void *s2, size_t n) {
return -1;
}
extern "C" int bcmp(const void *s1, const void *s2, size_t n)
__attribute__ ((weak, alias ("memcmp")));

View File

@@ -0,0 +1,18 @@
/** \file swprintf.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
int swprintf(wchar_t * restrict s, size_t n, const wchar_t * restrict format, ...)
{
assert(!"swprintf() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcslen.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
size_t wcslen(const wchar_t * s)
{
assert(!"wcslen() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstod.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
double wcstod(const wchar_t * restrict nptr, wchar_t ** restrict endptr)
{
assert(!"wcstod() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstof.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
float wcstof(const wchar_t * restrict nptr, wchar_t ** restrict endptr)
{
assert(!"wcstof() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstol.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
long wcstol(const wchar_t * restrict nptr, wchar_t ** restrict endptr, int base)
{
assert(!"wcstol() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstold.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
long double wcstold(const wchar_t * restrict nptr, wchar_t ** restrict endptr)
{
assert(!"wcstold() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstoll.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
long long wcstoll(const wchar_t * restrict nptr, wchar_t ** restrict endptr, int base)
{
assert(!"wcstoll() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstoul.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
unsigned long wcstoul(const wchar_t * restrict nptr, wchar_t ** restrict endptr, int base)
{
assert(!"wcstoul() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wcstoull.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
unsigned long long wcstoull(const wchar_t * restrict nptr, wchar_t ** restrict endptr, int base)
{
assert(!"wcstoull() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wmemchr.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
wchar_t * wmemchr(const wchar_t * s, wchar_t c, size_t n)
{
assert(!"wmemchr() NYI");
return 0;
}

View File

@@ -0,0 +1,18 @@
/** \file wmemcmp.cpp
*
* This file is part of the C standard library for the jsix operating
* system.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <assert.h>
#include <wchar.h>
int wmemcmp(const wchar_t * s1, const wchar_t * s2, size_t n)
{
assert(!"wmemcmp() NYI");
return 0;
}

View File

@@ -12,7 +12,7 @@
/**[[[cog code generation
import cog
from j6libc import definition, int_widths, int_mods
from codegen.int_types import definition, int_widths, int_mods
for width in int_widths:
definition("typedef", f"__INT{width}_TYPE__", f"int{width}_t;")

View File

@@ -1,19 +1,20 @@
#include <assert.h>
#include <j6/syslog.hh>
#include <pci/device.h>
#include "pci.h"
namespace pci {
struct pci_cap_msi
struct cap_msi
{
pci_cap::type id;
cap::type id;
uint8_t next;
uint16_t control;
} __attribute__ ((packed));
struct pci_cap_msi32
struct cap_msi32
{
pci_cap::type id;
cap::type id;
uint8_t next;
uint16_t control;
uint32_t address;
@@ -23,9 +24,9 @@ struct pci_cap_msi32
uint32_t pending;
} __attribute__ ((packed));
struct pci_cap_msi64
struct cap_msi64
{
pci_cap::type id;
cap::type id;
uint8_t next;
uint16_t control;
uint64_t address;
@@ -35,53 +36,23 @@ struct pci_cap_msi64
uint32_t pending;
} __attribute__ ((packed));
/*
void dump_msi(pci_cap_msi *cap)
{
auto cons = console::get();
cons->printf("MSI Cap:\n");
cons->printf(" id: %02x\n", cap->id);
cons->printf(" next: %02x\n", cap->next);
cons->printf("control: %04x\n", cap->control);
if (cap->control & 0x0080) {
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
cons->printf("address: %016x\n", cap64->address);
cons->printf(" data: %04x\n", cap64->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap64->mask);
cons->printf("pending: %08x\n", cap64->pending);
}
} else {
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
cons->printf("address: %08x\n", cap32->address);
cons->printf(" data: %04x\n", cap32->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap32->mask);
cons->printf("pending: %08x\n", cap32->pending);
}
}
cons->putc('\n');
};
*/
pci_device::pci_device() :
m_base(nullptr),
m_bus_addr(0),
m_vendor(0),
m_device(0),
m_class(0),
m_subclass(0),
m_progif(0),
m_revision(0),
m_header_type(0)
device::device() :
m_base {nullptr},
m_bus_addr {0, 0, 0},
m_vendor {0},
m_device {0},
m_class {0},
m_subclass {0},
m_progif {0},
m_revision {0},
m_header_type {0}
{
}
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
m_base(group.base_for(bus, device, func)),
m_msi(nullptr),
m_bus_addr(bus_addr(bus, device, func))
device::device(bus_addr addr, uint32_t *base) :
m_base {base},
m_msi {nullptr},
m_bus_addr {addr}
{
m_vendor = m_base[0] & 0xffff;
m_device = (m_base[0] >> 16) & 0xffff;
@@ -99,8 +70,10 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
uint16_t *status = command + 1;
j6::syslog(j6::logs::srv, j6::log_level::info, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
j6::syslog(j6::logs::srv, j6::log_level::info,
"Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
addr.bus, addr.device, addr.function,
m_class, m_subclass, m_progif, m_vendor, m_device);
j6::syslog(j6::logs::srv, j6::log_level::verbose, " = BAR0 %016lld", get_bar(0));
j6::syslog(j6::logs::srv, j6::log_level::verbose, " = BAR1 %016lld", get_bar(1));
@@ -109,13 +82,13 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
// Walk the extended capabilities list
uint8_t next = m_base[13] & 0xff;
while (next) {
pci_cap *cap = reinterpret_cast<pci_cap *>(util::offset_pointer(m_base, next));
next = cap->next;
//log::verbose(logs::device, " - found PCI cap type %02x", cap->id);
cap *c = reinterpret_cast<cap *>(util::offset_pointer(m_base, next));
next = c->next;
//log::verbose(logs::device, " - found PCI cap type %02x", c->id);
if (cap->id == pci_cap::type::msi) {
m_msi = cap;
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
if (c->id == cap::type::msi) {
m_msi = c;
cap_msi *mcap = reinterpret_cast<cap_msi *>(c);
mcap->control &= ~0x70; // at most 1 vector allocated
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
}
@@ -124,20 +97,17 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
}
uint32_t
pci_device::get_bar(unsigned i)
device::get_bar(unsigned i)
{
if (m_header_type == 0) {
assert(i < 6); // Device max BAR is 5
} else {
assert(m_header_type == 1); // Only device or bridge
assert(i < 2); // Bridge max BAR is 1
}
if ((m_header_type == 0 && i > 5) || // Device max BAR is 5
(m_header_type == 1 && i > 2)) // Bridge max BAR is 1
return 0;
return m_base[4+i];
}
void
pci_device::set_bar(unsigned i, uint32_t val)
device::set_bar(unsigned i, uint32_t val)
{
if (m_header_type == 0) {
assert(i < 6); // Device max BAR is 5
@@ -150,19 +120,19 @@ pci_device::set_bar(unsigned i, uint32_t val)
}
void
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
device::write_msi_regs(uintptr_t address, uint16_t data)
{
if (!m_msi)
return;
if (m_msi->id == pci_cap::type::msi) {
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
if (m_msi->id == cap::type::msi) {
cap_msi *mcap = reinterpret_cast<cap_msi *>(m_msi);
if (mcap->control & 0x0080) {
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
cap_msi64 *mcap64 = reinterpret_cast<cap_msi64 *>(m_msi);
mcap64->address = address;
mcap64->data = data;
} else {
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
cap_msi32 *mcap32 = reinterpret_cast<cap_msi32 *>(m_msi);
mcap32->address = address;
mcap32->data = data;
}
@@ -175,8 +145,33 @@ pci_device::write_msi_regs(uintptr_t address, uint16_t data)
}
}
bool
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
/*
void dump_msi(cap_msi *cap)
{
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
}
auto cons = console::get();
cons->printf("MSI Cap:\n");
cons->printf(" id: %02x\n", cap->id);
cons->printf(" next: %02x\n", cap->next);
cons->printf("control: %04x\n", cap->control);
if (cap->control & 0x0080) {
cap_msi64 *cap64 = reinterpret_cast<cap_msi64 *>(cap);
cons->printf("address: %016x\n", cap64->address);
cons->printf(" data: %04x\n", cap64->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap64->mask);
cons->printf("pending: %08x\n", cap64->pending);
}
} else {
cap_msi32 *cap32 = reinterpret_cast<cap_msi32 *>(cap);
cons->printf("address: %08x\n", cap32->address);
cons->printf(" data: %04x\n", cap32->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap32->mask);
cons->printf("pending: %08x\n", cap32->pending);
}
}
cons->putc('\n');
};
*/
} // namespace pci

View File

@@ -0,0 +1,55 @@
#include "pci/config.h"
#include <j6/syslog.hh>
#include <pci/config.h>
#include <pci/device.h>
#include <pci/group.h>
namespace pci {
//map config space into memory:
//inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
//map_phys(sys, group.base, pci::group::config_size, mmio_flags);
group::iterator::iterator(const group &g, bus_addr a) :
m_group {g}, m_addr {a}
{
increment_until_valid(false);
}
void
group::iterator::increment()
{
// If we're iterating functions, the device must be valid.
// Finish iterating all functions.
if (m_addr.function && m_addr.function < bus_addr::max_functions) {
++m_addr.function;
return;
}
m_addr.function = 0;
if (m_addr.device < bus_addr::max_devices) {
++m_addr.device;
return;
}
m_addr.device = 0;
++m_addr.bus;
}
void
group::iterator::increment_until_valid(bool pre_increment)
{
if (pre_increment)
increment();
bus_addr end_addr = {0, 0, uint16_t(m_group.bus_end+1)};
if (!m_group.has_device(m_addr) && m_addr < end_addr)
increment();
}
bool
group::has_device(bus_addr addr) const
{
return device_base(addr)->vendor != 0xffff;
}
} // namespace pci

View File

@@ -0,0 +1,28 @@
#pragma once
/// \file bus_addr.h
/// PCIe Bus Address structure
#include <stdint.h>
namespace pci {
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
struct bus_addr
{
static constexpr uint16_t max_functions = 8;
static constexpr uint16_t max_devices = 32;
uint16_t function : 3;
uint16_t device : 5;
uint16_t bus : 8;
uint16_t as_int() const { return *reinterpret_cast<const uint16_t*>(this); }
operator uint16_t() const { return as_int(); }
};
inline bool operator==(const bus_addr &a, const bus_addr &b) { return a.as_int() == b.as_int(); }
inline bool operator!=(const bus_addr &a, const bus_addr &b) { return a.as_int() != b.as_int(); }
inline bool operator< (const bus_addr &a, const bus_addr &b) { return a.as_int() < b.as_int(); }
inline bool operator> (const bus_addr &a, const bus_addr &b) { return a.as_int() > b.as_int(); }
} // namespace pci

View File

@@ -0,0 +1,131 @@
#pragma once
/// \file config.h
/// PCIe device configuration headers
#include <stdint.h>
#include <util/bitset.h>
namespace pci {
/// Command register bits
enum class control
{
io_space, // Respond to IO space access
mem_space, // Respond to memory space access
bus_master, // Allow acting as a bus master
special, // (Not in PCIe) Enable Special Cycle
mwi_enable, // (Not in PCIe) Enable Memory Write and Invalidate
vga_snoop, // (Not in PCIe) Enable VGA Palette Snoop
parity_err, // Enable PERR# assertion for parity errors
reserved7,
serr_enable, // Enable SERR# pin
fb2b_enable, // (Not in PCIe) Allow fast back-to-back
int_disable, // Disable INTx# assertions
};
/// Status register bits
enum class status
{
intr = 3, // Interrupt status
caps = 4, // Has capability list
mhz66 = 5, // (Not in PCIe) Can run at 66MHz
fb2b = 7, // (Not in PCIe) Supports fast back-to-back
md_parity = 8, // Master data parity error
tgt_abort_s = 11, // Target-Abort sent
tgt_abort_r = 12, // Target-Abort received
mst_abort_r = 13, // Master-Abort received
sys_err_s = 14, // SERR# asserted
parity = 15, // Parity error dedected
};
struct header_base
{
uint16_t vendor; // Vendor ID
uint16_t device; // Vendor's device ID
util::bitset16 command;
util::bitset16 status;
uint8_t revision; // Device revision
uint8_t device_class[3];
uint8_t cache_line; // Not used in PCIe
uint8_t master_latency; // Not used in PCIe
uint8_t header_type : 7;
uint8_t multi_device : 1;
uint8_t bist; // Built-in Self Test
};
struct header_unknown :
public header_base
{
uint8_t unknown1[36];
uint8_t caps;
uint8_t unknown2[7];
uint8_t int_line;
uint8_t int_pin;
uint8_t unknown3[2];
};
/// Device configuration header
struct header0 :
public header_base
{
uint32_t bar[6]; // Base address registers
uint32_t cis;
uint16_t subsystem_vendor;
uint16_t subsystem;
uint32_t exrom;
uint8_t caps;
uint8_t reserved1[3];
uint32_t reserved2;
uint8_t int_line;
uint8_t int_pin;
uint8_t min_gnt; // Not used in PCIe
uint8_t max_lat; // Not used in PCIe
};
/// Bridge configuration header
struct header1 :
public header_base
{
uint32_t bar[2]; // Base address registers
uint8_t pri_bus; // Not used in PCIe
uint8_t sec_bus;
uint8_t sub_bus;
uint8_t sec_lat; // Not used in PCIe
uint8_t io_base;
uint8_t io_limit;
uint16_t sec_status;
uint16_t mem_base;
uint16_t mem_limit;
uint16_t prefetch_mem_base;
uint16_t prefetch_mem_limit;
uint32_t prefetch_mem_base_high;
uint32_t prefetch_mem_limit_high;
uint16_t io_base_high;
uint16_t io_limit_high;
uint8_t caps;
uint8_t reserved1[3];
uint32_t exrom;
uint8_t int_line;
uint8_t int_pin;
uint16_t bridge_control;
};
} // namespace pci

View File

@@ -0,0 +1,88 @@
#pragma once
/// \file device.h
/// PCIe device
#include <stdint.h>
#include <pci/bus_addr.h>
#include <util/pointers.h>
namespace pci {
struct cap
{
enum class type : uint8_t
{
msi = 0x05,
msix = 0x11
};
type id;
uint8_t next;
} __attribute__ ((packed));
/// Information about a discovered PCIe device
class device
{
public:
/// Default constructor creates an empty object.
device();
/// Constructor
/// \arg addr The bus address of this device
/// \arg base Base address of this device's config space
device(bus_addr addr, uint32_t *base);
/// Check if this device is multi-function.
/// \returns True if this device is multi-function
inline bool multi() const { return m_multi; }
/// Get the bus address of this device/function
inline bus_addr addr() const { return m_bus_addr; }
/// Get the device class
/// \returns The PCI device class
inline uint8_t devclass() const { return m_class; }
/// Get the device subclass
/// \returns The PCI device subclass
inline uint8_t subclass() const { return m_subclass; }
/// Get the device program interface
/// \returns The PCI device program interface
inline uint8_t progif() const { return m_progif; }
/// Read one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \returns The contents of the BAR
uint32_t get_bar(unsigned i);
/// Write one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \arg val The value to write
void set_bar(unsigned i, uint32_t val);
/// Write to the MSI registers
/// \arg addr The address to write to the MSI address registers
/// \arg data The value to write to the MSI data register
void write_msi_regs(uintptr_t addr, uint16_t data);
private:
uint32_t *m_base;
cap *m_msi;
bus_addr m_bus_addr;
uint16_t m_vendor;
uint16_t m_device;
uint8_t m_class;
uint8_t m_subclass;
uint8_t m_progif;
uint8_t m_revision;
bool m_multi;
uint8_t m_header_type;
};
} // namespace pci

View File

@@ -0,0 +1,64 @@
#pragma once
/// \file pci.h
/// PCIe devices and groups
#include <stdint.h>
#include <pci/bus_addr.h>
#include <util/pointers.h>
namespace pci {
struct header_unknown;
/// Represents data about a PCI bus group from the ACPI MCFG
struct group
{
static constexpr size_t config_size = 0x1000'0000;
uint16_t group_id;
uint16_t bus_start;
uint16_t bus_end;
header_unknown *base;
/// Iterator that returns successive valid bus addresses for a group
struct iterator {
iterator(const group &g, bus_addr a = {0,0,0});
iterator(const iterator &i) : m_group {i.m_group}, m_addr {i.m_addr} {}
inline const bus_addr & operator*() const { return m_addr; }
inline const bus_addr * operator->() const { return &m_addr; }
inline bus_addr & operator*() { return m_addr; }
inline bus_addr * operator->() { return &m_addr; }
iterator & operator++() { increment_until_valid(); return *this; }
iterator operator++(int) { iterator old = *this; increment_until_valid(); 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:
void increment();
void increment_until_valid(bool pre_increment=true);
const group &m_group;
bus_addr m_addr;
};
iterator begin() const { return iterator {*this, {0, 0, bus_start}}; }
iterator end() const { return iterator {*this, {0, 0, uint16_t(bus_end + 1)}}; }
/// Get the base address of the MMIO configuration registers for a device
/// \arg addr The bus address of the device
/// \returns A pointer to the MMIO configuration registers
inline header_unknown* device_base(bus_addr addr) const {
return util::offset_pointer(base, addr.as_int() << 12);
}
/// Check if the given device function is present.
/// \arg addr The bus address of the device (relative to this group)
/// \returns True if the device function is present
bool has_device(bus_addr addr) const;
};
} // namespace pci

View File

@@ -0,0 +1,14 @@
# vim: ft=python
module("pci",
kind = "lib",
deps = [ "libc", "util" ],
sources = [
"device.cpp",
"group.cpp",
],
public_headers = [
"pci/bus_addr.h",
"pci/device.h",
"pci/group.h",
])

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