104 Commits

Author SHA1 Message Date
Justin C. Miller
17261622c6 [scripts] Add missing iced-x86 dependency for print_got.py 2024-02-19 16:20:10 -08:00
Justin C. Miller
dff9e53658 [init] Randomize load address of dynamic ELF modules
Update init to use the new xoroshiro RNG to create a random load address for dynamic executables.
Also pass an address past the end of the loader in the loader_arg to use when loading dynamic
dependencies.
2024-02-19 15:00:42 -08:00
Justin C. Miller
0bf709a339 [util] Add xoroshiro256++
Adding xoroshiro256++ psuedorandom number generator
2024-02-19 14:58:20 -08:00
Justin C. Miller
c245949ea4 [ld.so] Add dynamic library linking
ld.so will now go through all DT_NEEDED entries in the dynamic table and load and relocate
those shared libraries as well. Lazy linking of functions via the PLT is not yet supported,
all PLT entries are looked up ahead of time by ld.so.
2024-02-18 17:39:42 -08:00
Justin C. Miller
c27f8baa31 [libj6] Fix the infinite loop in simple_strlen
The simple_strlen function was incrementing n but not advancing the pointer.
2024-02-18 17:29:43 -08:00
Justin C. Miller
f51f519c31 [kernel] Allow passing 0 to vma_resize to query the current size
Passing a size of 0 in to vma_resize will now not attempt to alter the VMA size, but will
still put the size into the passed-in pointer. Using this allows querying the size of a
VMA without changing it.
2024-02-18 17:27:07 -08:00
Justin C. Miller
55a485ee67 [elf] Add get_section_by_name
Add the get_section_by_name function to iterate sections and compare name strings.
2024-02-18 17:22:23 -08:00
Justin C. Miller
ba6e8e1349 [libc] Pull crt0 out into its own module
Sorta. crt0 is a separate module as far as bonnibel is concerned, but it's
still part of the libc module file.
2024-02-13 22:41:40 -08:00
Justin C. Miller
75d30fb56d [scripts] Add debugging help for linked lists and GOT
Adding in scripts that i wrote to examine GOT/PLT/DYNAMIC in shared libs, as well
as a GDB pretty-printer for util::linked_list.
2024-02-13 22:36:50 -08:00
Justin C. Miller
4abcf238a0 [bonnibel] Add SONAME to shared objects
Add -soname to the linker options when making shared libraries, so that they
have a SONAME string in their DYNAMIC section.
2024-02-13 22:33:51 -08:00
Justin C. Miller
c05b4211fa [libj6] Make sure thread stacks are aligned
Stacks were not 16-byte aligned when coming into j6::thread::init_proc,
make sure it aligns stacks for SSE by adding the `force_align_arg_pointer`
attribute.
2024-02-13 20:06:10 -08:00
Justin C. Miller
337b8bb36b [panic] Don't spin the CPU in panic
While waiting for the main panicing CPU to finish, don't spin the CPU
without using `asm("pause")` to lower power consumption. Some panics can
get stuck in this state and oh man do my fans spin up.
2023-08-31 19:43:26 -07:00
Justin C. Miller
97433fc7d1 [libc] Properly call init functions and main through GOT
In the CRT startup code, when linked in a PIC executable, jumps to
`__init_libj6`, `__init_libc`, `main`, and `exit` were not linked
correctly. They needed a bit more support for looking up the GOT, and
getting the symbol address out of it.

Now libutil has a `got.inc` file for inclusion in asm code that needs to
reference symbols from the GOT.
2023-08-31 19:42:14 -07:00
Justin C. Miller
fc16ed54b3 [kernel] Change VMA syscall addr param to inout
This change allows the `vma_map` and `vma_create_map` syscalls to map to
addresses other than the one specified, and therefore makes the address
parameter to those syscalls `inout` in order to return the mapped
address.

Also add the `exact` flag for specifying that mapping needs to be done
at the exact address given. If the mapping collides with another, the
new `j6_err_collision` error is returned.
2023-08-31 19:40:02 -07:00
Justin C. Miller
8cbde13139 [build] Address symbol visibility and DSO builds
Added an `API` macro in `j6/api.h` that expands to mark the given
declaration as a default-visible symbol. Also change `format` and
`vformat` to non-template functions, and make calls to `main`, `exit`,
and the library init functions in `_start` GOT-relative.
2023-08-26 19:30:26 -07:00
Justin C. Miller
646a534dfd [libj6] Remove driver_main
The `driver_main` sinature was an alternate signature for `main`
implemented with weak symbols, but it causes linking issues when not
statically linked, and drivers are going to work differently soon
anyway. Just get rid of it for now.
2023-08-26 19:23:13 -07:00
Justin C. Miller
eda816ad90 [build] Add build knowledge of dynamic libraries
Bonnibel will now build dynamic libraries when they're dependencies for
non-statically linked modules. It will also copy those shared libraries
into the initrd image for programs being copied into the image.
2023-08-26 19:19:04 -07:00
Justin C. Miller
c4bb60299e [ld.so] Add a minimal dynamic linker
This commit includes a number of changes to enable loading of PIE
executables:

- The loader in srv.init checks for a `PT_INTERP` segment in the program
  its loading, and if it exists, loads the specified interpreter and
  passes control to it instead of the program itself.
- Added ld.so the dynamic linker executable and set it as the
  interpreter for all user-target programs.
- Program initial stack changed again to now contain a number of
  possible tagged structures, including a new one for ld.so's arguments,
  and for passing handles tagged with protocol ids.
- Added a stub for a new VFS protocol. Unused so far, but srv.init will
  need to serve VFS requests from ld.so once I transition libraries to
  shared libs for user-target programs. (Right now all executables are
  PIE but statically linked, so they only need internal relocations.)
- Added 16 and 8 bit variants of `util::bitset`. This ended up not being
  used, but could be useful.
2023-08-12 22:55:37 -07:00
Justin C. Miller
3cfd0cf86b [libj6] Make j6::thread a template for lambdas
Instead of a C-style function pointer taking `void *userdata`, let
j6::thread take a lambda as its thread procedure.
2023-08-09 21:08:45 -07:00
Justin C. Miller
8b3fa3ed01 [kernel] Make mailbox non-fixed-length again
Going back to letting mailboxes use variable-length data. Note that this
requires extra copies, so shared memory channels should be used for
anything in the hot path. But this allows better RPC over mailboxes and
other flexibility.

Other changes:
- added a j6::proto::sl::client class to act as a service locator
  client, instead of duplicating that code in every program.
- moved protocol ids into j6/tables/protocols.inc so that C++ clients
  can easily have their own API
2023-08-07 22:59:03 -07:00
Justin C. Miller
a0f91ed0fd [kernel] Allow handle lists in syscall definitions
This change allows for parameters in definition files to have the "list"
option while also needing the handle verification. The verify function
will now iterate through the list checking capabilities and types on
every valid handle in the list.
2023-08-06 18:54:01 -07:00
Justin C. Miller
77590b31a6 [build] Remove unneeded lib options
Remove the `-lc++`, `-lc++abi`, and `-lunwind` options from the user
target, where they should be handled automatically. (ie, we're not using
`-nostdlib` or its bretheren.)
2023-08-06 10:46:45 -07:00
Justin C. Miller
ce27749705 [srv.init] Allow init loader to load dynamic ELFs
Use the `elf::file::valid()` change to allow the loader to load `ET_DYN`
binaries, and add a static offset to all load addresses for them.
2023-08-06 10:42:25 -07:00
Justin C. Miller
b239bb6df2 [util] Fix a non-explicit-typed shift in sized_uint
In basic_types.h, `sized_uint<N>` was shifting a mask by the bit width
of the type, which meant it wouldn't work for 64 bit types.
2023-08-05 17:43:14 -07:00
Justin C. Miller
28379dc0f6 [libj6] Add symbol sizes to syscall stubs
Add extra info to the NASM `global` directive to specify the sizes of
these symbols, mostly so they look right in `nm` or `readelf` and don't
trick me into thinking something is wrong.
2023-08-05 17:40:24 -07:00
Justin C. Miller
c86c0f2ae6 [elf] Allow checking for different file types
Previously `elf::file::valid()` only returned true for ELF files of type
`ET_EXEC`, now allow passing in of an expected file type.
2023-08-05 17:37:45 -07:00
Justin C. Miller
bbe27c6b53 [build] Move init to its own target
In order to allow -fpic and -fpie in the user target, move init to it's
own target -- it needs its own special build rules to make it loadable
by boot.
2023-07-31 00:16:02 -07:00
Justin C. Miller
21916ab869 [build] Refactor build options definitions
Split out build definition YAML files to allow different options based
on config, target, kind of module, and target/kind combination.
2023-07-30 23:44:04 -06:00
Justin C. Miller
778e766f6b [libj6] Fix a memcpy return address bug
My `REP.MOVSB` `memcpy` implementation had marked its C++ variable
constraints as output instead of input, causing the compiler to emit
code to copy the values of `$rsi` and `$rdi` back into the `src` and
`dst` pointers, so after the copy `dst` pointed to the memory just
beyond what had been copied.

Very few places actually used the return value from `memcpy`, so this
went unnoticed for a bit..
2023-07-12 19:46:02 -07:00
Justin C. Miller
5d1fdd0e81 [all] Reference headers in src instead of copying
This is the second of two big changes to clean up includes throughout
the project. Since I've started using clangd with Neovim and using
VSCode's intellisense, my former strategy of copying all header files
into place in `build/include` means that the real files don't show up in
`compile_commands.json` and so display many include errors when viewing
those header files in those tools.

That setup was mostly predicated on a desire to keep directory depths
small, but really I don't think paths like `src/libraries/j6/j6` are
much better than `src/libraries/j6/include/j6`, and the latter doesn't
have the aforementioned issues, and is clearer to the casual observer as
well.

Some additional changes:

- Added a new module flag `copy_headers` for behavior similar to the old
  style, but placing headers in `$module_dir/include` instead of the
  global `build/include`. This was needed for external projects that
  don't follow the same source/headers folder structure - in this case,
  `zstd`.
- There is no longer an associated `headers.*.ninja` for each
  `module.*.ninja` file, as only parsed headers need to be listed; this
  functionality has been moved back into the module's ninja file.
2023-07-12 19:45:43 -07:00
Justin C. Miller
f5208d1641 [all] Remove dependencies on non-freestanding libc
This is the first of two rather big changes to clean up includes
throughout the project. In this commit, the implicit semi-dependency on
libc that bonnibel adds to every module is removed. Previously, I was
sloppy with includes of libc headers and include directory order. Now,
the freestanding headers from libc are split out into libc_free, and an
implicit real dependency is added onto this module, unless `no_libc` is
set to `True`. The full libc needs to be explicitly specified as a
dependency to be used.

Several things needed to change in order to do this:

- Many places use `memset` or `memcpy` that cannot depend on libc. The
  kernel has basic implementations of them itself for this reason. Now
  those functions are moved into the lower-level `j6/memutils.h`, and
  libc merely references them. Other modules are now free to reference
  those functions from libj6 instead.
- The kernel's `assert.h` was renamed kassert.h (matching its `kassert`
  function) so that the new `util/assert.h` can use `__has_include` to
  detect it and make sure the `assert` macro is usable in libutil code.
- Several implementation header files under `__libj6/` also moved under
  the new libc_free.
- A new `include_phase` property has been added to modules for Bonnibel,
  which can be "normal" (default) or "late" which uses `-idirafter`
  instead of `-I` for includes.
- Since `<utility>` and `<new>` are not freestanding, implementations of
  `remove_reference`, `forward`, `move`, and `swap` were added to the
  `util` namespace to replace those from `std`, and `util/new.h` was
  added to declare `operator new` and `operator delete`.
2023-07-12 19:38:31 -07:00
Justin C. Miller
a7beb0df18 [util] Add moving append() call to vector
Add a version of `append()` that takes an rvalue reference.
2023-07-10 01:45:31 -07:00
Justin C. Miller
1ec46ee641 [util] Use chunk_size alias instead of N in deque
Using an alias to increase readability. No functional changes.
2023-07-10 01:44:19 -07:00
Justin C. Miller
3ba0600694 [tools] Add --no-build option to qemu.sh
Add an option to tell qemu.sh not to build before running. Also update
paths to be more explicit about where log files go.
2023-07-10 01:43:07 -07:00
Justin C. Miller
a449a88395 [user] Update logging and return in user apps
Update some userspace apps to return more distinct error messages and
make better use of j6::syslog.
2023-07-10 01:41:55 -07:00
Justin C. Miller
4bf03266a9 [libj6] Account for double-mapped ring buffers in j6::channel
When keeping track of addresses to give to the kernel for channel shared
memory, double the channel's size since the kernel will double-map the
area as a ring buffer.
2023-07-10 01:37:31 -07:00
Justin C. Miller
4bceac3d56 [kernel] Check for null handle arg in mailbox_call
The handle argument to `mailbox_call` is optional, so needs to be
manually checked by the syscall handler before dereferencing.
2023-07-10 01:34:19 -07:00
Justin C. Miller
350396d70f [tools] Commit memory debug (et al) tooling
These are some changes I made to debug tooling while tracking down the
bugfix in the previous commit.

Each `scripts/debug_*_alloc.gdb` script has gdb output a `*_allocs.txt`
file, which in turn can be parsed by the `scripts/parse_*_allocs.py`
script to find errors.
2023-07-10 01:31:07 -07:00
Justin C. Miller
ad3afae315 [kernel] Fix a heap double-allocate bug
In the heap allocator, new blocks allocated directly for an allocate
request (instead of indirectly as part of a block split) would only set
their order in the tracking map, not their free flag. This left
uninitialized data in the block info map, which thus meant it was marked
as free for looking up for merges. (Not for allocations, since the block
didn't actually appear in the free list.)
2023-07-10 01:24:13 -07:00
Justin C. Miller
0dc86f2a0d [misc] Get VSCode running / debugging working
I spent some time getting VSCode debugging working. Now I can use VSCode
on windows to work on jsix in Linux (WSL) and launch and debug it within
QEMU. So many layers but it works pretty nicely!
2023-07-07 16:19:47 -07:00
Justin C. Miller
2b3c276f33 [util] Abstract out radix_tree class from page_tree
Generalized the radix tree code from page_tree as util::radix_tree so
that it can be used elsewhere.
2023-07-04 17:43:23 -07:00
Justin C. Miller
8bf2425c4a [tools] Make qemu.sh debugcon output optional
I got sick of closing the debugcon window all the time when I only need
it sometimes, so it's now gated behind the '-x' option to qemu.sh.
2023-07-04 16:23:47 -07:00
Justin C. Miller
72530ccb15 [misc] Remove vscode artifacts
If I use vscode again at some point and set it up to run jsix nicely
I'll readd.
2023-07-02 17:56:09 -07:00
Justin C. Miller
da14fd123e [kernel] Add MXCSR handling, mask SIMD exceptions
Now threads inherit their MXCSR (sans exception state bits) SIMD
settings from their creator. By default, all exceptions are masked, and
both "to zero" flags are set.
2023-05-21 14:48:27 -07:00
Justin C. Miller
f215b98f74 [panic] Fix display of r14 & r15 in panic display
The register outputs for r14 and r15 when panic printed out any set of
CPU registers was incorrectly reusing r12 & r13 instead.
2023-05-20 13:07:40 -07:00
Justin C. Miller
b5662bfd25 [kernel] Initial XSAVE support implementation
Initial support for XSAVE, but not XSAVEOPT or XSAVEC:

- Enable XSAVE and set up xcr0 for all CPUs
- Allocate XSAVE area for all non-kernel threads
- Call XSAVE and XRSTOR on task switch
2023-05-05 12:04:37 -06:00
Justin C. Miller
3b3857548c [libcpu] Add CPU_FEATURE_WRN
The new CPU_FEATURE_WRN macro in the cpu features list will cause the
kernel to emit a warning but not panic if the feature is missing.
2023-05-01 20:35:12 -06:00
Justin C. Miller
1e2e154747 [libcpu] Fix CPUID register overwriting bug
Well god damnit, when i converted the `cpu::cpu_id::regs` struct to a
union, i was super sloppy and forgot to wrap the existing fields in
their own anonymous struct. I have been wrong about CPUID vales for
ages.
2023-04-30 15:05:23 -06:00
Justin C. Miller
2d8d4fd200 [libc] Fix random SSE alignment GP faults
The libc CRT _start function had a stray pop left in it, which was
causing the stack to never be 16-byte aligned and thus causing crashes
when SSE instructions were called.
2023-04-09 15:20:17 -07:00
Justin C. Miller
459b40ad57 [kernel] Fix merge conflict getting committed in cpu.cpp 2023-03-16 20:08:05 -07:00
Justin C. Miller
90cf1e2220 [tools] Use less for viewing debugcon output in qemu.sh
Allow scrollback and keeping around the pane after qemu exits by using
less as the viewer for the log output.
2023-03-16 20:01:40 -07:00
Justin C. Miller
692e0d8656 [drv.uart] Replace user code with new channels
Move all the user space uses of channels to use j6::channel.
2023-03-16 19:59:28 -07:00
Justin C. Miller
3ab1a6b170 [util] Remove asserts from util::bitset
When used in kernel vs. non-kernel code the assert macros were not
working as expected. Other util code does not use assert like this, so
I'm just dropping it from bitset.
2023-03-16 19:59:28 -07:00
Justin C. Miller
069bc63d1f [kernel] Return j6_err_timed_out when event_wait times out
Previously event_wait was incorrectly returning j6_status_ok when timing
out.
2023-03-16 19:59:28 -07:00
Justin C. Miller
373121c455 [libj6] Take out explicit type IDs from object_types.inc
This caused errors when there were gaps, so don't allow explicit IDs,
and just number types in order.
2023-03-16 19:59:28 -07:00
Justin C. Miller
936b12a977 [kernel] Deal with getting multiple log entries in debugcon
Getting entries from the logger object can return multiple in the same
buffer - debugcon was not accounting for the subsequent entries.
2023-03-16 19:59:28 -07:00
Justin C. Miller
2a4c286f2b [kernel] Fix error when log data wraps ring buffer
The offset wasn't being properly masked.
2023-03-16 19:59:28 -07:00
Justin C. Miller
bfab4f085e [cpu] Rename cpu_id::validate() to cpu_id::features()
Validate wasn't a correct name anymore. Also move the features enum out
of the cpu_id class scope and into the `cpu` namespace directly.
2023-03-16 19:59:24 -07:00
Justin C. Miller
201e7191ef [kernel] Make scheduler run queue's prev be an id, not a pointer
This would lead to errors in GDB's j6threads when the previous thread
had already exited.
2023-03-16 19:56:14 -07:00
Justin C. Miller
9fa588566f [kernel] First steps at removing channel objects
This commit does a number of things to start the transition of channels
from kernel to user space:

- Remove channel objects / syscalls from the kernel
- Add mutex type in libj6
- Add condition type in libj6
- Add a `ring` type flag for VMA syscalls to create ring buffers
- Implement a rudimentary shared memory channel using all of the above
2023-03-16 19:56:14 -07:00
Justin C. Miller
ed95574c24 [kernel] Add (wip) futex syscalls
Add the syscalls j6_futex_wait and j6_futex_wake. Currently marking this
as WIP as they need more testing.

Added to support futexes:
- vm_area and vm_space support for looking up physical address for a
  virtual address
- libj6 mutex implementation using futex system calls
2023-03-16 19:56:14 -07:00
Justin C. Miller
0c777bc62f [util] Add move-assignment operators to node_map, deque
Also add them to wait_queue as a wrapper to calling deque's
move-assignmen operator.
2023-03-16 19:56:14 -07:00
Justin C. Miller
1d7d5e8015 [tools] Allow for pointer-to-integer types in .def files
The upcoming futex syscalls will be easier to use (and to auto verify)
if passed a pointer instead of an address, this allows for changing a
`Primitive` to a `PrimitiveRef` by adding a `*` to the end.
2023-03-16 19:56:14 -07:00
Justin C. Miller
95627ba43c [kernel] Set mxcsr and xcr0 in cpu_early_init
There are some SSE instructions (moveaps, moveups) in userland code that
QEMU software emulation seems to be fine with but generate `#UD` on KVM.
So let's finally get floating-point support working. This is the first
step, just setting the control regs to try to fix that error.
2023-02-23 18:22:22 -08:00
Justin C. Miller
841ac41e36 [tools] Rearrange qemu tmux windows on smaller displays
So I can still use ./qemu.sh --debug on my laptop.
2023-02-22 21:14:10 -08:00
Justin C. Miller
a46d8bce37 [libc] Mark cast_to as always_inline
I've seen the compiler emit a call to cast_to, which it should never do.
2023-02-20 11:36:36 -08:00
Justin C. Miller
4052911ac4 [kernel] Clean up log areas
Removing several log areas that are not used
2023-02-20 11:35:44 -08:00
Justin C. Miller
3a7a18011c [init] Move PCIe probing to srv.init
This was kept in the kernel as a way to keep exercising the code, but it
doesn't belong there. This moves it to init, which doesn't do anything
but probe for devices currently - but at least it's executing the code
in userspace now.
2023-02-20 11:23:49 -08:00
Justin C. Miller
15e2f8abf3 [boot] Stop picking largest video mode
For now, using VNC, I want to keep the framebuffer mode small, so I'm
commenting out the bootloader's loop to pick the biggest video mode.
I'll revisit this as a bootconfig option later.
2023-02-20 11:21:35 -08:00
Justin C. Miller
6b43ddc8d7 [build] Update user build flags
Make symbols hidden by default, explicitly set architecture.
2023-02-20 11:20:46 -08:00
Justin C. Miller
508058c3d7 [uefi_fb] Fix log messages not showing in drv.uefi_fb
Also add drv.uefi_fb to the default manifest.
2023-02-20 11:18:59 -08:00
Justin C. Miller
4c9ff44b1c [init] Switch init to driver_main instead of custom _start
init still uses a custom _start to set up the stack, but then jumps to
_libc_crt0_start. The modules data passed to it is taken from the
j6_init_args instead of having it stashed into a global variable.

Also replace several uses of snprintf/j6_log with j6::syslog.
2023-02-20 11:15:08 -08:00
Justin C. Miller
c092e07832 [libj6] Allow driver_main instead of main for accepting extra arguments
Clang will complain if main() is not declared with 0, 2, or 3 arguments.
In order to allow an extra 4th parameter, a new weak main() symbol which
just jumps to driver_main is defined, and _start passes the extra init
pointer to main.

Additionally, libc's crt0.s _start is made weak, and a matching
_libc_crt0_start symbol is defined for implementations that wish to
override _start but still call libc's _start. (Will be used by init.)
2023-02-20 11:05:53 -08:00
Justin C. Miller
abe7fe37d0 [libj6] Add formatting j6::syslog wrapper for j6_log
To replace all the places where snprintf/j6_log are called with buffers
on the stack for most frames.
2023-02-20 11:01:45 -08:00
Justin C. Miller
cca8e8b3ad [init] Let init pass module data to drivers
First pass at passing module data to drivers in init. Also fix some
remaining __handle_self references.
2023-02-19 14:44:16 -08:00
Justin C. Miller
7c194950bb [uart] Make UART driver single-threaded
The design of the UART driver was needlessly multi-threaded and a source
of threading bugs. Just make it single-threaded.
2023-02-19 14:41:55 -08:00
Justin C. Miller
723f7d0330 [kernel] Delete processes & threads only via refcounts
Previously processes and threads would be deleted by the scheduler. Now,
only delete them based on refcounts - this allows joining an
already-exited thread, for instance.
2023-02-19 14:37:31 -08:00
Justin C. Miller
274891854f [kernel] Simplify event and wait_queue
Previously event tried to read its value in event::wake_observer, which
required jumping through some hoops in how wait_queue was designed, so
that a value wouldn't be wasted if the wait_queue was empty. Now, read
the event value in event::wait after returning from the thread::block
call instead, which simplifies the whole process and lets us simplify
the wait_queue API as well.
2023-02-19 14:34:03 -08:00
Justin C. Miller
94b2a79f79 [kernel] Remove process & thread self-handles
For the coming switch to cap/handle ref-counting being the main lifetime
determiner of objects, get rid of self handles for threads and processes
to avoid circular references. Instead, passing 0 to syscalls expecting a
thread or process handle signifies "this process/thread".
2023-02-19 11:23:23 -08:00
Justin C. Miller
d2a6113fb7 [kernel] Fix frame allocation for multiple pages
There was an inverted boolean logic in determining how many consecutive
pages were available.

Also adding some memory debugging tools I added to track down the recent
memory bugs:

- A direct debugcon::write call, for logging to the debugcon without the
  possible page faults with the logger.
- A new vm_space::lock call, to make a page not fillable in memory
  debugging mode
- A mode in heap_allocator to always alloc new pages, and lock freed
  pages to cause page faults for use-after-free bugs.
- Logging in kobject on creation and deletion
- Page table cache structs are now page-sized for easy pointer math
2023-02-19 01:07:13 -08:00
Justin C. Miller
55c88dd943 [many] Fix many cases of 1 << n exceeding the size of int
Yet again burned by the fack that integer literals are assumed to be of
type int, so `1 << n` is 0 for any n >= 32. This burned me in the frame
allocator, but I also grepped for all instances of `1 <<` and fixed
those too.
2023-02-18 19:53:04 -08:00
Justin C. Miller
42db1e8899 [kernel] Add lock-releasing version of thread::block()
Add a version of thread::block() that takes a lock and releases it after
marking the thread as unready, but before calling the scheduler.

Use this version of block() in the wait_queue.
2023-02-18 17:21:39 -08:00
Justin C. Miller
38ca7004a6 [util] Add thread id to kernel spinlocks
Expose a function __current_thread_id() and use it to record the thread
id on a spinlock waiter when called from the kernel.
2023-02-18 15:21:56 -08:00
Justin C. Miller
8817766469 [kernel] Keep other threads out of idle priority
Split out different constants for scheduler::idle_priority and
scheduler::max_priority, so that threads never fall to the same priority
level as the idle threads.
2023-02-18 14:17:57 -08:00
Justin C. Miller
e250aaef30 [kernel] Exit the current thread last on process exit
Previously process::exit() was going through the threads in order
calling thread::exit() - which blocks and never wakes if called on the
current thread. Since the current thread likely belongs to the process
which is exiting, and the current thread wasn't guaranteed to be last in
the list, this could leave threads not cleaned up.

Worse, no matter what, this caused the m_threads_lock to always be held
forever on exit, keeping the scheduler from ever finishing a call to
process::thread_exited() on its threads.
2023-02-18 14:05:22 -08:00
Justin C. Miller
7b29ba7d23 [uart] Pass com1 address directly to thread proc
To avoid a race condition I was seeing where the child thread was
reading g_com1 and seeing it as null, now we just pass the pointer as an
argument.
2023-02-14 20:35:03 -08:00
Justin C. Miller
dc30437ce7 [kernel] Remove page_table's cache counter
The `s_cache_count` counter had the potential to get out of sync with
the cache itself. Since we only call `fill_table_page_cache()` when the
cache is empty, the counter was not useful. After chasing the bug for
hours to figure out how they were getting out of sync, I just ripped it
out.
2023-02-14 20:29:40 -08:00
Justin C. Miller
2c2398b549 [kernel] Protect process::m_threads with a lock
Another spot I meant to go back and clean up with a lock - found it when
a process with threads running on two CPUs exited, and the scheduler
tried to delete the process on both CPUs.
2023-02-14 20:25:19 -08:00
Justin C. Miller
bce01591f3 [kernel] Improve debugcon & panic display
Several minor changes related to debug output.

- Colorize the debugcon logger like the userspace one.
- Display the process and thread for each cpu in the panic display
- Skip the panic() frame in panic back traces
- Don't try to follow obviously bad (non-canonical) stack frame pointers
2023-02-14 20:18:56 -08:00
Justin C. Miller
847ef1ccfe [tools] Make qemu.sh --debug also show the debugcon
Made qemu.sh split another tmux pane to show the debugcon output as well
when debugging. Thanks, ultrawide monitor.
2023-02-14 20:14:33 -08:00
Justin C. Miller
6fa9b33ac0 [debugging] Add j6log gdb command
Now that the log ring buffer is at a fixed known address, and entries
persist even after being read, it's easy to add a command to see what's
in the buffer from GDB. Useful if there's some log messages that hadn't
yet been printed at the time of a panic.
2023-02-14 20:10:30 -08:00
Justin C. Miller
df6d5b3b16 [debugging] Fix gdb script koid refs & panic CPU display
Two minor debugging helpers:

- the GDB script was still referencing m_koid on objects, switched to
  the replacement m_obj_id instead.
- finally gave in and made panic print 1-based CPU ids like GDB uses
  instead of 0-based like the hardware and LITERALLY EVERYTHING ELSE
2023-02-10 17:46:21 -08:00
Justin C. Miller
4884a624d9 [kernel] Make panic::panic not inline
Panic is referenced everywhere (mostly through kassert being always
inline). It's also so much easier to breakpoint on panic in GDB this
way.
2023-02-10 17:44:17 -08:00
Justin C. Miller
ea587076ed [init] Go back to boot modules having inline data
In order to pass along arguments like the framebuffer, it's far simpler
to have that data stored along with the modules than mapping new pages
for every structure. Also now optionally pass a module's data to a
driver as init starts it.
2023-02-10 01:01:01 -08:00
Justin C. Miller
0eddb002f0 [libj6] Create a standard definition of the log entry type
Move logger::entry to libj6 as j6_log_entry, and use that everywhere. It
was silly that it was copied into srv.logger and drv.uefi_fb
2023-02-10 00:57:00 -08:00
Justin C. Miller
8f968f4954 [uart] Fix uart driver & testapp j6::thread usage
The prior change to j6::thread allowing arguments did not test drv.uart
or testapp. Oops.
2023-02-08 23:20:44 -08:00
Justin C. Miller
094b54d728 [tests] Get mailbox test running again
This commit fixes the mailbox tests in test_runner, which broke when
mailbox was simplified to just use call and respond. It also fixes a
bug the tests uncovered: if the mailbox is closed while a caller is in
the reply map (ie, when its call data has been passed on to a thread
calling respond, but has yet to be responded to itself), that caller is
never awoken.
2023-02-08 23:16:22 -08:00
Justin C. Miller
4125175870 [kernel] Give threads initial arguments
This commit changes the add_user_thunk to point to a new routine,
initialize_user_cpu, which sets all the registers that were previously
unset when starting a new user thread. The values for rdi and rsi are
popped off the initial stack values that add_user_thunk sets up, so that
user thread procs can take up to two arguments.

To suppor this, j6_thread_create gained two new arguments, which are
passed on to the thread.

This also let me finally get rid of the hack of passing an argument in
rsp when starting init.
2023-02-08 23:10:17 -08:00
Justin C. Miller
1cb8f1258d [testapp] Re-add testapp to default manifest
This commit re-adds testapp to the default manifest and does some
housecleaning on the module:

- Remove the old serial.* and io.*
- Update it to use current syscall APIs
- Update it to use libj6's higher-level thread API
2023-02-08 22:44:05 -08:00
Justin C. Miller
f05a1d3310 [kernel] Revive the debugcon logger as a kernel thread
The debugcon logger is now separate from logger::output, and is instead
a kernel-internal thread that watches for logs and prints them to the
deubcon device.
2023-02-08 22:32:01 -08:00
Justin C. Miller
71069cb38b [kernel] Empty wait_queue after calling clear()
Bugfix - wait_queue::clear() was not emptying out its util::deque after
waking all the threads, so it would just grow forever.
2023-02-08 22:29:49 -08:00
Justin C. Miller
393db1e792 [kernel] Switch logger from bip to ring buffer
A bip-buffer is good for producer/consumer systems, but ideally logs
will stay in the buffer until they're ousted because they need to be
overwritten. Now they're a regular ring buffer and every entry has an
incremental id. Consumers pass in the last id they've seen, and will get
the next log in the sequence.
2023-02-07 20:19:02 -08:00
Justin C. Miller
0a097ec7d3 [kernel] Add add_existing() to page_tree
This ended up being unused, but still probably useful: Refactor out the
"find" logic of page_tree::find_or_add (note that this is different than
the "find" logic of page_tree::find, as it potentially modifies the tree
to add a location to accommodate the page being searched for) into a new
page_tree::get_entry method. That was then used to add an add_existing
method for inserting pages into the page_tree.
2023-02-07 19:40:12 -08:00
Justin C. Miller
ada660deeb [kernel] Move log buffer to its own memory section
In prep for the coming change to keep log entries as a true ring buffer,
move the log buffer from bss into its own memory section.

Related changes in this commit:
- New vm_area_ring, which maps a set of pages twice to allow easy linear
  reading of data from a ring buffer when it wraps around the end.
- logger_init() went away, and the logger ctor is called from
  mem::initialize()
- Instead of an event object, the logger just has a bare wait_queue
- util::counted::from template type changed slightly to allow easy
  conversion from an intptr_t as well as a pointer
- Previously added debugcon_logger code removed - this will be added in
  a separate file in a followup commit
2023-02-08 09:21:52 -08:00
305 changed files with 6216 additions and 2797 deletions

5
.gitignore vendored
View File

@@ -3,7 +3,7 @@
/build* /build*
*.bak *.bak
tags tags
jsix.log *.log
*.out *.out
*.o *.o
*.a *.a
@@ -13,3 +13,6 @@ sysroot
__pycache__ __pycache__
/venv /venv
compile_commands.json compile_commands.json
buddy_allocs.txt
frame_allocs.txt
heap_allocs.txt

19
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/src/libraries/**",
"${workspaceFolder}/build/**",
"${workspaceFolder}/sysroot/include"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"compileCommands": "${workspaceFolder}/compile_commands.json"
}
],
"version": 4
}

32
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "QEMU Debug Server",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/jsix.elf",
"args": [],
"cwd": "${workspaceFolder}",
"logging": {
//"engineLogging": true,
"programOutput": true
},
"stopAtConnect": true,
"stopAtEntry": false,
"setupCommands": [
{"text": "dashboard -enabled off", "ignoreFailures": true}
],
"MIMode": "gdb",
"miDebuggerServerAddress": "localhost:1234",
"debugServerPath": "${workspaceFolder}/qemu.sh",
"debugServerArgs": "--debug --no-build",
"serverLaunchTimeout": 5000,
}
]
}

43
.vscode/tasks.json vendored
View File

@@ -1,28 +1,41 @@
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "0.1.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"taskName": "make", "label": "Ninja",
"command": "make.bat", "type": "shell",
"isBuildCommand": true "command": "source ${workspaceFolder}/venv/bin/activate.fish; ninja",
"detail": "Build the project",
"options": {
"cwd": "${workspaceFolder}/build"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}, },
{ {
"taskName": "clean", "label": "Run QEMU",
"command": "make.bat", "command": "./qemu.sh",
"args": [ "clean" ], "args": [ "--no-build", "--kvm" ],
"isShellCommand": true "dependsOn": ["Ninja"],
"options": {
"cwd": "${workspaceFolder}"
},
}, },
{ {
"taskName": "qemu (windowed)", "label": "clean",
"command": "qemu-win.bat", "command": "${workspaceFolder}/venv/bin/ninja",
"showOutput": "never", "options": {
"isTestCommand": true "cwd": "${workspaceFolder}/build"
}, },
{ "args": [
"taskName": "qemu", "-t",
"command": "qemu.bat" "clean"
]
} }
] ]
} }

View File

@@ -1,39 +0,0 @@
---
variables:
cc: "${source_root}/sysroot/bin/clang"
cxx: "${source_root}/sysroot/bin/clang++"
ld: "${source_root}/sysroot/bin/ld.lld"
ar: ar
nasm: nasm
objcopy: objcopy
ccflags: [
"-I${source_root}/src/include",
"-I${source_root}/src/include/x86_64",
"-fcolor-diagnostics",
"-U__STDCPP_THREADS__",
"-D_LIBCPP_HAS_NO_THREADS",
"-DVERSION_MAJOR=${version_major}",
"-DVERSION_MINOR=${version_minor}",
"-DVERSION_PATCH=${version_patch}",
"-DVERSION_GITSHA=0x${version_sha}",
'-DGIT_VERSION=\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
'-DGIT_VERSION_WIDE=L\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
"-Wformat=2", "-Winit-self", "-Wfloat-equal", "-Winline", "-Wmissing-format-attribute",
"-Wmissing-include-dirs", "-Wswitch", "-Wundef", "-Wdisabled-optimization",
"-Wpointer-arith", "-Wno-attributes", "-Wno-sign-compare", "-Wno-multichar",
"-Wno-div-by-zero", "-Wno-endif-labels", "-Wno-pragmas", "-Wno-format-extra-args",
"-Wno-unused-result", "-Wno-deprecated-declarations", "-Wno-unused-function",
"-Wno-address-of-packed-member", "-Wno-invalid-offsetof", "-Wno-format-nonliteral",
"-Werror" ]
asflags: [
"-DVERSION_MAJOR=${version_major}",
"-DVERSION_MINOR=${version_minor}",
"-DVERSION_PATCH=${version_patch}",
"-DVERSION_GITSHA=0x${version_sha}",
"-I${source_root}/src/include" ]
cflags: [ "-std=c11" ]
cxxflags: [ "-std=c++17" ]

View File

@@ -1,30 +0,0 @@
---
extends: base
variables:
ld: clang++
ccflags: [
"-nostdlib",
"-nodefaultlibs",
"-fno-builtin",
"-I${source_root}/external",
"--target=x86_64-unknown-windows",
"-ffreestanding",
"-mno-red-zone",
"-fshort-wchar",
"-fno-omit-frame-pointer",
"-ggdb",
"-g3" ]
cxxflags: [ "-fno-exceptions", "-fno-rtti" ]
ldflags: [
"--target=x86_64-unknown-windows",
"-nostdlib",
"-Wl,-entry:efi_main",
"-Wl,-subsystem:efi_application",
"-fuse-ld=lld-link",
"-g" ]

View File

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

39
assets/build/global.yaml Normal file
View File

@@ -0,0 +1,39 @@
---
cc: "${source_root}/sysroot/bin/clang"
cxx: "${source_root}/sysroot/bin/clang++"
ld: "${source_root}/sysroot/bin/ld.lld"
ar: ar
nasm: nasm
objcopy: objcopy
ccflags: [
"-I${source_root}/src/include",
"-fcolor-diagnostics",
"-U__STDCPP_THREADS__",
"-D_LIBCPP_HAS_NO_THREADS",
"-D__jsix_config=${build_config}",
"-D__jsix_config_${build_config}",
"-DVERSION_MAJOR=${version_major}",
"-DVERSION_MINOR=${version_minor}",
"-DVERSION_PATCH=${version_patch}",
"-DVERSION_GITSHA=0x${version_sha}",
'-DGIT_VERSION=\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
'-DGIT_VERSION_WIDE=L\"${version_major}.${version_minor}.${version_patch}+${version_sha}\"',
"-Wformat=2", "-Winit-self", "-Winline", "-Wmissing-format-attribute",
"-Wmissing-include-dirs", "-Wswitch", "-Wundef", "-Wdisabled-optimization",
"-Wpointer-arith", "-Wno-attributes", "-Wno-sign-compare", "-Wno-multichar",
"-Wno-div-by-zero", "-Wno-endif-labels", "-Wno-pragmas", "-Wno-format-extra-args",
"-Wno-unused-result", "-Wno-deprecated-declarations", "-Wno-unused-function",
"-Wno-address-of-packed-member", "-Wno-invalid-offsetof", "-Wno-format-nonliteral",
"-Werror" ]
asflags: [
"-DVERSION_MAJOR=${version_major}",
"-DVERSION_MINOR=${version_minor}",
"-DVERSION_PATCH=${version_patch}",
"-DVERSION_GITSHA=0x${version_sha}",
"-I${source_root}/src/include" ]
cflags: [ "-std=c11" ]
cxxflags: [ "-std=c++17" ]

View File

@@ -1,59 +0,0 @@
---
extends: base
variables:
asflags: [ "-I${source_root}/src/kernel/" ]
ccflags: [
"--target=x86_64-jsix-elf",
"-fno-stack-protector",
"-I${source_root}/external",
"-nostdinc",
"-nostdlib",
"-ffreestanding",
"-nodefaultlibs",
"-fno-builtin",
"-fno-plt",
"-mno-sse",
"-fno-omit-frame-pointer",
"-mno-red-zone",
"-mcmodel=kernel",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-g3",
"-ggdb",
"-D__ELF__",
"-D__jsix__",
"-D__j6kernel",
"-U__linux",
"-U__linux__",
"-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1",
"-DPRINTF_INCLUDE_CONFIG_H=1",
"-isystem${build_root}/include/libc",
"-isystem${source_root}/sysroot/include",
"--sysroot='${source_root}/sysroot'" ]
cflags: [ '-nostdinc' ]
cxxflags: [
"-fno-exceptions",
"-fno-rtti",
"-nostdinc",
"-isystem${source_root}/sysroot/include/c++/v1" ]
ldflags: [
"-g",
"-m", "elf_x86_64",
"-nostdlib",
"-Bstatic",
"--no-eh-frame-hdr",
"-z", "norelro",
"-z", "separate-code" ]

View File

@@ -1,8 +1,6 @@
# This file is automatically generated by bonnibel
rule compile.c rule compile.c
command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in command = $cc -MMD -MF $out.d $cflags $ccflags -o $out -c $in
description = Compiling $name description = Compiling [$target]:$name
depfile = $out.d depfile = $out.d
deps = gcc deps = gcc
@@ -17,7 +15,7 @@ rule dump_c_run
rule compile.cpp rule compile.cpp
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
description = Compiling $name description = Compiling [$target]:$name
depfile = $out.d depfile = $out.d
deps = gcc deps = gcc
@@ -32,25 +30,33 @@ rule dump_cpp_run
rule compile.s rule compile.s
command = $nasm -o $out -felf64 -MD $out.d $asflags $in command = $nasm -o $out -felf64 -MD $out.d $asflags $in
description = Assembling $name description = Assembling [$target]:$name
depfile = $out.d depfile = $out.d
deps = gcc deps = gcc
rule parse.cog rule parse.cog
command = cog -o $out -d -D target=$target $cogflags $in command = cog -o $out -d -D target=$target $cogflags $in
description = Parsing $name description = Parsing [$target]:$name
rule exe rule exe
command = $ld $ldflags -o $out $in $libs command = $ld $ldflags -o $out $in $libs
description = Linking $name description = Linking exe [$target]:$name
rule driver
command = $ld $ldflags -o $out $in $libs
description = Linking driver [$target]:$name
rule lib rule lib
command = $ld -shared -soname $soname $ldflags -o $out $in $libs
description = Linking [$target]:$name
rule lib_static
command = $ar qcs $out $in command = $ar qcs $out $in
description = Archiving $name description = Archiving [$target]:$name
rule cp rule cp
command = cp $in $out command = cp $in $out
description = Copying $name description = Copying [$target]:$name
rule dump rule dump
command = objdump -DSC -M intel $in > $out command = objdump -DSC -M intel $in > $out

View File

@@ -0,0 +1,26 @@
---
ld: clang++
ccflags: [
"-nostdlib",
"-nodefaultlibs",
"-fno-builtin",
"-I${source_root}/external",
"--target=x86_64-unknown-windows",
"-ffreestanding",
"-mno-red-zone",
"-fshort-wchar",
"-fno-omit-frame-pointer",
]
cxxflags: [ "-fno-exceptions", "-fno-rtti" ]
ldflags: [
"--target=x86_64-unknown-windows",
"-nostdlib",
"-Wl,-entry:efi_main",
"-Wl,-subsystem:efi_application",
"-fuse-ld=lld-link",
]

View File

@@ -0,0 +1,39 @@
---
ccflags: [
"--target=x86_64-jsix-elf",
"-fno-omit-frame-pointer",
"-fno-stack-protector",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-D__ELF__",
"-D__jsix__",
"-U__linux",
"-U__linux__",
"-DMSPACES",
"--sysroot='${source_root}/sysroot'"
]
cxxflags: [
"-fno-exceptions",
"-fno-rtti",
]
ldflags: [
"-Bstatic",
"-m", "elf_x86_64",
"--sysroot='${source_root}/sysroot'",
"--no-eh-frame-hdr",
"-L", "${source_root}/sysroot/lib",
"-z", "separate-code",
"-lc++", "-lc++abi", "-lunwind",
"--no-dependent-libraries",
]
libs: [
"${target_dir}/crt0.o",
]

View File

@@ -0,0 +1,52 @@
---
asflags: [ "-I${source_root}/src/kernel/" ]
ccflags: [
"--target=x86_64-jsix-elf",
"-fno-stack-protector",
"-I${source_root}/external",
"-nostdinc",
"-nostdlib",
"-ffreestanding",
"-nodefaultlibs",
"-fno-builtin",
"-fno-plt",
"-mno-sse",
"-fno-omit-frame-pointer",
"-mno-red-zone",
"-mcmodel=kernel",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-D__ELF__",
"-D__jsix__",
"-D__j6kernel",
"-U__linux",
"-U__linux__",
"-DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1",
"-DPRINTF_INCLUDE_CONFIG_H=1",
"--sysroot='${source_root}/sysroot'"
]
cflags: [ '-nostdinc' ]
cxxflags: [
"-fno-exceptions",
"-fno-rtti",
"-nostdinc",
]
ldflags: [
"-m", "elf_x86_64",
"-nostdlib",
"-Bstatic",
"--no-eh-frame-hdr",
"-z", "norelro",
"-z", "separate-code"
]

View File

@@ -0,0 +1,15 @@
---
ccflags: [
"-fpie"
]
ldflags: [
"-pie",
"--dynamic-linker", "/jsix/lib/ld.so",
"--push-state", "--as-needed", "-Bstatic", "-lc++", "-lc++abi", "-lunwind", "--pop-state",
]
libs: [
"${target_dir}/crt0.o",
]

View File

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

View File

@@ -0,0 +1,33 @@
---
asflags: []
ccflags: [
"--target=x86_64-jsix-elf",
"-fno-omit-frame-pointer",
"-fno-stack-protector",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-D__ELF__",
"-D__jsix__",
"-U__linux",
"-U__linux__",
"--sysroot='${source_root}/sysroot'",
"-fpic",
]
cxxflags: [
"-fno-exceptions",
"-fno-rtti",
]
ldflags: [
"-m", "elf_x86_64",
"--sysroot='${source_root}/sysroot'",
"--no-eh-frame-hdr",
"-L", "${source_root}/sysroot/lib",
"-z", "separate-code",
"--no-dependent-libraries",
]

View File

@@ -1,38 +0,0 @@
---
extends: base
variables:
asflags: [ "-I${source_root}/src/kernel/" ]
ccflags: [
"--target=x86_64-jsix-elf",
"-fno-omit-frame-pointer",
"-fno-stack-protector",
"-g",
"-D__ELF__",
"-D__jsix__",
"-U__linux",
"-U__linux__",
"-isystem${source_root}/sysroot/include",
"-isystem${build_root}/include/libc",
"--sysroot='${source_root}/sysroot'" ]
cxxflags: [
"-fno-exceptions",
"-fno-rtti",
"-isystem${source_root}/sysroot/include/c++/v1" ]
ldflags: [
"-g",
"-Bstatic",
"--sysroot='${source_root}/sysroot'",
"-L", "${source_root}/sysroot/lib",
"-z", "separate-code",
"-lc++", "-lc++abi", "-lunwind",
"--no-dependent-libraries",
]

View File

@@ -1,8 +1,14 @@
import gdb import gdb
import gdb.printing import gdb.printing
import sys
sys.path.append('./scripts')
import re
from collections import namedtuple from collections import namedtuple
Capability = namedtuple("Capability", ["id", "parent", "refcount", "caps", "type", "koid"]) Capability = namedtuple("Capability", ["id", "parent", "refcount", "caps", "type", "koid"])
LogEntry = namedtuple("LogHeader", ["id", "bytes", "severity", "area", "message"])
class PrintStackCommand(gdb.Command): class PrintStackCommand(gdb.Command):
def __init__(self): def __init__(self):
@@ -168,12 +174,12 @@ class GetThreadsCommand(gdb.Command):
rsp = int(gdb.parse_and_eval(f"{tcb}->rsp")) rsp = int(gdb.parse_and_eval(f"{tcb}->rsp"))
pri = int(gdb.parse_and_eval(f"{tcb}->priority")) pri = int(gdb.parse_and_eval(f"{tcb}->priority"))
flags = int(gdb.parse_and_eval(f"{thread}->m_state")) flags = int(gdb.parse_and_eval(f"{thread}->m_state"))
koid = int(gdb.parse_and_eval(f"{thread}->m_koid")) koid = int(gdb.parse_and_eval(f"{thread}->m_obj_id"))
proc = int(gdb.parse_and_eval(f"{thread}->m_parent.m_koid")) proc = int(gdb.parse_and_eval(f"{thread}->m_parent.m_obj_id"))
creator = int(gdb.parse_and_eval(f"{thread}->m_creator")) creator = int(gdb.parse_and_eval(f"{thread}->m_creator"))
if creator != 0: if creator != 0:
creator_koid = int(gdb.parse_and_eval(f"{thread}->m_creator->m_koid")) creator_koid = int(gdb.parse_and_eval(f"{thread}->m_creator->m_obj_id"))
creator = f"{creator_koid:x}" creator = f"{creator_koid:x}"
else: else:
creator = "<no thread>" creator = "<no thread>"
@@ -233,11 +239,7 @@ class GetThreadsCommand(gdb.Command):
self.print_cpudata(cpu) self.print_cpudata(cpu)
previous = int(gdb.parse_and_eval(f"{runlist}.prev")) previous = int(gdb.parse_and_eval(f"{runlist}.prev"))
if previous != 0: print(f" prev: {previous:x}")
tcb = f"((TCB*){previous:#x})"
thread = f"({tcb}->thread)"
koid = int(gdb.parse_and_eval(f"{thread}->m_koid"))
print(f" prev: {koid:x}")
print() print()
for pri in range(8): for pri in range(8):
@@ -283,6 +285,79 @@ class PrintProfilesCommand(gdb.Command):
print(f"{name:>{max_len}}: {avg:15.3f}") print(f"{name:>{max_len}}: {avg:15.3f}")
class DumpLogCommand(gdb.Command):
level_names = ["", "fatal", "error", "warn", "info", "verbose", "spam"]
def __init__(self):
super().__init__("j6log", gdb.COMMAND_DATA)
from memory import Layout
layout = Layout("definitions/memory_layout.yaml")
for region in layout.regions:
if region.name == "logs":
self.base_addr = region.start
break
self.areas = []
area_re = re.compile(r"LOG\(\s*(\w+).*")
with open("src/libraries/j6/include/j6/tables/log_areas.inc", 'r') as areas_inc:
for line in areas_inc:
m = area_re.match(line)
if m:
self.areas.append(m.group(1))
def get_entry(self, addr):
addr = int(addr)
size = int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->bytes"))
mlen = size - 8
return LogEntry(
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->id")),
size,
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->severity")),
int(gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->area")),
gdb.parse_and_eval(f"((j6_log_entry*){addr:#x})->message").string(length=mlen))
def invoke(self, arg, from_tty):
start = gdb.parse_and_eval("g_logger.m_start & (g_logger.m_buffer.count - 1)")
end = gdb.parse_and_eval("g_logger.m_end & (g_logger.m_buffer.count - 1)")
if end < start:
end += gdb.parse_and_eval("g_logger.m_buffer.count")
print(f"Logs are {start} -> {end}")
addr = self.base_addr + start
end += self.base_addr
while addr < end:
entry = self.get_entry(addr)
if entry.bytes < 8:
print(f"Bad log header size: {entry.bytes}")
break
addr += entry.bytes
area = "??"
if entry.area < len(self.areas):
area = self.areas[entry.area]
level = self.level_names[entry.severity]
print(f"{area:>7}:{level:7} {entry.message}")
class ShowCurrentProcessCommand(gdb.Command):
def __init__(self):
super().__init__("j6current", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
def get_obj_and_id(name):
obj = int(gdb.parse_and_eval(f"((cpu_data*)$gs_base)->{name}"))
oid = -1
if obj != 0:
oid = int(gdb.parse_and_eval(f"((obj::kobject*){obj:#x})->m_obj_id"))
return obj, oid
process, pid = get_obj_and_id("process")
thread, tid = get_obj_and_id("thread")
print(f"{pid:02x}/{tid:02x} [ {process:x} / {thread:x} ]")
class CapTablePrinter: class CapTablePrinter:
def __init__(self, val): def __init__(self, val):
node_map = val["m_caps"] node_map = val["m_caps"]
@@ -307,7 +382,7 @@ class CapTablePrinter:
refcount = int(node["holders"]), refcount = int(node["holders"]),
caps = int(node["caps"]), caps = int(node["caps"]),
type = str(node["type"])[14:], type = str(node["type"])[14:],
koid = node['object']['m_koid'])) koid = node['object']['m_obj_id']))
self.nodes.sort(key=lambda n: n.id, reverse=True) self.nodes.sort(key=lambda n: n.id, reverse=True)
@@ -399,11 +474,39 @@ class HandleSetPrinter:
return self._iterator(self.node_map['m_nodes'], self.capacity) return self._iterator(self.node_map['m_nodes'], self.capacity)
class LinkedListPrinter:
def __init__(self, llist):
self.name = llist.type.tag
self.head = llist['m_head']
self.tail = llist['m_tail']
self.items = []
current = self.head
while current:
item = current.dereference()
self.items.append((str(len(self.items)), item))
current = item['m_next']
class _iterator:
def __iter__(self):
return self
def __next__(self):
raise StopIteration
def to_string(self):
return f"{self.name}[{len(self.items)}]"
def children(self):
return self.items
def build_pretty_printers(): def build_pretty_printers():
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix") pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
pp.add_printer("cap table", '^cap_table$', CapTablePrinter) pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
pp.add_printer("handle set", '^util::node_set<unsigned long, 0, heap_allocated>$', HandleSetPrinter) pp.add_printer("handle set", '^util::node_set<unsigned long, 0, heap_allocated>$', HandleSetPrinter)
pp.add_printer("vector", '^util::vector<.*>$', VectorPrinter) pp.add_printer("vector", '^util::vector<.*>$', VectorPrinter)
pp.add_printer("linked list", '^util::linked_list<.*>$', LinkedListPrinter)
return pp return pp
gdb.printing.register_pretty_printer( gdb.printing.register_pretty_printer(
@@ -415,8 +518,11 @@ PrintBacktraceCommand()
TableWalkCommand() TableWalkCommand()
GetThreadsCommand() GetThreadsCommand()
PrintProfilesCommand() PrintProfilesCommand()
DumpLogCommand()
ShowCurrentProcessCommand()
gdb.execute("display/i $rip") gdb.execute("display/i $rip")
gdb.execute("define hook-quit\nkill\nend")
if not gdb.selected_inferior().was_attached: if not gdb.selected_inferior().was_attached:
gdb.execute("add-symbol-file build/panic.serial.elf") gdb.execute("add-symbol-file build/panic.serial.elf")
gdb.execute("target remote :1234") gdb.execute("target remote :1234")

View File

@@ -35,7 +35,7 @@ name: IDENTIFIER
options: "[" ( OPTION | IDENTIFIER )+ "]" options: "[" ( OPTION | IDENTIFIER )+ "]"
description: COMMENT+ description: COMMENT+
PRIMITIVE: INT_TYPE | "size" | "string" | "buffer" | "address" PRIMITIVE: INT_TYPE "*"? | "size" | "string" | "buffer" | "address"
INT_TYPE: /u?int(8|16|32|64)?/ INT_TYPE: /u?int(8|16|32|64)?/
NUMBER: /(0x)?[0-9a-fA-F]+/ NUMBER: /(0x)?[0-9a-fA-F]+/
UID: /[0-9a-fA-F]{16}/ UID: /[0-9a-fA-F]{16}/

View File

@@ -8,5 +8,9 @@ panic:
- panic.serial - panic.serial
services: services:
- srv.logger - srv.logger
- testapp
drivers: drivers:
- drv.uart - drv.uart
- drv.uefi_fb
libs:
- ld.so

View File

@@ -9,5 +9,3 @@ panic:
- panic.serial - panic.serial
services: services:
- test_runner - test_runner
drivers:
- drv.uart

1
configure vendored
View File

@@ -35,6 +35,7 @@ def generate(output, config, manifest):
"source_root": root, "source_root": root,
"build_root": output, "build_root": output,
"module_root": path, "module_root": path,
"config": config,
} }
code = compile(open(modfile, 'r').read(), modfile, "exec") code = compile(open(modfile, 'r').read(), modfile, "exec")

View File

@@ -24,3 +24,6 @@
- name: buffers - name: buffers
size: 64G size: 64G
- name: logs
size: 2G

View File

@@ -1,21 +0,0 @@
object channel : object {
uid 3ea38b96aa0e54c8
capabilities [
send
receive
close
]
method create [constructor]
method close [destructor cap:close]
method send [cap:send] {
param data buffer [inout]
}
method receive [cap:receive] {
param data buffer [out]
param flags uint64
}
}

View File

@@ -1,5 +1,6 @@
# Mailboxes are objects that enable synchronous IPC via short message-passing # Mailboxes are objects that enable synchronous IPC via arbitrary
# of tagged handles. # message-passing of tagged data and/or handles. Not as efficient
# as shared memory channels, but more flexible.
object mailbox : object { object mailbox : object {
uid 99934ad04ece1e07 uid 99934ad04ece1e07
@@ -13,13 +14,14 @@ object mailbox : object {
method create [constructor] method create [constructor]
method close [destructor cap:close] method close [destructor cap:close]
# Send a message to the reciever, and block until a # Send a message to the reciever, and block until a response is
# response is sent. Note that getting this response # sent. Note that getting this response does not require the
# does not require the receive capability. # receive capability.
method call [cap:send] { method call [cap:send] {
param tag uint64 [inout] param tag uint64 [inout]
param subtag uint64 [inout] param data buffer [optional inout]
param give_handle ref object [optional inout handle] param data_in_len size # number of bytes in data used for input
param handles ref object [optional inout handle list]
} }
# Respond to a message sent using call, and wait for another # Respond to a message sent using call, and wait for another
@@ -28,8 +30,9 @@ object mailbox : object {
# to waiting for a new message. # to waiting for a new message.
method respond [cap:receive] { method respond [cap:receive] {
param tag uint64 [inout] param tag uint64 [inout]
param subtag uint64 [inout] param data buffer [optional inout]
param give_handle ref object [optional inout handle] param data_in_len size # number of bytes in data used for input
param handles ref object [optional inout handle list]
param reply_tag uint64 [inout] param reply_tag uint64 [inout]
param flags uint64 param flags uint64
} }

View File

@@ -10,8 +10,9 @@ object system : object {
change_iopl change_iopl
] ]
# Get a log line from the kernel log # Get the next log line from the kernel log
method get_log [cap:get_log] { method get_log [cap:get_log] {
param seen uint64 # Last seen log id
param buffer buffer [out zero_ok] # Buffer for the log message data structure param buffer buffer [out zero_ok] # Buffer for the log message data structure
} }

View File

@@ -7,9 +7,11 @@ object thread : object {
] ]
method create [constructor] { method create [constructor] {
param process ref process [cap:create_thread] param process ref process [optional cap:create_thread]
param stack_top address param stack_top address
param entrypoint address param entrypoint address
param arg0 uint64
param arg1 uint64
} }
method kill [destructor cap:kill] method kill [destructor cap:kill]

View File

@@ -17,20 +17,21 @@ object vma : object {
method create_map [constructor cap:map] { method create_map [constructor cap:map] {
param size size param size size
param address address param address address [inout]
param flags uint32 param flags uint32
} }
method map [cap:map] { method map [cap:map] {
param process ref process param process ref process [optional]
param address address param address address [inout]
param flags uint32
} }
method unmap [cap:unmap] { method unmap [cap:unmap] {
param process ref process param process ref process [optional]
} }
method resize [cap:resize] { method resize [cap:resize] {
param size size [inout] param size size [inout] # New size for the VMA, or 0 to query the current size without changing
} }
} }

View File

@@ -1,6 +1,5 @@
import "objects/object.def" import "objects/object.def"
import "objects/channel.def"
import "objects/event.def" import "objects/event.def"
import "objects/mailbox.def" import "objects/mailbox.def"
import "objects/process.def" import "objects/process.def"
@@ -12,12 +11,12 @@ interface syscalls [syscall] {
uid 01d9b6a948961097 uid 01d9b6a948961097
expose ref object expose ref object
expose ref system
expose ref event expose ref event
expose ref process
expose ref thread
expose ref mailbox expose ref mailbox
expose ref channel expose ref process
expose ref system
expose ref thread
expose ref vma expose ref vma
# Simple no-op syscall for testing # Simple no-op syscall for testing
@@ -43,6 +42,19 @@ interface syscalls [syscall] {
param mask uint32 # The capability bitmask param mask uint32 # The capability bitmask
} }
# Block waiting on a futex
function futex_wait [static] {
param address uint32* # Address of the futex value
param current uint32 # Current value of the futex
param timeout uint64 # Wait timeout in nanoseconds
}
# Wake threads waiting on a futex
function futex_wake [static] {
param address uint32* # Address of the futex value
param count uint64 # Number of threads to wake, or 0 for all
}
# Testing mode only: Have the kernel finish and exit QEMU with the given exit code # Testing mode only: Have the kernel finish and exit QEMU with the given exit code
function test_finish [test] { function test_finish [test] {
param exit_code uint32 param exit_code uint32

View File

@@ -3,8 +3,8 @@
zstd = module("zstd", zstd = module("zstd",
root = "${source_root}/external/zstd", root = "${source_root}/external/zstd",
kind = "lib", kind = "lib",
deps = [ ], copy_headers = True,
output = "libzstd.a", deps = [ "libc" ],
sources = [ sources = [
"decompress/zstd_decompress.c", "decompress/zstd_decompress.c",
"decompress/zstd_ddict.c", "decompress/zstd_ddict.c",

75
qemu.sh
View File

@@ -1,7 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
build="$(dirname $0)/build" root=$(dirname $0)
assets="$(dirname $0)/assets" build="${root}/build"
assets="${root}/assets"
no_build=""
debug="" debug=""
isaexit='-device isa-debug-exit,iobase=0xf4,iosize=0x04' isaexit='-device isa-debug-exit,iobase=0xf4,iosize=0x04'
debugtarget="${build}/jsix.elf" debugtarget="${build}/jsix.elf"
@@ -9,7 +12,9 @@ gfx="-nographic"
vga="-vga none" vga="-vga none"
log="" log=""
kvm="" kvm=""
cpu="Broadwell,+pdpe1gb" debugcon_cmd=""
cpu_features=",+pdpe1gb,+invtsc,+hypervisor,+x2apic,+xsavec,+xsaves,+xsaveopt"
cpu="Broadwell"
smp=4 smp=4
while true; do while true; do
@@ -35,14 +40,14 @@ while true; do
;; ;;
-r | --remote) -r | --remote)
shift shift
vnchost="${1:-${VNCHOST:-localhost}}" vnchost="${1:-${VNCHOST:-"localhost:5500"}}"
gfx="-vnc ${vnchost}:7000,reverse" gfx="-vnc ${vnchost},reverse"
vga="" vga=""
shift shift
;; ;;
-k | --kvm) -k | --kvm)
kvm="-enable-kvm" kvm="-enable-kvm"
cpu="host" cpu="max"
shift shift
;; ;;
-c | --cpus) -c | --cpus)
@@ -50,7 +55,15 @@ while true; do
shift 2 shift 2
;; ;;
-l | --log) -l | --log)
log="-d mmu,int,guest_errors -D jsix.log" log="-d mmu,int,guest_errors -D ${root}/jsix.log"
shift
;;
-x | --debugcon)
debugcon_cmd="less --follow-name -R +F debugcon.log"
shift
;;
--no-build)
no_build="yes"
shift shift
;; ;;
*) *)
@@ -67,17 +80,44 @@ if [[ ! -c /dev/kvm ]]; then
kvm="" kvm=""
fi fi
if ! ninja -C "${build}"; then [[ -z "${no_build}" ]] && if ! ninja -C "${build}"; then
exit 1 exit 1
fi fi
if [[ -n $TMUX ]]; then if [[ -n $TMUX ]]; then
cols=$(tput cols)
log_width=100
if [[ -n $debug ]]; then if [[ -n $debug ]]; then
tmux split-window -h "gdb ${debugtarget}" & log_cols=1
if [[ $debugcon_cmd ]]; then
log_cols=2
fi
gdb_width=$(($cols - $log_cols * $log_width))
if (($gdb_width < 150)); then
stack=1
gdb_width=$(($cols - $log_width))
tmux split-window -h -l $gdb_width "gdb ${debugtarget}"
if [[ $debugcon_cmd ]]; then
tmux select-pane -t .left
tmux split-window -v "$debugcon_cmd"
fi
else else
tmux split-window -h -l 100 "sleep 1; telnet localhost 45455" & if [[ $debugcon_cmd ]]; then
tmux split-window -h -l $(($log_width + $gdb_width)) "$debugcon_cmd"
fi
tmux split-window -h -l $gdb_width "gdb ${debugtarget}"
tmux select-pane -t .left
tmux select-pane -t .right
fi
else
if [[ $debugcon_cmd ]]; then
tmux split-window -h -l $log_width "$debugcon_cmd"
tmux last-pane tmux last-pane
tmux split-window -l 10 "sleep 1; telnet localhost 45454" & fi
tmux split-window -l 10 "sleep 1; telnet localhost 45454"
fi fi
elif [[ $DESKTOP_SESSION = "i3" ]]; then elif [[ $DESKTOP_SESSION = "i3" ]]; then
if [[ -n $debug ]]; then if [[ -n $debug ]]; then
@@ -87,19 +127,24 @@ elif [[ $DESKTOP_SESSION = "i3" ]]; then
fi fi
fi fi
if [[ -n "${debug}" ]]; then
(sleep 1; echo "Debugging ready.") &
fi
qemu-system-x86_64 \ qemu-system-x86_64 \
-drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \ -drive "if=pflash,format=raw,readonly=on,file=${assets}/ovmf/x64/ovmf_code.fd" \
-drive "if=pflash,format=raw,file=${build}/ovmf_vars.fd" \ -drive "if=pflash,format=raw,file=${build}/ovmf_vars.fd" \
-drive "format=raw,file=${build}/jsix.img" \ -drive "format=raw,file=${build}/jsix.img" \
-chardev socket,host=localhost,port=45455,server,nowait,telnet=on,id=jsix_debug -device isa-debugcon,iobase=0x6600,chardev=jsix_debug \ -chardev "file,path=${root}/debugcon.log,id=jsix_debug" \
-device "isa-debugcon,iobase=0x6600,chardev=jsix_debug" \
-monitor telnet:localhost:45454,server,nowait \ -monitor telnet:localhost:45454,server,nowait \
-serial stdio \ -serial stdio \
-smp "${smp}" \ -smp "${smp}" \
-m 4096 \ -m 4096 \
-cpu "${cpu}" \ -cpu "${cpu}${cpu_features}" \
-M q35 \ -M q35 \
-no-reboot \ -no-reboot \
-name "jsix" \ -name jsix \
$isaexit $log $gfx $vga $kvm $debug $isaexit $log $gfx $vga $kvm $debug
((result = ($? >> 1) - 1)) ((result = ($? >> 1) - 1))

View File

@@ -5,3 +5,5 @@ pyyaml >= 5.4
lark == 0.12.0 lark == 0.12.0
pure-cdb == 4 pure-cdb == 4
pyzstd == 0.15 pyzstd == 0.15
pyelftools
iced-x86

View File

@@ -0,0 +1,68 @@
_config_cache = {}
def _load(filename, depfiles):
from . import load_config
if not filename in _config_cache:
if filename.exists():
depfiles.add(filename)
data = load_config(filename)
_config_cache[filename] = data
return _config_cache.get(filename, dict())
def _build_config(chain, depfiles):
config = {}
for d in [_load(c, depfiles) for c in chain]:
for k, v in d.items():
if isinstance(v, (list, tuple)):
config[k] = config.get(k, list()) + list(v)
elif isinstance(v, dict):
config[k] = config.get(k, dict())
config[k].update(v)
else:
config[k] = v
return config
def _make_ninja_config(outfile, config, files):
from os import makedirs
from ninja.ninja_syntax import Writer
makedirs(outfile.parent, exist_ok=True)
with open(outfile, "w") as buildfile:
build = Writer(buildfile)
build.comment("This file is automatically generated by bonnibel")
build.comment("Source config files:")
for f in files:
build.comment(f" - {f}")
build.newline()
for k, v in config.items():
build.variable(k, v)
def generate_configs(root, output, config, targets, kinds):
assets = root / "assets" / "build"
base = ["global.yaml", f"config.{config}.yaml"]
depfiles = set()
for target in targets:
chain = base + [f"target.{target}.yaml"]
files = [assets / f for f in chain]
config = _build_config(files, depfiles)
outfile = output / target / f"config.ninja"
_make_ninja_config(outfile, config, files)
for kind in kinds:
custom = [f"kind.{kind}.yaml", f"target.{target}.{kind}.yaml"]
files = [assets / f for f in chain + custom]
config = _build_config(files, depfiles)
outfile = output / target / f"config.{kind}.ninja"
_make_ninja_config(outfile, config, files)
return depfiles

View File

@@ -26,7 +26,7 @@ class Manifest:
name="kernel", target="kernel") name="kernel", target="kernel")
self.init = self.__build_entry(modules, self.init = self.__build_entry(modules,
config.get("init", None)) config.get("init", None), target="init")
self.panics = [self.__build_entry(modules, i, target="kernel") self.panics = [self.__build_entry(modules, i, target="kernel")
for i in config.get("panic", tuple())] for i in config.get("panic", tuple())]
@@ -37,6 +37,13 @@ class Manifest:
self.drivers = [self.__build_entry(modules, i) self.drivers = [self.__build_entry(modules, i)
for i in config.get("drivers", tuple())] for i in config.get("drivers", tuple())]
libs = set(config.get("libs", tuple()))
libs.update(self.__libdeps([modules[e.module] for e in self.services]))
libs.update(self.__libdeps([modules[e.module] for e in self.drivers]))
self.libs = [self.__build_entry(modules, i)
for i in libs]
self.flags = config.get("flags", tuple()) self.flags = config.get("flags", tuple())
self.symbols = "" self.symbols = ""
@@ -71,7 +78,14 @@ class Manifest:
if not f in Manifest.flags: if not f in Manifest.flags:
raise BonnibelError(f"Manifest specifies unknown flag '{f}'") raise BonnibelError(f"Manifest specifies unknown flag '{f}'")
return Manifest.Entry(name, target, mod.output, flags) return Manifest.Entry(name, target, mod.get_output(), flags)
def __libdeps(self, modules):
deps = set([m.name for m in modules if m.kind == "lib"])
for m in modules:
if m.static: continue
deps.update(self.__libdeps(m.depmods))
return deps
def add_data(self, output, desc, flags=tuple()): def add_data(self, output, desc, flags=tuple()):
e = Manifest.Entry(None, None, output, flags) e = Manifest.Entry(None, None, output, flags)

View File

@@ -1,4 +1,5 @@
from . import include_rel, mod_rel, target_rel from . import include_rel, mod_rel, target_rel
from . import BonnibelError
def resolve(path): def resolve(path):
if path.startswith('/') or path.startswith('$'): if path.startswith('/') or path.startswith('$'):
@@ -7,29 +8,43 @@ def resolve(path):
return str(Path(path).resolve()) return str(Path(path).resolve())
class BuildOptions: class BuildOptions:
def __init__(self, includes=tuple(), libs=tuple(), order_only=tuple(), ld_script=None): def __init__(self, includes=tuple(), local=tuple(), late=tuple(), libs=tuple(), order_only=tuple(), ld_script=None):
self.includes = list(includes) self.includes = list(includes)
self.local = list(local)
self.late = list(late)
self.libs = list(libs) self.libs = list(libs)
self.order_only = list(order_only) self.order_only = list(order_only)
self.ld_script = ld_script and str(ld_script) self.ld_script = ld_script and str(ld_script)
@property @property
def implicit(self): def implicit(self):
libfiles = list(map(lambda x: x[0], self.libs))
if self.ld_script is not None: if self.ld_script is not None:
return self.libs + [self.ld_script] return libfiles + [self.ld_script]
else: else:
return self.libs return libfiles
@property
def linker_args(self):
from pathlib import Path
def arg(path, static):
if static: return path
return "-l:" + Path(path).name
return [arg(*l) for l in self.libs]
class Module: class Module:
__fields = { __fields = {
# name: (type, default) # name: (type, default)
"kind": (str, "exe"), "kind": (str, "exe"),
"output": (str, None), "outfile": (str, None),
"basename": (str, None),
"targets": (set, ()), "targets": (set, ()),
"deps": (set, ()), "deps": (set, ()),
"public_headers": (set, ()), "public_headers": (set, ()),
"copy_headers": (bool, False),
"includes": (tuple, ()), "includes": (tuple, ()),
"include_phase": (str, "normal"),
"sources": (tuple, ()), "sources": (tuple, ()),
"drivers": (tuple, ()), "drivers": (tuple, ()),
"variables": (dict, ()), "variables": (dict, ()),
@@ -37,13 +52,15 @@ class Module:
"description": (str, None), "description": (str, None),
"no_libc": (bool, False), "no_libc": (bool, False),
"ld_script": (str, None), "ld_script": (str, None),
"static": (bool, False),
} }
def __init__(self, name, modfile, root, **kwargs): def __init__(self, name, modfile, root, **kwargs):
from .source import make_source from pathlib import Path
from .source import make_source, make_copy_source
# Required fields # Required fields
self.root = root self.root = Path(root)
self.name = name self.name = name
self.modfile = modfile self.modfile = modfile
@@ -57,31 +74,46 @@ class Module:
for name in kwargs: for name in kwargs:
if not name in self.__fields: if not name in self.__fields:
raise AttributeError(f"No attribute named {name} on Module") raise AttributeError(f"No attribute named {name} on Module ({modfile})")
if not self.no_libc:
self.deps.add("libc_free")
# Turn strings into real Source objects # Turn strings into real Source objects
self.sources = [make_source(root, f) for f in self.sources] self.sources = [make_source(root, f) for f in self.sources]
self.public_headers = [make_source(root, f) for f in self.public_headers]
header_source = lambda f: make_source(root, Path("include") / f)
if self.copy_headers:
header_source = lambda f: make_copy_source(root, f, "include")
self.public_headers = [header_source(f) for f in self.public_headers]
# Filled by Module.update # Filled by Module.update
self.depmods = set() self.depmods = set()
def __str__(self): def __repr__(self):
return "Module {} {}\n\t{}".format(self.kind, self.name, "\n\t".join(map(str, self.sources))) return f"<Module {self.kind} {self.name}>"
@property @property
def output(self): def basename(self):
if self.__output is not None: if self.__basename is not None:
return self.__output return self.__basename
if self.kind == "lib": if self.kind == "lib":
return f"lib{self.name}.a" return f"lib{self.name}"
else: else:
return f"{self.name}.elf" return self.name
@output.setter @basename.setter
def output(self, value): def basename(self, value):
self.__output = value self.__basename = value
def get_output(self, static=False):
if self.outfile is not None:
return self.outfile
elif self.kind == "headers":
return None
else:
ext = dict(exe=".elf", driver=".drv", lib=(static and ".a" or ".so"))
return self.basename + ext.get(self.kind, "")
@classmethod @classmethod
def update(cls, mods): def update(cls, mods):
@@ -114,66 +146,28 @@ class Module:
from collections import defaultdict from collections import defaultdict
from ninja.ninja_syntax import Writer from ninja.ninja_syntax import Writer
def walk_deps(deps): def walk_deps(deps, static, results):
open_set = set(deps) for mod in deps:
closed_set = set() if static or mod.name not in results:
while open_set: results[mod.name] = (mod, static)
dep = open_set.pop() walk_deps(mod.depmods, static or mod.static, results)
closed_set.add(dep)
open_set |= {m for m in dep.depmods if not m in closed_set}
return closed_set
all_deps = walk_deps(self.depmods) all_deps = {}
walk_deps(self.depmods, self.static, all_deps)
all_deps = all_deps.values()
def gather_phony(build, deps, child_rel, add_headers=False): def gather_phony(build, deps, child_rel):
phony = ".headers.phony" phony = ".headers.phony"
child_phony = [child_rel(phony, module=c.name) child_phony = [child_rel(phony, module=c.name)
for c in all_deps] for c, _ in all_deps]
header_targets = []
if add_headers:
if not self.no_libc:
header_targets.append(f"${{build_root}}/include/libc/{phony}")
if self.public_headers:
header_targets.append(f"${{build_root}}/include/{self.name}/{phony}")
build.build( build.build(
rule = "touch", rule = "touch",
outputs = [mod_rel(phony)], outputs = [mod_rel(phony)],
implicit = child_phony + header_targets, implicit = child_phony,
order_only = list(map(mod_rel, deps)), order_only = list(map(mod_rel, deps)),
) )
filename = str(output / f"headers.{self.name}.ninja")
with open(filename, "w") as buildfile:
build = Writer(buildfile)
build.comment("This file is automatically generated by bonnibel")
build.newline()
build.variable("module_dir", f"${{build_root}}/include/{self.name}")
header_deps = []
inputs = []
headers = set(self.public_headers)
while headers:
source = headers.pop()
headers.update(source.next)
if source.action:
build.newline()
build.build(rule=source.action, **source.args)
if source.gather:
header_deps += list(source.outputs)
if source.input:
inputs.extend(map(mod_rel, source.outputs))
build.newline()
gather_phony(build, header_deps, include_rel)
filename = str(output / f"module.{self.name}.ninja") filename = str(output / f"module.{self.name}.ninja")
with open(filename, "w") as buildfile: with open(filename, "w") as buildfile:
build = Writer(buildfile) build = Writer(buildfile)
@@ -182,13 +176,21 @@ class Module:
build.newline() build.newline()
build.variable("module_dir", target_rel(self.name + ".dir")) 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( modopts = BuildOptions(
includes = [self.root, "${module_dir}"], local = [self.root, "${module_dir}"],
ld_script = self.ld_script and self.root / self.ld_script, ld_script = self.ld_script and self.root / self.ld_script,
) )
if self.public_headers: if self.public_headers:
modopts.includes += [f"${{build_root}}/include/{self.name}"] modopts.includes += [
self.root / "include",
f"${{target_dir}}/{self.name}.dir/include",
]
for key, value in self.variables.items(): for key, value in self.variables.items():
build.variable(key, value) build.variable(key, value)
@@ -207,27 +209,64 @@ class Module:
modopts.includes.append(str(incpath)) modopts.includes.append(str(incpath))
modopts.includes.append(destpath) modopts.includes.append(destpath)
all_deps = walk_deps(self.depmods) for dep, static in all_deps:
for dep in all_deps:
if dep.public_headers: if dep.public_headers:
modopts.includes += [f"${{build_root}}/include/{dep.name}"] if dep.include_phase == "normal":
modopts.includes += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
if dep.kind == "lib": elif dep.include_phase == "late":
modopts.libs.append(target_rel(dep.output)) modopts.late += [dep.root / "include", f"${{target_dir}}/{dep.name}.dir/include"]
else: else:
modopts.order_only.append(target_rel(dep.output)) 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: if modopts.includes:
build.variable("ccflags", ["${ccflags}"] + [f"-I{i}" for i in modopts.includes]) cc_includes += [f"-I{i}" for i in modopts.includes]
build.variable("asflags", ["${asflags}"] + [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: if modopts.libs:
build.variable("libs", ["${libs}"] + modopts.libs) build.variable("libs", ["-L${target_dir}", "${libs}"] + modopts.linker_args)
if modopts.ld_script: if modopts.ld_script:
build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script]) build.variable("ldflags", ["${ldflags}"] + ["-T", modopts.ld_script])
header_deps = [] 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 = [] inputs = []
sources = set(self.sources) sources = set(self.sources)
@@ -245,11 +284,13 @@ class Module:
if source.input: if source.input:
inputs.extend(map(mod_rel, source.outputs)) inputs.extend(map(mod_rel, source.outputs))
build.newline() gather_phony(build, header_deps, target_rel)
gather_phony(build, header_deps, target_rel, add_headers=True) if self.kind == "headers":
# Header-only, don't output a build rule
return
output = target_rel(self.output) output = target_rel(self.get_output())
build.newline() build.newline()
build.build( build.build(
rule = self.kind, rule = self.kind,
@@ -257,6 +298,8 @@ class Module:
inputs = inputs, inputs = inputs,
implicit = modopts.implicit, implicit = modopts.implicit,
order_only = modopts.order_only, order_only = modopts.order_only,
variables = {"name": self.name,
"soname": self.get_output()},
) )
dump = output + ".dump" dump = output + ".dump"
@@ -268,14 +311,34 @@ class Module:
variables = {"name": self.name}, 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: if self.default:
build.newline() build.newline()
build.default(output) build.default(output)
build.default(dump) build.default(dump)
def add_input(self, path, **kwargs): def add_input(self, path, **kwargs):
from .source import Source from .source import make_source
s = Source(self.root, path, **kwargs) s = make_source(self.root, path, **kwargs)
self.sources.append(s) self.sources.append(s)
return s.outputs return s.outputs

View File

@@ -12,14 +12,17 @@ class Project:
def generate(self, root, output, modules, config, manifest_file): def generate(self, root, output, modules, config, manifest_file):
import sys import sys
import bonnibel
from os.path import join from os.path import join
from ninja.ninja_syntax import Writer from ninja.ninja_syntax import Writer
from .target import Target
targets = set() targets = set()
kinds = set()
for mod in modules.values(): for mod in modules.values():
targets.update({Target.load(root, t, config) for t in mod.targets}) targets.update(mod.targets)
kinds.add(mod.kind)
from .config import generate_configs
config_deps = generate_configs(root, output, config, targets, kinds)
with open(output / "build.ninja", "w") as buildfile: with open(output / "build.ninja", "w") as buildfile:
build = Writer(buildfile) build = Writer(buildfile)
@@ -28,6 +31,7 @@ class Project:
build.variable("ninja_required_version", "1.3") build.variable("ninja_required_version", "1.3")
build.variable("build_root", output) build.variable("build_root", output)
build.variable("source_root", root) build.variable("source_root", root)
build.variable("build_config", config)
build.newline() build.newline()
build.include(root / "assets/build/rules.ninja") build.include(root / "assets/build/rules.ninja")
@@ -46,11 +50,7 @@ class Project:
build.newline() build.newline()
for target in targets: for target in targets:
build.subninja(output / target.name / "target.ninja") build.subninja(output / target / "target.ninja")
build.newline()
for mod in modules.values():
build.subninja(output / f"headers.{mod.name}.ninja")
build.newline() build.newline()
build.build( build.build(
@@ -154,6 +154,9 @@ class Project:
for program in manifest.drivers: for program in manifest.drivers:
add_initrd_stripped("jsix/drivers", program) add_initrd_stripped("jsix/drivers", program)
for program in manifest.libs:
add_initrd_stripped("jsix/lib", program)
syms = manifest.add_data("symbol_table.dat", syms = manifest.add_data("symbol_table.dat",
"Symbol table", ("symbols",)) "Symbol table", ("symbols",))
@@ -163,7 +166,7 @@ class Project:
build.build( build.build(
rule = "makest", rule = "makest",
outputs = [syms_out], outputs = [syms_out],
inputs = [f"${{build_root}}/kernel/{modules['kernel'].output}"], inputs = [f"${{build_root}}/kernel/{modules['kernel'].get_output(static=True)}"],
) )
fatroot_content.append(syms_out) fatroot_content.append(syms_out)
manifest.symbols = syms_file manifest.symbols = syms_file
@@ -232,8 +235,7 @@ class Project:
[f"{self.root}/configure", str(manifest_file)] + \ [f"{self.root}/configure", str(manifest_file)] + \
[str(mod.modfile) for mod in modules.values()] [str(mod.modfile) for mod in modules.values()]
for target in targets: regen_implicits += list(map(str, config_deps))
regen_implicits += target.depfiles
build.build( build.build(
rule = "compdb", rule = "compdb",
@@ -249,7 +251,7 @@ class Project:
implicit = regen_implicits, implicit = regen_implicits,
implicit_outputs = implicit_outputs =
[f"module.{mod.name}.ninja" for mod in modules.values()] + [f"module.{mod.name}.ninja" for mod in modules.values()] +
[f"{target.name}/target.ninja" for target in targets] + [f"{target}/target.ninja" for target in targets] +
[boot_config], [boot_config],
) )
@@ -257,9 +259,9 @@ class Project:
build.default(["${build_root}/jsix.img"]) build.default(["${build_root}/jsix.img"])
for target in targets: for target in targets:
mods = [m.name for m in modules.values() if target.name in m.targets] mods = [m.name for m in modules.values() if target in m.targets]
targetdir = output / target.name targetdir = output / target
targetdir.mkdir(exist_ok=True) targetdir.mkdir(exist_ok=True)
buildfilename = str(targetdir / "target.ninja") buildfilename = str(targetdir / "target.ninja")
@@ -268,17 +270,16 @@ class Project:
build.comment("This file is automatically generated by bonnibel") build.comment("This file is automatically generated by bonnibel")
build.newline() build.newline()
build.variable("target", target.name) build.variable("target", target)
build.variable("target_dir", output / target.name) build.variable("target_dir", output / target)
build.newline() build.newline()
for name, value in target.items(): build.include(f"{target}/config.ninja")
build.variable(name, value)
build.newline() build.newline()
for kind in ('defs', 'run'): for kind in ('defs', 'run'):
for lang in ('c', 'cpp'): for lang in ('c', 'cpp'):
deffile = str(output / target.name / f"{lang}.{kind}") deffile = str(output / target / f"{lang}.{kind}")
build.build( build.build(
rule = f"dump_{lang}_{kind}", rule = f"dump_{lang}_{kind}",

View File

@@ -72,22 +72,21 @@ class ParseSource(Source):
variables = dict(name=self.path), variables = dict(name=self.path),
) )
class HeaderSource(Source): class CopySource(ParseSource):
action = "cp" action = "cp"
gather = True
def __init__(self, path, root = "${module_dir}", deps=tuple(), prefix = ""):
self.path = path
self.root = root
self.deps = deps
self.prefix = prefix
@property @property
def outputs(self): def output(self):
return (self.path,) from pathlib import Path
return Path(self.prefix) / self.path
@property class HeaderSource(Source): pass
def args(self):
return dict(
outputs = [mod_rel(self.path)],
inputs = [join(self.root, self.path)],
implicit = list(map(_resolve, self.deps)),
variables = dict(name=self.path),
)
class CompileSource(Source): class CompileSource(Source):
action = property(_dynamic_action("compile")) action = property(_dynamic_action("compile"))
@@ -117,3 +116,6 @@ def make_source(root, path):
return HeaderSource(path, root) return HeaderSource(path, root)
else: else:
raise RuntimeError(f"{path} has no Source type") raise RuntimeError(f"{path} has no Source type")
def make_copy_source(root, path, prefix = ""):
return CopySource(path, root, prefix=prefix)

View File

@@ -1,50 +0,0 @@
class Target(dict):
__targets = {}
@classmethod
def load(cls, root, name, config=None):
from . import load_config
if (name, config) in cls.__targets:
return cls.__targets[(name, config)]
configs = root / "assets/build"
dicts = []
depfiles = []
basename = name
if config:
basename += f"-{config}"
while basename is not None:
filename = str(configs / (basename + ".yaml"))
depfiles.append(filename)
desc = load_config(filename)
basename = desc.get("extends")
dicts.append(desc.get("variables", dict()))
t = Target(name, config, depfiles)
for d in reversed(dicts):
for k, v in d.items():
if isinstance(v, (list, tuple)):
t[k] = t.get(k, list()) + list(v)
elif isinstance(v, dict):
t[k] = t.get(k, dict())
t[k].update(v)
else:
t[k] = v
cls.__targets[(name, config)] = t
return t
def __init__(self, name, config, depfiles):
self.__name = name
self.__config = config
self.__depfiles = tuple(depfiles)
def __hash__(self):
return hash((self.__name, self.__config))
name = property(lambda self: self.__name)
config = property(lambda self: self.__config)
depfiles = property(lambda self: self.__depfiles)

View File

@@ -0,0 +1,62 @@
das -enabled off
break heap_allocator.cpp:200
commands
silent
printf "n %016lx %d\n", m_end, current
continue
end
break heap_allocator.cpp:206
commands
silent
printf "N %016lx %d\n", m_end, order
continue
end
break heap_allocator::register_free_block
commands
silent
printf "F %016lx %d\n", block, order
continue
end
break heap_allocator.cpp:118
commands
silent
printf "f %016lx %d\n", block, info->order
continue
end
break heap_allocator.cpp:241
commands
silent
printf "S %016lx %d\n", block, order
continue
end
break heap_allocator.cpp:158
commands
silent
printf "P %016lx %d\n", block, order
continue
end
break heap_allocator.cpp:180
commands
silent
printf "p %016lx %d\n", block, order
continue
end
break heap_allocator.cpp:182
commands
silent
printf "M %016lx %016lx %d\n", block, buddy, order
continue
end
set logging file buddy_allocs.txt
set logging overwrite on
set logging enabled on
continue

View File

@@ -0,0 +1,18 @@
das -enabled off
break frame_allocator.cpp:62
commands
silent
printf "+ %016lx %3d\n", *address, n
continue
end
break frame_allocator.cpp:95
commands
silent
printf "- %016lx %3d\n", address, count
continue
end
set logging file frame_allocs.txt
set logging overwrite on
set logging enabled on
continue

View File

@@ -0,0 +1,29 @@
das -enabled off
break heap_allocator.cpp:81
commands
silent
printf "+ %016lx %4d\n", block, length
continue
end
break heap_allocator.cpp:86
commands
silent
printf "+ %016lx %4d\n", block, length
continue
end
break heap_allocator.cpp:120
commands
silent
printf "- %016lx\n", p
continue
end
break heap_allocator.cpp:140
commands
silent
printf "> %016lx %4d %4d\n", p, old_length, new_length
continue
end
set logging file heap_allocs.txt
set logging overwrite on
set logging enabled on
continue

File diff suppressed because one or more lines are too long

View File

@@ -42,31 +42,37 @@ class PrimitiveRef(Primitive):
def cxx_names(self, options): def cxx_names(self, options):
return self.c_names(options) return self.c_names(options)
_inttypes = {
"int": "int",
"uint": "unsigned",
"size": "size_t",
"address": "uintptr_t",
"int8": "int8_t",
"uint8": "uint8_t",
"int16": "int16_t",
"uint16": "uint16_t",
"int32": "int32_t",
"uint32": "uint32_t",
"int64": "int64_t",
"uint64": "uint64_t",
}
_primitives = { _primitives = {
"string": PrimitiveRef("string", "char"), "string": PrimitiveRef("string", "char"),
"buffer": PrimitiveRef("buffer", "void", counted=True), "buffer": PrimitiveRef("buffer", "void", counted=True),
"int": Primitive("int", "int"),
"uint": Primitive("uint", "unsigned"),
"size": Primitive("size", "size_t"),
"address": Primitive("address", "uintptr_t"),
"int8": Primitive("int8", "int8_t"),
"uint8": Primitive("uint8", "uint8_t"),
"int16": Primitive("int16", "int16_t"),
"uint16": Primitive("uint16", "uint16_t"),
"int32": Primitive("int32", "int32_t"),
"uint32": Primitive("uint32", "uint32_t"),
"int64": Primitive("int64", "int64_t"),
"uint64": Primitive("uint64", "uint64_t"),
} }
def get_primitive(name): def get_primitive(name):
p = _primitives.get(name) p = _primitives.get(name)
if not p: if p: return p
it = _inttypes.get(name.replace('*', ''))
if it:
if '*' in name:
return PrimitiveRef(name, it)
else:
return Primitive(name, it)
from ..errors import InvalidType from ..errors import InvalidType
raise InvalidType(name) raise InvalidType(name)
return p

93
scripts/parse_buddy_allocs.py Executable file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
class block:
def __init__(self, start, order, free=False):
self.start = start
self.order = order
self.free = free
@property
def end(self):
return self.start + (1<<self.order)
def overlaps(self, other):
return other.start < self.end and other.end > self.start
def __str__(self):
return f"[{self.start:016x} {self.order:2} {self.free and 'free' or 'used'}]"
def get_block(blocks, addr, order, reason):
b = blocks.get(addr)
if b is None:
print(f"ERROR: {reason} unknown block [{addr:016x}, {order}]")
elif b.order != order:
print(f"ERROR: {reason} block {b} for order {order}")
else:
return b
return None
def new_block(blocks, addr, order):
b = block(addr, order)
for existing in blocks.values():
if b.overlaps(existing):
print(f"ERROR: new block {b} overlaps existing {existing}")
blocks[addr] = b
def free_block(blocks, addr, order, free):
s = free and "freeing" or "popping"
b = get_block(blocks, addr, order, s)
if b and b.free == free:
print(f"ERROR: {s} block {b}")
elif b:
b.free = free
def split_block(blocks, addr, order):
b = get_block(blocks, addr, order+1, "splitting")
if b:
b.order = order
buddy = b.start ^ (1<<order)
blocks[buddy] = block(buddy, order)
def merge_blocks(blocks, addr1, addr2, order):
b1 = get_block(blocks, addr1, order, "merging")
b2 = get_block(blocks, addr2, order, "merging")
if b1.start > b2.start:
b1, b2 = b2, b1
del blocks[b2.start]
b1.order = order + 1
def parse_line(blocks, line):
args = line.strip().split()
match args:
case ['N', addr, order]:
new_block(blocks, int(addr, base=16), int(order))
case ['n', addr, order]:
new_block(blocks, int(addr, base=16), int(order))
case ['P', addr, order]:
free_block(blocks, int(addr, base=16), int(order), False)
case ['p', addr, order]:
free_block(blocks, int(addr, base=16), int(order), False)
case ['F', addr, order]:
free_block(blocks, int(addr, base=16), int(order), True)
case ['S', addr, order]:
split_block(blocks, int(addr, base=16), int(order))
case ['M', addr1, addr2, order]:
merge_blocks(blocks, int(addr1, base=16), int(addr2, base=16), int(order))
case _:
pass
def parse_file(f):
blocks = {}
for line in f.readlines():
parse_line(blocks, line)
#for addr in sorted(blocks.keys()):
# print(f"{addr:09x}: {blocks[addr]}")
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1]) as f:
parse_file(f)
else:
parse_file(sys.stdin)

40
scripts/parse_frame_allocs.py Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
def add_maps(allocs, addr, count):
for i in range(count):
if addr+i in allocs:
print(f"ERROR: frame {addr+i:012x} map collision.")
else:
#print(f" frame {addr+i:012x} mapped")
allocs.add(addr+i)
def remove_maps(allocs, addr, count):
for i in range(count):
if addr+i not in allocs:
print(f" WARN: removing unmapped frame {addr+i:012x}")
else:
#print(f" frame {addr+i:012x} unmapped")
allocs.remove(addr+i)
def parse_line(allocs, line):
args = line.strip().split()
match args:
case ['+', addr, count]:
add_maps(allocs, int(addr, base=16), int(count))
case ['-', addr, count]:
remove_maps(allocs, int(addr, base=16), int(count))
case _:
pass
def parse_file(f):
allocs = set()
for line in f.readlines():
parse_line(allocs, line)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1]) as f:
parse_file(f)
else:
parse_file(sys.stdin)

62
scripts/parse_heap_allocs.py Executable file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
from bisect import bisect_left, insort
from operator import attrgetter
by_start = attrgetter('start')
class alloc:
def __init__(self, start, size):
self.start = start
self.size = size
@property
def end(self):
return self.start + self.size
def overlaps(self, other):
return other.start < self.end and other.end > self.start
def __str__(self):
return f"[{self.start:012x} - {self.end:012x}]"
def __gt__(self, other):
return self.start > other.start
def add_alloc(allocs, addr, length):
a = alloc(addr, length)
for existing in allocs:
if a.overlaps(existing):
print(f"ERROR: allocation {a} overlaps existing {existing}")
insort(allocs, a)
def remove_alloc(allocs, addr):
a = alloc(addr, 0)
i = bisect_left(allocs, a)
if len(allocs) > i and allocs[i].start == addr:
del allocs[i]
else:
print(f"ERROR: freeing unallocated {a}")
def parse_line(allocs, line):
args = line.strip().split()
match args:
case ['+', addr, length]:
add_alloc(allocs, int(addr, base=16), int(length))
case ['-', addr]:
remove_alloc(allocs, int(addr, base=16))
case _:
pass
def parse_file(f):
allocs = []
for line in f.readlines():
parse_line(allocs, line)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1]) as f:
parse_file(f)
else:
parse_file(sys.stdin)

124
scripts/print_got.py Executable file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env python3
def section_header(name):
print()
print(name)
print("-" * 40)
def print_dyn(elf):
section = elf.get_section_by_name(".dynamic")
if section is None:
return
section_header(".dynamic")
for tag in section.iter_tags():
print(tag)
def print_got(elf, name):
import struct
section = elf.get_section_by_name(name)
if section is None:
return
section_header(name)
base_ip = section['sh_addr']
data = section.data()
n = section.data_size // 8
for i in range(n):
addr = struct.unpack_from("Q", data, i*8)[0]
print(f"[{i:2x}]: {base_ip+i*8:6x} {addr:16x}")
def print_plt(elf):
from iced_x86 import Decoder, Formatter, FormatterSyntax
section_header(".plt")
section = elf.get_section_by_name(".plt")
n = section.data_size // 16
data = section.data()
frm = Formatter(FormatterSyntax.NASM)
frm.digit_separator = "'"
frm.first_operand_char_index = 8
frm.hex_prefix = "0x"
frm.hex_suffix = ""
frm.leading_zeros = False
frm.rip_relative_addresses = False
frm.small_hex_numbers_in_decimal = False
frm.uppercase_hex = False
base_ip = section['sh_addr']
for i in range(n):
entry = data[ i*16 : (i+1)*16 ]
d = Decoder(64, entry, ip=base_ip + i*16)
indent = f"[{i:2x}]:"
for instr in d:
disasm = frm.format(instr)
print(f"{indent:6} {instr.ip:6x} {disasm}")
indent = ""
print()
def print_gnu_hash(elf):
hash_section = elf.get_section_by_name(".gnu.hash")
data = hash_section.data()
import struct
(nbuckets, symoff, bloom_sz, bloom_sh) = struct.unpack_from("IIII", data, 0)
blooms = struct.unpack_from(f"{bloom_sz}Q", data, 16)
buckets = struct.unpack_from(f"{nbuckets}I", data, 16+(bloom_sz*8))
p = 16 + (bloom_sz*8) + (nbuckets*4)
n = (len(data) - p) // 4
chains = struct.unpack_from(f"{n}I", data, p)
section_header(".gnu.hash")
print(f" Bucket Count: {nbuckets}")
print(f"Symbol Offset: {symoff}")
print(f" Buckets: {buckets}")
print("\n Bloom words:")
for i in range(len(blooms)):
print(f" [{i:2}]: {blooms[i]:016x}")
print("\n Hashes:")
for i in range(len(chains)):
h = chains[i]
end = ""
if (h & 1) == 1:
end = "END"
bloom_idx = (h>>6) % bloom_sz
bloom_msk = ((1<<(h%64)) | (1<<((h>>bloom_sh)%64)))
print(f" [{i+symoff:2}]: {h:08x} {end:5} {bloom_idx:2}/{bloom_msk:016x}")
def print_tables(filename):
from elftools.elf.elffile import ELFFile
print(filename)
print("=" * 50)
with open(filename, 'rb') as f:
elf = ELFFile(f)
print_got(elf, ".got")
print_got(elf, ".got.plt")
print_plt(elf)
print_dyn(elf)
print_gnu_hash(elf)
print()
if __name__ == "__main__":
import sys
for filename in sys.argv[1:]:
print_tables(filename)

View File

@@ -0,0 +1 @@
set -gx VNCHOST (ip -j route list default | jq -r '.[0].gateway'):5500

View File

@@ -71,10 +71,10 @@ allocator::add_modules()
allocate_pages(1, alloc_type::init_args, true)); allocate_pages(1, alloc_type::init_args, true));
if (m_modules) if (m_modules)
m_modules->next = reinterpret_cast<uintptr_t>(mods); m_modules->next = mods;
m_modules = mods; m_modules = mods;
m_next_mod = mods->modules; m_next_mod = reinterpret_cast<module*>(mods+1);
return; return;
} }
@@ -109,9 +109,9 @@ allocator::allocate_pages(size_t count, alloc_type type, bool zero)
} }
module * module *
allocator::allocate_module() allocator::allocate_module(size_t extra)
{ {
static constexpr size_t size = sizeof(module); size_t size = sizeof(module) + extra;
size_t remaining = size_t remaining =
reinterpret_cast<uintptr_t>(m_modules) + page_size reinterpret_cast<uintptr_t>(m_modules) + page_size
@@ -120,8 +120,8 @@ allocator::allocate_module()
if (size > remaining) if (size > remaining)
add_modules(); add_modules();
++m_modules->count;
module *m = m_next_mod; module *m = m_next_mod;
m->bytes = size;
m_next_mod = util::offset_pointer(m_next_mod, size); m_next_mod = util::offset_pointer(m_next_mod, size);
return m; return m;
} }

View File

@@ -32,7 +32,7 @@ public:
void * allocate_pages(size_t count, alloc_type type, bool zero = false); void * allocate_pages(size_t count, alloc_type type, bool zero = false);
module * allocate_module(); module * allocate_module(size_t extra = 0);
void memset(void *start, size_t size, uint8_t value); void memset(void *start, size_t size, uint8_t value);
void copy(void *to, const void *from, size_t size); void copy(void *to, const void *from, size_t size);

View File

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

View File

@@ -79,10 +79,11 @@ check_cpu_supported()
status_line status {L"Checking CPU features"}; status_line status {L"Checking CPU features"};
cpu::cpu_id cpu; cpu::cpu_id cpu;
cpu::cpu_id::features features = cpu.validate(); cpu::features features = cpu.features();
bool supported = true; bool supported = true;
#define CPU_FEATURE_OPT(...) #define CPU_FEATURE_OPT(...)
#define CPU_FEATURE_WRN(...)
#define CPU_FEATURE_REQ(name, ...) \ #define CPU_FEATURE_REQ(name, ...) \
if (!features[cpu::feature::name]) { \ if (!features[cpu::feature::name]) { \
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \ status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \

View File

@@ -167,15 +167,15 @@ load_module(
fs::file &disk, fs::file &disk,
const wchar_t *name, const wchar_t *name,
const wchar_t *path, const wchar_t *path,
bootproto::module_type type, bootproto::module_type type)
uint16_t subtype)
{ {
status_line status(L"Loading module", name); status_line status(L"Loading module", name);
bootproto::module *mod = g_alloc.allocate_module(); bootproto::module *mod = g_alloc.allocate_module(sizeof(util::buffer));
mod->type = type; mod->type = type;
mod->subtype = subtype;
mod->data = load_file(disk, path); util::buffer *data = mod->data<util::buffer>();
*data = load_file(disk, path);
} }
} // namespace loader } // namespace loader

View File

@@ -60,14 +60,12 @@ load_program(
/// \arg name The human-readable name of the module /// \arg name The human-readable name of the module
/// \arg path The path of the file to load the module from /// \arg path The path of the file to load the module from
/// \arg type The major type to set on the module /// \arg type The major type to set on the module
/// \arg subtype The subtype to set on the module
void void
load_module( load_module(
fs::file &disk, fs::file &disk,
const wchar_t *name, const wchar_t *name,
const wchar_t *path, const wchar_t *path,
bootproto::module_type type, bootproto::module_type type);
uint16_t subtype);
} // namespace loader } // namespace loader
} // namespace boot } // namespace boot

View File

@@ -7,6 +7,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <bootproto/acpi.h>
#include <bootproto/bootconfig.h> #include <bootproto/bootconfig.h>
#include <bootproto/kernel.h> #include <bootproto/kernel.h>
#include <bootproto/memory.h> #include <bootproto/memory.h>
@@ -125,7 +126,7 @@ load_resources(
loader::parse_program(L"init server", init, args->init); loader::parse_program(L"init server", init, args->init);
loader::load_module(disk, L"initrd", bc.initrd(), loader::load_module(disk, L"initrd", bc.initrd(),
bootproto::module_type::initrd, 0); bootproto::module_type::initrd);
return reinterpret_cast<bootproto::entrypoint>(kentry); return reinterpret_cast<bootproto::entrypoint>(kentry);
} }
@@ -176,9 +177,23 @@ efi_main(uefi::handle image, uefi::system_table *st)
bootproto::entrypoint kentry = bootproto::entrypoint kentry =
load_resources(args, screen, image, pager, bs); load_resources(args, screen, image, pager, bs);
bootproto::module *acpi_mod =
g_alloc.allocate_module(sizeof(bootproto::acpi));
acpi_mod->type = bootproto::module_type::acpi;
bootproto::acpi *acpi = acpi_mod->data<bootproto::acpi>();
acpi->root = args->acpi_table;
pager.update_kernel_args(args); pager.update_kernel_args(args);
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services); memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
for (size_t i = 0; i < args->mem_map.count; ++i) {
bootproto::mem_entry &e = args->mem_map.pointer[i];
if (e.type == bootproto::mem_type::acpi) {
acpi->region = util::buffer::from(e.start, e.pages * memory::page_size);
break;
}
}
args->allocations = allocs; args->allocations = allocs;
args->init_modules = reinterpret_cast<uintptr_t>(modules); args->init_modules = reinterpret_cast<uintptr_t>(modules);
@@ -194,6 +209,7 @@ efi_main(uefi::handle image, uefi::system_table *st)
change_pointer(args); change_pointer(args);
change_pointer(args->pml4); change_pointer(args->pml4);
change_pointer(args->symbol_table.pointer);
change_pointer(args->init.sections.pointer); change_pointer(args->init.sections.pointer);
//status.next(); //status.next();

View File

@@ -249,13 +249,13 @@ build_frame_blocks(const util::counted<bootproto::mem_entry> &kmap)
unsigned i = 0; unsigned i = 0;
uint64_t b1 = (page_count + 4095) / 4096; uint64_t b1 = (page_count + 4095) / 4096;
blk->map1 = (1 << b1) - 1; blk->map1 = (1ull << b1) - 1;
uint64_t b2 = (page_count + 63) / 64; uint64_t b2 = (page_count + 63) / 64;
uint64_t b2q = b2 / 64; uint64_t b2q = b2 / 64;
uint64_t b2r = b2 % 64; uint64_t b2r = b2 % 64;
g_alloc.memset(blk->map2, b2q, 0xff); g_alloc.memset(blk->map2, b2q, 0xff);
blk->map2[b2q] = (1 << b2r) - 1; blk->map2[b2q] = (1ull << b2r) - 1;
break; break;
} }
} }
@@ -269,7 +269,7 @@ build_frame_blocks(const util::counted<bootproto::mem_entry> &kmap)
size_t b = blk.count / 64; size_t b = blk.count / 64;
size_t r = blk.count % 64; size_t r = blk.count % 64;
g_alloc.memset(blk.bitmap, b*8, 0xff); g_alloc.memset(blk.bitmap, b*8, 0xff);
blk.bitmap[b] = (1 << r) - 1; blk.bitmap[b] = (1ull << r) - 1;
bitmap += bitmap_size(blk.count); bitmap += bitmap_size(blk.count);
} }

View File

@@ -45,6 +45,7 @@ pick_mode(uefi::boot_services *bs)
uint32_t res = info->horizontal_resolution * info->vertical_resolution; uint32_t res = info->horizontal_resolution * info->vertical_resolution;
int pixmode = static_cast<int>(info->pixel_format); int pixmode = static_cast<int>(info->pixel_format);
/*
const uint32_t modes = gop->mode->max_mode; const uint32_t modes = gop->mode->max_mode;
for (uint32_t i = 0; i < modes; ++i) { for (uint32_t i = 0; i < modes; ++i) {
size_t size = 0; size_t size = 0;
@@ -63,6 +64,7 @@ pick_mode(uefi::boot_services *bs)
pixmode = new_pixmode; pixmode = new_pixmode;
} }
} }
*/
screen *s = new screen; screen *s = new screen;
s->mode = { s->mode = {
@@ -109,20 +111,15 @@ make_module(screen *s)
{ {
using bootproto::module; using bootproto::module;
using bootproto::module_type; using bootproto::module_type;
using bootproto::device_type; namespace devices = bootproto::devices;
using bootproto::devices::uefi_fb;
uefi_fb *fb = new uefi_fb; module *mod = g_alloc.allocate_module(sizeof(devices::uefi_fb));
mod->type = module_type::device;
mod->type_id = devices::type_id_uefi_fb;
devices::uefi_fb *fb = mod->data<devices::uefi_fb>();
fb->framebuffer = s->framebuffer; fb->framebuffer = s->framebuffer;
fb->mode = s->mode; fb->mode = s->mode;
module *mod = g_alloc.allocate_module();
mod->type = module_type::device;
mod->subtype = static_cast<uint16_t>(device_type::uefi_fb);
mod->data = {
.pointer = fb,
.count = sizeof(uefi_fb),
};
} }
} // namespace video } // namespace video

View File

@@ -6,7 +6,9 @@
#include <util/enum_bitfields.h> #include <util/enum_bitfields.h>
#include <util/misc.h> // for byteswap32 #include <util/misc.h> // for byteswap32
struct acpi_table_header namespace acpi {
struct table_header
{ {
uint32_t type; uint32_t type;
uint32_t length; uint32_t length;
@@ -18,24 +20,26 @@ struct acpi_table_header
uint32_t creator_id; uint32_t creator_id;
uint32_t creator_revision; uint32_t creator_revision;
bool validate(uint32_t expected_type = 0) const; bool validate() const { return util::checksum(this, length) == 0; }
} __attribute__ ((packed)); } __attribute__ ((packed));
#define TABLE_HEADER(signature) \ #define TABLE_HEADER(signature) \
static constexpr uint32_t type_id = util::byteswap32(signature); \ static constexpr uint32_t type_id = util::byteswap32(signature); \
acpi_table_header header; table_header header;
template <typename T> template <typename T>
bool acpi_validate(const T *t) { return t->header.validate(T::type_id); } bool validate(const T *t) {
return t->header.validate(T::type_id);
}
template <typename T> template <typename T>
size_t acpi_table_entries(const T *t, size_t size) size_t table_entries(const T *t, size_t size) {
{
return (t->header.length - sizeof(T)) / size; return (t->header.length - sizeof(T)) / size;
} }
enum class acpi_gas_type : uint8_t
enum class gas_type : uint8_t
{ {
system_memory, system_memory,
system_io, system_io,
@@ -46,9 +50,9 @@ enum class acpi_gas_type : uint8_t
functional_fixed = 0x7f functional_fixed = 0x7f
}; };
struct acpi_gas struct gas
{ {
acpi_gas_type type; gas_type type;
uint8_t reg_bits; uint8_t reg_bits;
uint8_t reg_offset; uint8_t reg_offset;
@@ -58,7 +62,7 @@ struct acpi_gas
} __attribute__ ((packed)); } __attribute__ ((packed));
enum class acpi_fadt_flags : uint32_t enum class fadt_flags : uint32_t
{ {
wbinvd = 0x00000001, wbinvd = 0x00000001,
wbinvd_flush = 0x00000002, wbinvd_flush = 0x00000002,
@@ -83,9 +87,9 @@ enum class acpi_fadt_flags : uint32_t
hw_reduced_acpi = 0x00100000, hw_reduced_acpi = 0x00100000,
low_pwr_s0_idle = 0x00200000 low_pwr_s0_idle = 0x00200000
}; };
is_bitfield(acpi_fadt_flags); is_bitfield(fadt_flags);
struct acpi_fadt struct fadt
{ {
TABLE_HEADER('FACP'); TABLE_HEADER('FACP');
@@ -130,9 +134,9 @@ struct acpi_fadt
uint16_t iapc_boot_arch; uint16_t iapc_boot_arch;
uint8_t reserved1; uint8_t reserved1;
acpi_fadt_flags flags; fadt_flags flags;
acpi_gas reset_reg; gas reset_reg;
uint8_t reset_value; uint8_t reset_value;
uint16_t arm_boot_arch; uint16_t arm_boot_arch;
@@ -142,28 +146,28 @@ struct acpi_fadt
uint64_t x_facs; uint64_t x_facs;
uint64_t x_dsdt; uint64_t x_dsdt;
acpi_gas x_pm1a_event_block; gas x_pm1a_event_block;
acpi_gas x_pm1b_event_block; gas x_pm1b_event_block;
acpi_gas x_pm1a_control_block; gas x_pm1a_control_block;
acpi_gas x_pm1b_control_block; gas x_pm1b_control_block;
acpi_gas x_pm2_control_block; gas x_pm2_control_block;
acpi_gas x_pm_timer_block; gas x_pm_timer_block;
acpi_gas x_gpe0_block; gas x_gpe0_block;
acpi_gas x_gpe1_block; gas x_gpe1_block;
acpi_gas sleep_control_reg; gas sleep_control_reg;
acpi_gas sleep_status_reg; gas sleep_status_reg;
uint64_t hypervisor_vendor_id; uint64_t hypervisor_vendor_id;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_xsdt struct xsdt
{ {
TABLE_HEADER('XSDT'); TABLE_HEADER('XSDT');
acpi_table_header *headers[0]; table_header *headers[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_apic struct apic
{ {
TABLE_HEADER('APIC'); TABLE_HEADER('APIC');
uint32_t local_address; uint32_t local_address;
@@ -171,7 +175,7 @@ struct acpi_apic
uint8_t controller_data[0]; uint8_t controller_data[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_mcfg_entry struct mcfg_entry
{ {
uint64_t base; uint64_t base;
uint16_t group; uint16_t group;
@@ -180,24 +184,24 @@ struct acpi_mcfg_entry
uint32_t reserved; uint32_t reserved;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_mcfg struct mcfg
{ {
TABLE_HEADER('MCFG'); TABLE_HEADER('MCFG');
uint64_t reserved; uint64_t reserved;
acpi_mcfg_entry entries[0]; mcfg_entry entries[0];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_hpet struct hpet
{ {
TABLE_HEADER('HPET'); TABLE_HEADER('HPET');
uint32_t hardware_id; uint32_t hardware_id;
acpi_gas base_address; gas base_address;
uint8_t index; uint8_t index;
uint16_t periodic_min; uint16_t periodic_min;
uint8_t attributes; uint8_t attributes;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acpi_bgrt struct bgrt
{ {
TABLE_HEADER('BGRT'); TABLE_HEADER('BGRT');
uint16_t version; uint16_t version;
@@ -208,3 +212,35 @@ struct acpi_bgrt
uint32_t offset_y; uint32_t offset_y;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct rsdp1
{
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__ ((packed));
struct rsdp2
{
char signature[8];
uint8_t checksum10;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
table_header *xsdt_address;
uint8_t checksum20;
uint8_t reserved[3];
} __attribute__ ((packed));
template <typename T> static const T *
check_get_table(const table_header *header)
{
if (!header || header->type != T::type_id)
return nullptr;
return reinterpret_cast<const T *>(header);
}
} // namespace acpi

View File

@@ -1,5 +1,5 @@
#include "apic.h" #include "apic.h"
#include "assert.h" #include "kassert.h"
#include "clock.h" #include "clock.h"
#include "interrupts.h" #include "interrupts.h"
#include "io.h" #include "io.h"

View File

@@ -1,21 +0,0 @@
#include "assert.h"
#include "idt.h"
namespace panic {
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
void const *symbol_table = nullptr;
void
install(uintptr_t entrypoint, util::const_buffer symbol_data)
{
IDT::set_nmi_handler(entrypoint);
symbol_table = symbol_data.pointer;
}
} // namespace panic
extern "C"
void __assert_fail(const char *message, const char *file, unsigned line, const char *function) {
panic::panic(message, nullptr, function, file, line);
}

View File

@@ -68,6 +68,7 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps)
capability * capability *
cap_table::find_without_retain(j6_handle_t id) cap_table::find_without_retain(j6_handle_t id)
{ {
util::scoped_lock lock {m_lock};
return m_caps.find(id); return m_caps.find(id);
} }

View File

@@ -1,4 +1,4 @@
#include "assert.h" #include "kassert.h"
using __exit_func = void (*)(void *); using __exit_func = void (*)(void *);

View File

@@ -1,58 +1,72 @@
#include <new>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <j6/memutils.h>
#include <util/bitset.h> #include <util/bitset.h>
#include <util/no_construct.h>
#include "assert.h" #include "kassert.h"
#include "cpu.h" #include "cpu.h"
#include "cpu/cpu_id.h"
#include "device_manager.h" #include "device_manager.h"
#include "gdt.h" #include "gdt.h"
#include "idt.h" #include "idt.h"
#include "logger.h" #include "logger.h"
#include "msr.h" #include "msr.h"
#include "objects/thread.h" #include "objects/thread.h"
#include "scheduler.h"
#include "syscall.h" #include "syscall.h"
#include "tss.h" #include "tss.h"
#include "xsave.h"
unsigned g_num_cpus = 1; unsigned g_num_cpus = 1;
panic_data g_panic_data; panic_data g_panic_data;
panic_data *g_panic_data_p = &g_panic_data; panic_data *g_panic_data_p = &g_panic_data;
cpu_data g_bsp_cpu_data; static util::no_construct<cpu_data> __g_bsp_cpu_storage;
cpu_data &g_bsp_cpu_data = __g_bsp_cpu_storage.value;
cpu_data **g_cpu_data = nullptr; cpu_data **g_cpu_data = nullptr;
static cpu::features
get_features()
{
cpu::cpu_id cpuid;
return cpuid.features();
}
// Validate the required CPU features are present. Really, the bootloader already // Validate the required CPU features are present. Really, the bootloader already
// validated the required features, but still iterate the options and log about them. // validated the required features, but still iterate the options and log about them.
void static cpu::features
cpu_validate() cpu_validate(cpu_data *c)
{ {
cpu::cpu_id cpu; cpu::cpu_id cpuid;
char brand_name[50]; char brand_name[50];
cpu.brand_name(brand_name); cpuid.brand_name(brand_name);
cpu::cpu_id::features features = cpu.validate(); cpu::features &features = c->features;
log::info(logs::boot, "CPU: %s", brand_name); log::info(logs::boot, "CPU %2d: %s", c->index, brand_name);
log::info(logs::boot, " Vendor is %s", cpu.vendor_id()); log::info(logs::boot, " Vendor is %s", cpuid.vendor_id());
log::spam(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic()); log::spam(logs::boot, " Higest basic CPUID: 0x%02x", cpuid.highest_basic());
log::spam(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended); log::spam(logs::boot, " Higest ext CPUID: 0x%02x", cpuid.highest_ext() & ~cpu::cpu_id::cpuid_extended);
#define CPU_FEATURE_OPT(name, ...) \ #define CPU_FEATURE_OPT(name, ...) \
log::verbose(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no");
#define CPU_FEATURE_WRN(name, feat_leaf, feat_sub, regname, bit) \
log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
if (!features[cpu::feature::name]) log::warn(logs::boot, "Missing cpu feature %s but continuing", #name);
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \ #define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
log::verbose(logs::boot, " Supports %9s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \ log::verbose(logs::boot, " Flag %11s: %s", #name, features[cpu::feature::name] ? "yes" : "no"); \
kassert(features[cpu::feature::name], "Missing required CPU feature " #name ); kassert(features[cpu::feature::name], "Missing required CPU feature " #name );
#include "cpu/features.inc" #include "cpu/features.inc"
#undef CPU_FEATURE_OPT #undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ #undef CPU_FEATURE_REQ
return features;
} }
@@ -64,13 +78,31 @@ cpu_early_init(cpu_data *cpu)
cpu->idt->install(); cpu->idt->install();
cpu->gdt->install(); cpu->gdt->install();
util::bitset64 cr0_val = 0;
asm ("mov %%cr0, %0" : "=r"(cr0_val));
cr0_val
.set(cr0::WP)
.clear(cr0::CD);
asm volatile ( "mov %0, %%cr0" :: "r" (cr0_val) );
cpu->features = get_features();
uintptr_t cr3_val;
asm ("mov %%cr3, %0" : "=r"(cr3_val));
util::bitset64 cr4_val = 0; util::bitset64 cr4_val = 0;
asm ("mov %%cr4, %0" : "=r"(cr4_val)); asm ("mov %%cr4, %0" : "=r"(cr4_val));
cr4_val cr4_val
.set(cr4::OSXFSR) .set(cr4::OSXFSR)
.set(cr4::OSXMMEXCPT) .set(cr4::OSXMMEXCPT)
.set(cr4::PCIDE)
.set(cr4::OSXSAVE); .set(cr4::OSXSAVE);
// TODO: On KVM setting PCIDE generates a #GP even though
// the feature is listed as available in CPUID.
/*
if (cpu->features[cpu::feature::pcid])
cr4_val.set(cr4::PCIDE);
*/
asm volatile ( "mov %0, %%cr4" :: "r" (cr4_val) ); asm volatile ( "mov %0, %%cr4" :: "r" (cr4_val) );
// Enable SYSCALL and NX bit // Enable SYSCALL and NX bit
@@ -80,6 +112,24 @@ cpu_early_init(cpu_data *cpu)
.set(efer::NXE); .set(efer::NXE);
wrmsr(msr::ia32_efer, efer_val); wrmsr(msr::ia32_efer, efer_val);
util::bitset64 xcr0_val = get_xcr0();
xcr0_val
.set(xcr0::SSE);
set_xcr0(xcr0_val);
// Set initial floating point state
const util::bitset32 mxcsr_val {
mxcsr::DAZ,
mxcsr::IM,
mxcsr::DM,
mxcsr::ZM,
mxcsr::OM,
mxcsr::UM,
mxcsr::PM,
mxcsr::FTZ,
};
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
// Install the GS base pointint to the cpu_data // Install the GS base pointint to the cpu_data
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu)); wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
} }
@@ -87,7 +137,6 @@ cpu_early_init(cpu_data *cpu)
cpu_data * cpu_data *
bsp_early_init() bsp_early_init()
{ {
cpu_validate();
memset(&g_panic_data, 0, sizeof(g_panic_data)); memset(&g_panic_data, 0, sizeof(g_panic_data));
extern IDT &g_bsp_idt; extern IDT &g_bsp_idt;
@@ -104,6 +153,7 @@ bsp_early_init()
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss}; cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
cpu->rsp0 = reinterpret_cast<uintptr_t>(&idle_stack_end); cpu->rsp0 = reinterpret_cast<uintptr_t>(&idle_stack_end);
cpu_early_init(cpu); cpu_early_init(cpu);
xsave_init();
return cpu; return cpu;
} }
@@ -120,8 +170,12 @@ bsp_late_init()
asm ("mov %%cr0, %0" : "=r"(cr0v)); asm ("mov %%cr0, %0" : "=r"(cr0v));
asm ("mov %%cr4, %0" : "=r"(cr4v)); asm ("mov %%cr4, %0" : "=r"(cr4v));
uint32_t mxcsrv = get_mxcsr();
uint64_t xcr0v = get_xcr0();
uint64_t efer = rdmsr(msr::ia32_efer); uint64_t efer = rdmsr(msr::ia32_efer);
log::spam(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0v, cr4v, efer); log::spam(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx mxcsr:%x xcr0:%x", cr0v, cr4v, efer, mxcsrv, xcr0v);
cpu_validate(&g_bsp_cpu_data);
} }
cpu_data * cpu_data *
@@ -162,7 +216,6 @@ cpu_init(cpu_data *cpu, bool bsp)
obj::thread *idle = obj::thread::create_idle_thread( obj::thread *idle = obj::thread::create_idle_thread(
g_kernel_process, g_kernel_process,
scheduler::max_priority,
cpu->rsp0); cpu->rsp0);
cpu->thread = idle; cpu->thread = idle;
@@ -190,4 +243,16 @@ cpu_init(cpu_data *cpu, bool bsp)
cpu->id = apic->get_id(); cpu->id = apic->get_id();
apic->calibrate_timer(); apic->calibrate_timer();
} }
xsave_enable();
}
/// Set up initial per-thread CPU state. Called once from initialize_user_cpu in
/// syscall.s, the first code run after a thread comes out of task_switch for the
/// very first time.
extern "C" void
cpu_initialize_thread_state()
{
const util::bitset32 &mxcsr_val = obj::thread::current().m_mxcsr;
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <cpu/cpu_id.h>
class GDT; class GDT;
class IDT; class IDT;
@@ -17,9 +18,12 @@ enum class cr0
{ {
PE = 0, // Protected mode enable PE = 0, // Protected mode enable
MP = 1, // Monitor co-processor MP = 1, // Monitor co-processor
EM = 2, // (FPU) Emulation
TS = 3, // Task switched
ET = 4, // Extension type ET = 4, // Extension type
NE = 5, // Numeric error NE = 5, // Numeric error
WP = 16, // (ring 0) Write protect WP = 16, // (ring 0) Write protect
CD = 30, // Cache disable
PG = 31, // Paging PG = 31, // Paging
}; };
@@ -47,6 +51,8 @@ enum class xcr0
ZMM_Hi256, ZMM_Hi256,
ZMM_Hi16, ZMM_Hi16,
PKRU = 9, PKRU = 9,
J6_SUPPORTED = X87 | SSE | AVX | BINDREG | BINDCSR | OPMASK | ZMM_Hi16 | ZMM_Hi256,
}; };
enum class efer enum class efer
@@ -58,6 +64,26 @@ enum class efer
FFXSR = 14, // Fast FXSAVE FFXSR = 14, // Fast FXSAVE
}; };
enum class mxcsr
{
IE = 0, // Invalid operation flag
DE = 1, // Denormal flag
ZE = 2, // Divide by zero flag
OE = 3, // Overflow flag
UE = 4, // Underflow flag
PE = 5, // Precision flag
DAZ = 6, // Denormals are zero
IM = 7, // Invalid operation mask
DM = 8, // Denormal mask
ZM = 9, // Divide by zero mask
OM = 10, // Overflow mask
UM = 11, // Underflow mask
PM = 12, // Precision mask
RC0 = 13, // Rounding control bit 0
RC1 = 14, // Rounding control bit 1
FTZ = 15, // Flush to zero
};
struct cpu_state struct cpu_state
{ {
uint64_t r15, r14, r13, r12, r11, r10, r9, r8; uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
@@ -105,9 +131,20 @@ struct cpu_data
// the assembly version // the assembly version
lapic *apic; lapic *apic;
panic_data *panic; panic_data *panic;
cpu::features features;
}; };
extern "C" cpu_data * _current_gsbase(); extern "C" {
uint32_t get_mxcsr();
uint32_t set_mxcsr(uint32_t val);
uint64_t get_xcr0();
uint64_t set_xcr0(uint64_t val);
cpu_data * _current_gsbase();
void cpu_initialize_thread_state();
}
/// Do early initialization of the BSP CPU. /// Do early initialization of the BSP CPU.
/// \returns A pointer to the BSP cpu_data structure /// \returns A pointer to the BSP cpu_data structure

View File

@@ -1,3 +1,34 @@
global get_mxcsr: function hidden (get_mxcsr.end - get_mxcsr)
get_mxcsr:
push 0
stmxcsr [rsp]
pop rax
ret
.end:
global set_mxcsr: function hidden (set_mxcsr.end - set_mxcsr)
set_mxcsr:
push rdi
ldmxcsr [rsp]
pop rax
ret
.end:
global get_xcr0: function hidden (get_xcr0.end - get_xcr0)
get_xcr0:
xor rcx, rcx ; there is no dana there is only xcr0
xgetbv
ret ; technically edx has the high 32 bits, but bits 10+ are reserved
.end:
global set_xcr0: function hidden (set_xcr0.end - set_xcr0)
set_xcr0:
xor rcx, rcx ; there is no dana there is only xcr0
mov rax, rdi ; technically edx should be or'd into the high bits, but xcr0 bits 10+ are resereved
xsetbv
ret
.end:
global get_rsp: function hidden (get_rsp.end - get_rsp) global get_rsp: function hidden (get_rsp.end - get_rsp)
get_rsp: get_rsp:
mov rax, rsp mov rax, rsp

87
src/kernel/debugcon.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include <util/format.h>
#include "debugcon.h"
#include "interrupts.h"
#include "logger.h"
namespace debugcon {
namespace {
static const uint8_t level_colors[] = {0x00, 0x09, 0x01, 0x0b, 0x0f, 0x07, 0x08};
const char *level_names[] = {"", "fatal", "error", "warn", "info", "verbose", "spam"};
const char *area_names[] = {
#define LOG(name, lvl) #name ,
#include <j6/tables/log_areas.inc>
#undef LOG
nullptr
};
inline void debug_out(const char *msg, size_t size) {
asm ( "rep outsb;" :: "c"(size), "d"(port), "S"(msg) );
}
inline void debug_endline() {
static const char *newline = "\e[0m\r\n";
asm ( "rep outsb;" :: "c"(6), "d"(port), "S"(newline) );
}
} // anon namespace
void
write(const char *fmt, ...)
{
char buffer[256];
va_list va;
va_start(va, fmt);
size_t n = util::vformat({buffer, sizeof(buffer)}, fmt, va);
va_end(va);
debug_out(buffer, n);
debug_out("\r\n", 2);
}
void
output(j6_log_entry *entry)
{
char buffer [256];
size_t dlen = util::format({buffer, sizeof(buffer)}, "\e[38;5;%dm%7s %7s\e[38;5;14m|\e[38;5;%dm ",
level_colors[entry->severity],
area_names[entry->area],
level_names[entry->severity],
level_colors[entry->severity]);
debug_out(buffer, dlen);
debug_out(entry->message, entry->bytes - sizeof(j6_log_entry));
debug_endline();
}
void
logger_task()
{
using entry = j6_log_entry;
uint64_t seen = 0;
size_t buf_size = 128;
uint8_t *buf = new uint8_t [buf_size];
while (true) {
size_t size = g_logger.get_entry(seen, buf, buf_size);
if (size > buf_size) {
delete [] buf;
buf_size *= 2;
buf = new uint8_t [buf_size];
continue;
}
entry *header = reinterpret_cast<entry*>(buf);
while (size > sizeof(entry)) {
output(header);
seen = header->id;
size -= header->bytes;
header = util::offset_pointer(header, header->bytes);
}
}
}
} // namespace debugcon

31
src/kernel/debugcon.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
/// \file debugcon.h
/// Kernel debugcon log output process
#include "scheduler.h"
namespace debugcon {
static constexpr bool enable =
#ifdef __jsix_config_debug
true;
#else
false;
#endif
static constexpr uint16_t port = 0x6600;
void logger_task();
void write(const char *fmt, ...);
inline void
init_logger()
{
if constexpr (enable) {
static constexpr uint8_t pri = scheduler::max_priority - 1;
scheduler &s = scheduler::get();
s.create_kernel_task(logger_task, pri, true);
}
}
} // namespace debugcon

View File

@@ -1,11 +1,10 @@
#include <new>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <util/misc.h> // for checksum #include <util/misc.h> // for checksum
#include <util/pointers.h> #include <util/pointers.h>
#include <arch/acpi/tables.h>
#include "assert.h" #include "kassert.h"
#include "acpi_tables.h"
#include "apic.h" #include "apic.h"
#include "clock.h" #include "clock.h"
#include "device_manager.h" #include "device_manager.h"
@@ -21,37 +20,6 @@ static const char expected_signature[] = "RSD PTR ";
device_manager device_manager::s_instance; device_manager device_manager::s_instance;
struct acpi1_rsdp
{
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__ ((packed));
struct acpi2_rsdp
{
char signature[8];
uint8_t checksum10;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
uint32_t length;
acpi_table_header *xsdt_address;
uint8_t checksum20;
uint8_t reserved[3];
} __attribute__ ((packed));
bool
acpi_table_header::validate(uint32_t expected_type) const
{
if (util::checksum(this, length) != 0) return false;
return !expected_type || (expected_type == type);
}
device_manager::device_manager() : device_manager::device_manager() :
m_lapic_base(0) m_lapic_base(0)
{ {
@@ -63,37 +31,32 @@ device_manager::device_manager() :
m_irqs[2] = {ignore_event, 0}; m_irqs[2] = {ignore_event, 0};
} }
template <typename T> static const T *
check_get_table(const acpi_table_header *header)
{
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
return reinterpret_cast<const T *>(header);
}
void void
device_manager::parse_acpi(const void *root_table) device_manager::parse_acpi(const void *root_table)
{ {
kassert(root_table != 0, "ACPI root table pointer is null."); kassert(root_table != 0, "ACPI root table pointer is null.");
const acpi1_rsdp *acpi1 = mem::to_virtual( const acpi::rsdp1 *acpi1 = mem::to_virtual(
reinterpret_cast<const acpi1_rsdp *>(root_table)); reinterpret_cast<const acpi::rsdp1 *>(root_table));
for (int i = 0; i < sizeof(acpi1->signature); ++i) for (int i = 0; i < sizeof(acpi1->signature); ++i)
kassert(acpi1->signature[i] == expected_signature[i], kassert(acpi1->signature[i] == expected_signature[i],
"ACPI RSDP table signature mismatch"); "ACPI RSDP table signature mismatch");
uint8_t sum = util::checksum(acpi1, sizeof(acpi1_rsdp), 0); uint8_t sum = util::checksum(acpi1, sizeof(acpi::rsdp1), 0);
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch."); kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
kassert(acpi1->revision > 1, "ACPI 1.0 not supported."); kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
const acpi2_rsdp *acpi2 = const acpi::rsdp2 *acpi2 =
reinterpret_cast<const acpi2_rsdp *>(acpi1); reinterpret_cast<const acpi::rsdp2 *>(acpi1);
sum = util::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp)); sum = util::checksum(acpi2, sizeof(acpi::rsdp2), sizeof(acpi::rsdp1));
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch."); kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
load_xsdt(mem::to_virtual(acpi2->xsdt_address)); const acpi::table_header *xsdt = mem::to_virtual(acpi2->xsdt_address);
kassert(xsdt->validate(), "Bad XSDT table");
load_xsdt(xsdt);
} }
const device_manager::apic_nmi * const device_manager::apic_nmi *
@@ -129,53 +92,50 @@ put_sig(char *into, uint32_t type)
} }
void void
device_manager::load_xsdt(const acpi_table_header *header) device_manager::load_xsdt(const acpi::table_header *header)
{ {
const auto *xsdt = check_get_table<acpi_xsdt>(header); const auto *xsdt = acpi::check_get_table<acpi::xsdt>(header);
char sig[5] = {0,0,0,0,0}; char sig[5] = {0,0,0,0,0};
log::info(logs::device, "ACPI 2.0+ tables loading"); log::info(logs::device, "ACPI 2.0+ tables loading");
put_sig(sig, xsdt->header.type); put_sig(sig, xsdt->header.type);
log::verbose(logs::device, " Found table %s", sig); log::verbose(logs::device, " Loading table %s", sig);
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*)); size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
for (size_t i = 0; i < num_tables; ++i) { for (size_t i = 0; i < num_tables; ++i) {
const acpi_table_header *header = const acpi::table_header *header =
mem::to_virtual(xsdt->headers[i]); mem::to_virtual(xsdt->headers[i]);
put_sig(sig, header->type);
log::verbose(logs::device, " Found table %s", sig);
kassert(header->validate(), "Table failed validation."); kassert(header->validate(), "Table failed validation.");
put_sig(sig, header->type);
switch (header->type) { switch (header->type) {
case acpi_apic::type_id: case acpi::apic::type_id:
log::verbose(logs::device, " Loading table %s", sig);
load_apic(header); load_apic(header);
break; break;
case acpi_mcfg::type_id: case acpi::hpet::type_id:
load_mcfg(header); log::verbose(logs::device, " Loading table %s", sig);
break;
case acpi_hpet::type_id:
load_hpet(header); load_hpet(header);
break; break;
default: default:
log::verbose(logs::device, " Skipping table %s", sig);
break; break;
} }
} }
} }
void void
device_manager::load_apic(const acpi_table_header *header) device_manager::load_apic(const acpi::table_header *header)
{ {
const auto *apic = check_get_table<acpi_apic>(header); const auto *apic = acpi::check_get_table<acpi::apic>(header);
m_lapic_base = apic->local_address; m_lapic_base = apic->local_address;
size_t count = acpi_table_entries(apic, 1); size_t count = acpi::table_entries(apic, 1);
uint8_t const *p = apic->controller_data; uint8_t const *p = apic->controller_data;
uint8_t const *end = p + count; uint8_t const *end = p + count;
@@ -265,33 +225,9 @@ device_manager::load_apic(const acpi_table_header *header)
} }
void void
device_manager::load_mcfg(const acpi_table_header *header) device_manager::load_hpet(const acpi::table_header *header)
{ {
const auto *mcfg = check_get_table<acpi_mcfg>(header); const auto *hpet = acpi::check_get_table<acpi::hpet>(header);
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
m_pci.set_size(count);
m_devices.set_capacity(16);
for (unsigned i = 0; i < count; ++i) {
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
m_pci[i].group = mcfge.group;
m_pci[i].bus_start = mcfge.bus_start;
m_pci[i].bus_end = mcfge.bus_end;
m_pci[i].base = mem::to_virtual<uint32_t>(mcfge.base);
log::spam(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
}
probe_pci();
}
void
device_manager::load_hpet(const acpi_table_header *header)
{
const auto *hpet = check_get_table<acpi_hpet>(header);
log::verbose(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x", log::verbose(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes); hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
@@ -311,28 +247,6 @@ device_manager::load_hpet(const acpi_table_header *header)
reinterpret_cast<uint64_t*>(hpet->base_address.address + mem::linear_offset)); reinterpret_cast<uint64_t*>(hpet->base_address.address + mem::linear_offset));
} }
void
device_manager::probe_pci()
{
for (auto &pci : m_pci) {
log::verbose(logs::device, "Probing PCI group at base %016lx", pci.base);
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
for (int dev = 0; dev < 32; ++dev) {
if (!pci.has_device(bus, dev, 0)) continue;
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
if (!d0.multi()) continue;
for (int i = 1; i < 8; ++i) {
if (pci.has_device(bus, dev, i))
m_devices.emplace(pci, bus, dev, i);
}
}
}
}
}
static uint64_t static uint64_t
tsc_clock_source(void*) tsc_clock_source(void*)
{ {
@@ -366,7 +280,7 @@ device_manager::init_drivers()
// becomes the singleton // becomes the singleton
master_clock = new clock(h.rate(), hpet_clock_source, &h); master_clock = new clock(h.rate(), hpet_clock_source, &h);
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate()); log::info(logs::timer, "Created master clock using HPET 0: Rate %d", h.rate());
} else { } else {
//TODO: Other clocks, APIC clock? //TODO: Other clocks, APIC clock?
master_clock = new clock(5000, tsc_clock_source, nullptr); master_clock = new clock(5000, tsc_clock_source, nullptr);
@@ -385,7 +299,7 @@ device_manager::dispatch_irq(unsigned irq)
if (!binding.target || binding.target == ignore_event) if (!binding.target || binding.target == ignore_event)
return binding.target == ignore_event; return binding.target == ignore_event;
binding.target->signal(1 << binding.signal); binding.target->signal(1ull << binding.signal);
return true; return true;
} }
@@ -410,10 +324,10 @@ device_manager::unbind_irqs(obj::event *target)
} }
} }
/*
bool bool
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data) device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
{ {
/*
// TODO: find gaps to fill // TODO: find gaps to fill
uint8_t irq = m_irqs.count(); uint8_t irq = m_irqs.count();
isr vector = isr::irq00 + irq; isr vector = isr::irq00 + irq;
@@ -424,12 +338,6 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
device.write_msi_regs( device.write_msi_regs(
0xFEE00000, 0xFEE00000,
static_cast<uint16_t>(vector)); static_cast<uint16_t>(vector));
*/
return true; return true;
} }
*/
void
device_manager::register_block_device(block_device *blockdev)
{
m_blockdevs.append(blockdev);
}

View File

@@ -6,11 +6,13 @@
#include "apic.h" #include "apic.h"
#include "hpet.h" #include "hpet.h"
#include "pci.h"
struct acpi_table_header;
class block_device; class block_device;
namespace acpi {
struct table_header;
}
namespace obj { namespace obj {
class event; class event;
} }
@@ -53,18 +55,6 @@ public:
/// \arg target The endpoint to remove /// \arg target The endpoint to remove
void unbind_irqs(obj::event *target); void unbind_irqs(obj::event *target);
/// Allocate an MSI IRQ for a device
/// \arg name Name of the interrupt, for display to user
/// \arg device Device this MSI is being allocated for
/// \arg cb Callback to call when the interrupt is received
/// \arg data Data to pass to the callback
/// \returns True if an interrupt was allocated successfully
bool allocate_msi(
const char *name,
pci_device &device,
irq_callback cb,
void *data);
/// Dispatch an IRQ interrupt /// Dispatch an IRQ interrupt
/// \arg irq The irq number of the interrupt /// \arg irq The irq number of the interrupt
/// \returns True if the interrupt was handled /// \returns True if the interrupt was handled
@@ -103,23 +93,6 @@ public:
/// configuration, or null if no configuration was provided /// configuration, or null if no configuration was provided
const irq_override * get_irq_override(uint8_t irq) const; const irq_override * get_irq_override(uint8_t irq) const;
/// Register the existance of a block device.
/// \arg blockdev Pointer to the block device
void register_block_device(block_device *blockdev);
/// Get the number of block devices in the system
/// \returns A count of devices
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
/// Get a block device
/// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr
inline block_device * get_block_device(unsigned i)
{
return i < m_blockdevs.count() ?
m_blockdevs[i] : nullptr;
}
/// Get an HPET device /// Get an HPET device
/// \arg i Index of the device to get /// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr /// \returns A pointer to the requested device, or nullptr
@@ -132,23 +105,15 @@ public:
private: private:
/// Parse the ACPI XSDT and load relevant sub-tables. /// Parse the ACPI XSDT and load relevant sub-tables.
/// \arg xsdt Pointer to the XSDT from the firmware /// \arg xsdt Pointer to the XSDT from the firmware
void load_xsdt(const acpi_table_header *xsdt); void load_xsdt(const acpi::table_header *xsdt);
/// Parse the ACPI MADT and initialize APICs from it. /// Parse the ACPI MADT and initialize APICs from it.
/// \arg apic Pointer to the MADT from the XSDT /// \arg apic Pointer to the MADT from the XSDT
void load_apic(const acpi_table_header *apic); void load_apic(const acpi::table_header *apic);
/// Parse the ACPI MCFG and initialize PCIe from it.
/// \arg mcfg Pointer to the MCFG from the XSDT
void load_mcfg(const acpi_table_header *mcfg);
/// Parse the ACPI HPET and initialize an HPET from it. /// Parse the ACPI HPET and initialize an HPET from it.
/// \arg hpet Pointer to the HPET from the XSDT /// \arg hpet Pointer to the HPET from the XSDT
void load_hpet(const acpi_table_header *hpet); void load_hpet(const acpi::table_header *hpet);
/// Probe the PCIe busses and add found devices to our
/// device list. The device list is destroyed and rebuilt.
void probe_pci();
/// Handle a bad IRQ. Called when an interrupt is dispatched /// Handle a bad IRQ. Called when an interrupt is dispatched
/// that has no callback. /// that has no callback.
@@ -162,9 +127,6 @@ private:
util::vector<apic_nmi> m_nmis; util::vector<apic_nmi> m_nmis;
util::vector<irq_override> m_overrides; util::vector<irq_override> m_overrides;
util::vector<pci_group> m_pci;
util::vector<pci_device> m_devices;
struct irq_binding struct irq_binding
{ {
obj::event *target = nullptr; obj::event *target = nullptr;
@@ -172,8 +134,6 @@ private:
}; };
util::vector<irq_binding> m_irqs; util::vector<irq_binding> m_irqs;
util::vector<block_device *> m_blockdevs;
static device_manager s_instance; static device_manager s_instance;
device_manager(const device_manager &) = delete; device_manager(const device_manager &) = delete;

View File

@@ -1,6 +1,7 @@
#include <bootproto/kernel.h> #include <bootproto/kernel.h>
#include "assert.h" #include "kassert.h"
#include "debugcon.h"
#include "frame_allocator.h" #include "frame_allocator.h"
#include "logger.h" #include "logger.h"
#include "memory.h" #include "memory.h"
@@ -51,7 +52,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address)
unsigned frame = (o1 << 12) + (o2 << 6) + o3; unsigned frame = (o1 << 12) + (o2 << 6) + o3;
// See how many contiguous pages are here // See how many contiguous pages are here
unsigned n = bsf(~m3 >> o3); unsigned n = bsf(~(m3 >> o3));
if (n > count) if (n > count)
n = count; n = count;
@@ -71,6 +72,7 @@ frame_allocator::allocate(size_t count, uintptr_t *address)
} }
} }
//debugcon::write("Allocated %2d frames at %016lx - %016lx", n, *address, *address + n * frame_size);
return n; return n;
} }
@@ -88,6 +90,8 @@ frame_allocator::free(uintptr_t address, size_t count)
if (!count) if (!count)
return; return;
//debugcon::write("Freeing %2d frames at %016lx - %016lx", count, address, address + count * frame_size);
for (long i = 0; i < m_count; ++i) { for (long i = 0; i < m_count; ++i) {
frame_block &block = m_blocks[i]; frame_block &block = m_blocks[i];
uintptr_t end = block.base + block.count * frame_size; uintptr_t end = block.base + block.count * frame_size;
@@ -101,9 +105,9 @@ frame_allocator::free(uintptr_t address, size_t count)
unsigned o3 = frame & 0x3f; unsigned o3 = frame & 0x3f;
while (count--) { while (count--) {
block.map1 |= (1 << o1); block.map1 |= (1ull << o1);
block.map2[o1] |= (1 << o2); block.map2[o1] |= (1ull << o2);
block.bitmap[o2] |= (1 << o3); block.bitmap[o2] |= (1ull << o3);
if (++o3 == 64) { if (++o3 == 64) {
o3 = 0; o3 = 0;
if (++o2 == 64) { if (++o2 == 64) {
@@ -139,12 +143,12 @@ frame_allocator::used(uintptr_t address, size_t count)
unsigned o3 = frame & 0x3f; unsigned o3 = frame & 0x3f;
while (count--) { while (count--) {
block.bitmap[o2] &= ~(1 << o3); block.bitmap[o2] &= ~(1ull << o3);
if (!block.bitmap[o2]) { if (!block.bitmap[o2]) {
block.map2[o1] &= ~(1 << o2); block.map2[o1] &= ~(1ull << o2);
if (!block.map2[o1]) { if (!block.map2[o1]) {
block.map1 &= ~(1 << o1); block.map1 &= ~(1ull << o1);
} }
} }

View File

@@ -1,9 +1,7 @@
#include <stdint.h> #include <j6/memutils.h>
#include <string.h>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "assert.h" #include "kassert.h"
#include "cpu.h" #include "cpu.h"
#include "gdt.h" #include "gdt.h"
#include "logger.h" #include "logger.h"

View File

@@ -1,13 +1,14 @@
#include <new>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <j6/memutils.h>
#include <util/pointers.h> #include <util/pointers.h>
#include <util/util.h> #include <util/util.h>
#include "assert.h" #include "kassert.h"
#include "heap_allocator.h" #include "heap_allocator.h"
#include "memory.h" #include "memory.h"
#include "objects/vm_area.h"
#include "vm_space.h"
uint32_t & get_map_key(heap_allocator::block_info &info) { return info.offset; } uint32_t & get_map_key(heap_allocator::block_info &info) { return info.offset; }
@@ -27,7 +28,7 @@ struct heap_allocator::free_header
inline free_header * buddy() const { inline free_header * buddy() const {
return reinterpret_cast<free_header *>( return reinterpret_cast<free_header *>(
reinterpret_cast<uintptr_t>(this) ^ (1 << order)); reinterpret_cast<uintptr_t>(this) ^ (1ull << order));
} }
inline bool eldest() const { return this < buddy(); } inline bool eldest() const { return this < buddy(); }
@@ -46,7 +47,6 @@ heap_allocator::heap_allocator(uintptr_t start, size_t size, uintptr_t heapmap)
m_maxsize {size}, m_maxsize {size},
m_allocated_size {0}, m_allocated_size {0},
m_map (reinterpret_cast<block_info*>(heapmap), 512) m_map (reinterpret_cast<block_info*>(heapmap), 512)
{ {
memset(m_free, 0, sizeof(m_free)); memset(m_free, 0, sizeof(m_free));
} }
@@ -57,6 +57,11 @@ heap_allocator::allocate(size_t length)
if (length == 0) if (length == 0)
return nullptr; return nullptr;
static constexpr unsigned min_order =
__debug_heap_allocation ?
12 : // allocating full pages in debug mode
heap_allocator::min_order;
unsigned order = util::log2(length); unsigned order = util::log2(length);
if (order < min_order) if (order < min_order)
order = min_order; order = min_order;
@@ -67,7 +72,7 @@ heap_allocator::allocate(size_t length)
util::scoped_lock lock {m_lock}; util::scoped_lock lock {m_lock};
m_allocated_size += (1 << order); m_allocated_size += (1ull << order);
free_header *block = pop_free(order); free_header *block = pop_free(order);
if (!block && !split_off(order, block)) { if (!block && !split_off(order, block)) {
@@ -95,7 +100,17 @@ heap_allocator::free(void *p)
kassert(info, "Attempt to free pointer not known to the heap"); kassert(info, "Attempt to free pointer not known to the heap");
if (!info) return; if (!info) return;
m_allocated_size -= (1 << info->order); size_t size = (1ull << info->order);
m_allocated_size -= size;
if constexpr (__debug_heap_allocation) {
extern obj::vm_area_untracked &g_kernel_heap_area;
size_t offset = reinterpret_cast<uintptr_t>(p) - mem::heap_offset;
size_t pages = mem::bytes_to_pages(size);
vm_space::kernel_space().lock(g_kernel_heap_area, offset, pages);
return;
}
block->clear(info->order); block->clear(info->order);
block = merge_block(block); block = merge_block(block);
@@ -118,15 +133,15 @@ heap_allocator::reallocate(void *p, size_t old_length, size_t new_length)
if (!info) if (!info)
return nullptr; return nullptr;
if (new_length <= (1 << info->order)) if (new_length <= (1ull << info->order))
return p; return p;
lock.release(); lock.release();
void *new_block = allocate(new_length); void *reallocated = allocate(new_length);
memcpy(new_block, p, old_length); memcpy(reallocated, p, old_length);
free(p); free(p);
return new_block; return reallocated;
} }
heap_allocator::free_header * heap_allocator::free_header *
@@ -180,13 +195,15 @@ heap_allocator::new_block(unsigned order)
unsigned current = address_order(m_end); unsigned current = address_order(m_end);
while (current < order) { while (current < order) {
register_free_block(reinterpret_cast<free_header*>(m_end), current); register_free_block(reinterpret_cast<free_header*>(m_end), current);
m_end += 1 << current; m_end += 1ull << current;
current = address_order(m_end); current = address_order(m_end);
} }
void *block = reinterpret_cast<void*>(m_end); void *block = reinterpret_cast<void*>(m_end);
m_end += 1 << order; m_end += 1ull << order;
m_map[map_key(block)].order = order; block_info &info = m_map[map_key(block)];
info.order = order;
info.free = false;
return block; return block;
} }

View File

@@ -34,8 +34,9 @@ public:
/// allocation with the contents copied over. /// allocation with the contents copied over.
void * reallocate(void *p, size_t old_length, size_t new_length); void * reallocate(void *p, size_t old_length, size_t new_length);
/// Minimum block size is (2^min_order). Must be at least 6. /// Minimum block size is (2^min_order). Must be at least 5 in
static const unsigned min_order = 6; // 2^6 == 64 B /// order to hold a free_header.
static const unsigned min_order = 5; // 2^5 == 32 B
/// Maximum block size is (2^max_order). Must be less than 32 + min_order. /// Maximum block size is (2^max_order). Must be less than 32 + min_order.
static const unsigned max_order = 22; // 2^22 == 4 MiB static const unsigned max_order = 22; // 2^22 == 4 MiB

View File

@@ -1,4 +1,4 @@
#include "assert.h" #include "kassert.h"
#include "device_manager.h" #include "device_manager.h"
#include "hpet.h" #include "hpet.h"
#include "io.h" #include "io.h"

View File

@@ -1,5 +1,4 @@
#include <string.h> #include <j6/memutils.h>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "cpu.h" #include "cpu.h"

View File

@@ -2,7 +2,7 @@
/// \file idt.h /// \file idt.h
/// Definitions relating to a CPU's IDT table /// Definitions relating to a CPU's IDT table
#include <stdint.h> #include <stdint.h>
#include "assert.h" #include "kassert.h"
class IDT class IDT
{ {

View File

@@ -1,7 +1,7 @@
#include <stdint.h> #include <stdint.h>
#include <util/format.h> #include <util/format.h>
#include "assert.h" #include "kassert.h"
#include "cpu.h" #include "cpu.h"
#include "device_manager.h" #include "device_manager.h"
#include "idt.h" #include "idt.h"
@@ -145,6 +145,30 @@ isr_handler(cpu_state *regs)
} }
break; break;
case isr::isrSIMDFPE: {
uint32_t mxcsr = 0;
asm volatile ("stmxcsr %0" : "=m"(mxcsr));
util::format({message, sizeof(message)},
"SIMD Exception; MXCSR[%s%s%s%s%s%s%s%s%s%s%s%s%s%s rc:%d]",
(mxcsr & 0x0001) ? " IE" : "",
(mxcsr & 0x0002) ? " DE" : "",
(mxcsr & 0x0004) ? " ZE" : "",
(mxcsr & 0x0008) ? " OE" : "",
(mxcsr & 0x0010) ? " UE" : "",
(mxcsr & 0x0020) ? " PE" : "",
(mxcsr & 0x0040) ? " DAZ" : "",
(mxcsr & 0x0080) ? " IM" : "",
(mxcsr & 0x0100) ? " DM" : "",
(mxcsr & 0x0200) ? " ZM" : "",
(mxcsr & 0x0400) ? " OM" : "",
(mxcsr & 0x0800) ? " UM" : "",
(mxcsr & 0x1000) ? " PM" : "",
(mxcsr & 0x8000) ? " FTZ" : "",
((mxcsr >> 13) & 0x3));
kassert(false, message, regs);
}
break;
case isr::isrSpurious: case isr::isrSpurious:
// No EOI for the spurious interrupt // No EOI for the spurious interrupt
return; return;

View File

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

25
src/kernel/ipc_message.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
/// \file ipc_message.h
/// Definition of shared message structure
#include <stdint.h>
#include <j6/types.h>
#include <util/counted.h>
namespace ipc {
struct message
{
uint64_t tag;
util::buffer data;
util::counted<j6_handle_t> handles;
message();
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
message(message &&other);
~message();
message & operator=(message &&other);
};
} // namespace ipc

57
src/kernel/kassert.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "kassert.h"
#include "idt.h"
namespace panic {
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
void const *symbol_table = nullptr;
void
install(uintptr_t entrypoint, util::const_buffer symbol_data)
{
IDT::set_nmi_handler(entrypoint);
symbol_table = symbol_data.pointer;
}
[[ noreturn ]]
void panic(
const char *message,
const cpu_state *user,
const char *function,
const char *file,
uint64_t line)
{
cpu_data &cpu = current_cpu();
// Grab the global panic block for ourselves
cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL);
// If we aren't the first CPU to panic, cpu.panic will be null
if (cpu.panic) {
cpu.panic->symbol_data = symbol_table;
cpu.panic->user_state = user;
cpu.panic->message = message;
cpu.panic->function = function;
cpu.panic->file = file;
cpu.panic->line = line;
cpu.panic->cpus = g_num_cpus;
static constexpr uint32_t send_nmi_command =
(4 << 8) | // Delivery mode NMI
(1 << 14) | // assert level high
(2 << 18); // destination all
*apic_icr = send_nmi_command;
}
while (1) asm ("hlt");
}
} // namespace panic
extern "C"
void __assert_fail(const char *message, const char *file, unsigned line, const char *function) {
panic::panic(message, nullptr, function, file, line);
}

View File

@@ -7,45 +7,13 @@
namespace panic { namespace panic {
constexpr uint32_t send_nmi_command =
(4 << 8) | // Delivery mode NMI
(1 << 14) | // assert level high
(2 << 18); // destination all
extern uint32_t *apic_icr;
extern void const *symbol_table;
[[ noreturn ]] [[ noreturn ]]
__attribute__ ((always_inline)) void panic(
inline void panic(
const char *message = nullptr, const char *message = nullptr,
const cpu_state *user = nullptr, const cpu_state *user = nullptr,
const char *function = __builtin_FUNCTION(), const char *function = __builtin_FUNCTION(),
const char *file = __builtin_FILE(), const char *file = __builtin_FILE(),
uint64_t line = __builtin_LINE()) uint64_t line = __builtin_LINE());
{
cpu_data &cpu = current_cpu();
// Grab the global panic block for ourselves
cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL);
// If we aren't the first CPU to panic, cpu.panic will be null
if (cpu.panic) {
cpu.panic->symbol_data = symbol_table;
cpu.panic->user_state = user;
cpu.panic->message = message;
cpu.panic->function = function;
cpu.panic->file = file;
cpu.panic->line = line;
cpu.panic->cpus = g_num_cpus;
*apic_icr = send_nmi_command;
}
while (1) asm ("hlt");
}
/// Install a panic handler. /// Install a panic handler.
/// \arg entrypoint Virtual address of the panic handler's entrypoint /// \arg entrypoint Virtual address of the panic handler's entrypoint

View File

@@ -1,22 +1,22 @@
# vim: ft=python # vim: ft=python
kernel = module("kernel", kernel = module("kernel",
kind = "exe",
default = True, default = True,
output = "jsix.elf", basename = "jsix",
targets = [ "kernel" ], targets = [ "kernel" ],
description = "jsix kernel", description = "jsix kernel",
deps = [ "util", "cpu", "bootproto", "j6" ], deps = [ "util", "cpu", "bootproto", "j6" ],
static = True,
ld_script = "kernel.ld", ld_script = "kernel.ld",
sources = [ sources = [
"apic.cpp", "apic.cpp",
"assert.cpp", "kassert.cpp",
"boot.s", "boot.s",
"capabilities.cpp", "capabilities.cpp",
"clock.cpp", "clock.cpp",
"cpprt.cpp", "cpprt.cpp",
"cpu.cpp", "cpu.cpp",
"debug.s", "cpu.s",
"device_manager.cpp", "device_manager.cpp",
"frame_allocator.cpp", "frame_allocator.cpp",
"gdt.cpp", "gdt.cpp",
@@ -27,13 +27,13 @@ kernel = module("kernel",
"interrupts.cpp", "interrupts.cpp",
"interrupts.s", "interrupts.s",
"io.cpp", "io.cpp",
"ipc_message.cpp",
"kernel_main.cpp", "kernel_main.cpp",
"logger.cpp", "logger.cpp",
"memory.cpp", "memory.cpp",
"memory.h.cog", "memory.h.cog",
"memory_bootstrap.cpp", "memory_bootstrap.cpp",
"msr.cpp", "msr.cpp",
"objects/channel.cpp",
"objects/event.cpp", "objects/event.cpp",
"objects/kobject.cpp", "objects/kobject.cpp",
"objects/mailbox.cpp", "objects/mailbox.cpp",
@@ -43,7 +43,6 @@ kernel = module("kernel",
"objects/vm_area.cpp", "objects/vm_area.cpp",
"page_table.cpp", "page_table.cpp",
"page_tree.cpp", "page_tree.cpp",
"pci.cpp",
"scheduler.cpp", "scheduler.cpp",
"smp.cpp", "smp.cpp",
"smp.s", "smp.s",
@@ -52,12 +51,12 @@ kernel = module("kernel",
"syscall.s", "syscall.s",
"syscall_verify.cpp.cog", "syscall_verify.cpp.cog",
"syscalls.inc.cog", "syscalls.inc.cog",
"syscalls/channel.cpp",
"syscalls/event.cpp", "syscalls/event.cpp",
"syscalls/handle.cpp", "syscalls/handle.cpp",
"syscalls/mailbox.cpp", "syscalls/mailbox.cpp",
"syscalls/object.cpp", "syscalls/object.cpp",
"syscalls/process.cpp", "syscalls/process.cpp",
"syscalls/futex.cpp",
"syscalls/system.cpp", "syscalls/system.cpp",
"syscalls/thread.cpp", "syscalls/thread.cpp",
"syscalls/vm_area.cpp", "syscalls/vm_area.cpp",
@@ -67,8 +66,12 @@ kernel = module("kernel",
"tss.cpp", "tss.cpp",
"vm_space.cpp", "vm_space.cpp",
"wait_queue.cpp", "wait_queue.cpp",
"xsave.cpp",
]) ])
if config == "debug":
kernel.add_input("debugcon.cpp")
from glob import glob from glob import glob
from os.path import join from os.path import join

View File

@@ -1,13 +1,14 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <j6/memutils.h>
#include <bootproto/kernel.h> #include <bootproto/kernel.h>
#include <util/vector.h> #include <util/vector.h>
#include "assert.h" #include "kassert.h"
#include "capabilities.h" #include "capabilities.h"
#include "cpu.h" #include "cpu.h"
#include "debugcon.h"
#include "device_manager.h" #include "device_manager.h"
#include "interrupts.h" #include "interrupts.h"
#include "logger.h" #include "logger.h"
@@ -36,9 +37,8 @@ kernel_main(bootproto::args *args)
panic::install(args->panic_handler, args->symbol_table); panic::install(args->panic_handler, args->symbol_table);
} }
logger_init();
cpu_data *cpu = bsp_early_init(); cpu_data *cpu = bsp_early_init();
mem::initialize(*args);
kassert(args->magic == bootproto::args_magic, kassert(args->magic == bootproto::args_magic,
"Bad kernel args magic number"); "Bad kernel args magic number");
@@ -51,8 +51,6 @@ kernel_main(bootproto::args *args)
disable_legacy_pic(); disable_legacy_pic();
mem::initialize(*args);
bsp_late_init(); bsp_late_init();
using bootproto::boot_flags; using bootproto::boot_flags;
@@ -73,6 +71,10 @@ kernel_main(bootproto::args *args)
scheduler *sched = new scheduler {g_num_cpus}; scheduler *sched = new scheduler {g_num_cpus};
smp::ready(); smp::ready();
// Initialize the debug console logger (does nothing if not built
// in debug mode)
debugcon::init_logger();
// Load the init server // Load the init server
load_init_server(args->init, args->init_modules); load_init_server(args->init, args->init_modules);
@@ -98,18 +100,12 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none); ((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags); obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags);
space.add(sect.virt_addr, vma); space.add(sect.virt_addr, vma, obj::vm_flags::exact);
} }
uint64_t iopl = (3ull << 12); uint64_t iopl = (3ull << 12);
obj::thread *main = p->create_thread(); obj::thread *main = p->create_thread();
main->add_thunk_user(program.entrypoint, 0, iopl); main->add_thunk_user(program.entrypoint, modules_address, 0, 0, iopl);
main->set_state(obj::thread::state::ready); main->set_state(obj::thread::state::ready);
// Hacky: No process exists to have created a stack for init; it needs to create
// its own stack. We take advantage of that to use rsp to pass it the init modules
// address.
auto *tcb = main->tcb();
tcb->rsp3 = modules_address;
} }

View File

@@ -1,20 +1,12 @@
#include <new> #include <j6/memutils.h>
#include <string.h>
#include <util/format.h> #include <util/format.h>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "assert.h" #include "kassert.h"
#include "logger.h" #include "logger.h"
#include "memory.h"
#include "objects/system.h" #include "objects/system.h"
#include "objects/thread.h" #include "objects/thread.h"
static constexpr bool j6_debugcon_enable = false;
static constexpr uint16_t j6_debugcon_port = 0x6600;
static uint8_t log_buffer[log::logger::log_pages * arch::frame_size];
// The logger is initialized _before_ global constructors are called, // The logger is initialized _before_ global constructors are called,
// so that we can start log output immediately. Keep its constructor // so that we can start log output immediately. Keep its constructor
// from being called here so as to not overwrite the previous initialization. // from being called here so as to not overwrite the previous initialization.
@@ -24,38 +16,31 @@ log::logger &g_logger = __g_logger_storage.value;
namespace log { namespace log {
namespace {
} // anon namespace
logger *logger::s_log = nullptr; logger *logger::s_log = nullptr;
const char *logger::s_level_names[] = {"", "fatal", "error", "warn", "info", "verbose", "spam"};
const char *logger::s_area_names[] = {
#define LOG(name, lvl) #name ,
#include <j6/tables/log_areas.inc>
#undef LOG
nullptr
};
inline void
debug_out(const char *msg, size_t size)
{
asm ( "rep outsb;" :: "c"(size), "d"(j6_debugcon_port), "S"(msg) );
}
inline void
debug_newline()
{
static const char *newline = "\r\n";
asm ( "rep outsb;" :: "c"(2), "d"(j6_debugcon_port), "S"(newline) );
}
logger::logger() : logger::logger() :
m_buffer(nullptr, 0) m_buffer {nullptr, 0},
m_start {0},
m_end {0},
m_count {0}
{ {
memset(&m_levels, 0, sizeof(m_levels)); memset(&m_levels, 0, sizeof(m_levels));
s_log = this; s_log = this;
} }
logger::logger(uint8_t *buffer, size_t size) : logger::logger(util::buffer data) :
m_buffer(buffer, size) m_buffer {data},
m_start {0},
m_end {0},
m_count {0}
{ {
kassert((data.count & (data.count - 1)) == 0,
"log buffer size must be a power of two");
memset(&m_levels, 0, sizeof(m_levels)); memset(&m_levels, 0, sizeof(m_levels));
s_log = this; s_log = this;
@@ -68,71 +53,56 @@ logger::logger(uint8_t *buffer, size_t size) :
void void
logger::output(level severity, logs area, const char *fmt, va_list args) logger::output(level severity, logs area, const char *fmt, va_list args)
{ {
char buffer[256]; static constexpr size_t buffer_len = 256;
static constexpr size_t message_len = buffer_len - sizeof(j6_log_entry);
if constexpr (j6_debugcon_enable) { char buffer[buffer_len];
size_t dlen = util::format({buffer, sizeof(buffer)}, "%7s %7s| ", j6_log_entry *header = reinterpret_cast<j6_log_entry *>(buffer);
s_area_names[static_cast<uint8_t>(area)],
s_level_names[static_cast<uint8_t>(severity)]);
debug_out(buffer, dlen);
}
entry *header = reinterpret_cast<entry *>(buffer); size_t size = sizeof(j6_log_entry);
header->bytes = sizeof(entry); size += util::vformat({header->message, message_len}, fmt, args);
header->area = area;
header->severity = severity;
size_t mlen = util::vformat({header->message, sizeof(buffer) - sizeof(entry) - 1}, fmt, args);
header->message[mlen] = 0;
header->bytes += mlen + 1;
if constexpr (j6_debugcon_enable) {
debug_out(header->message, mlen);
debug_newline();
}
util::scoped_lock lock {m_lock}; util::scoped_lock lock {m_lock};
uint8_t *out; while (free() < size) {
size_t n = m_buffer.reserve(header->bytes, reinterpret_cast<void**>(&out)); // Remove old entries until there's enough space
if (n < header->bytes) { const j6_log_entry *first = util::at<const j6_log_entry>(m_buffer, start());
m_buffer.commit(0); // Cannot write the message, give up m_start += first->bytes;
return;
} }
memcpy(out, buffer, n); header->id = ++m_count;
m_buffer.commit(n); header->bytes = size;
header->severity = static_cast<uint8_t>(severity);
header->area = static_cast<uint8_t>(area);
m_event.signal(1); memcpy(util::at<void>(m_buffer, end()), buffer, size);
m_end += size;
m_waiting.clear();
} }
size_t size_t
logger::get_entry(void *buffer, size_t size) logger::get_entry(uint64_t seen, void *buffer, size_t size)
{ {
util::scoped_lock lock {m_lock}; util::scoped_lock lock {m_lock};
void *out; while (seen == m_count) {
size_t out_size = m_buffer.get_block(&out);
if (out_size == 0 || out == 0) {
lock.release(); lock.release();
m_event.wait(); m_waiting.wait();
lock.reacquire(); lock.reacquire();
out_size = m_buffer.get_block(&out);
if (out_size == 0 || out == 0)
return 0;
} }
kassert(out_size >= sizeof(entry), "Couldn't read a full entry"); size_t off = m_start;
if (out_size < sizeof(entry)) j6_log_entry *ent = util::at<j6_log_entry>(m_buffer, offset(off));
return 0; while (seen >= ent->id) {
off += ent->bytes;
entry *ent = reinterpret_cast<entry *>(out); kassert(off < m_end, "Got to the end while looking for new log entry");
if (size >= ent->bytes) { ent = util::at<j6_log_entry>(m_buffer, offset(off));
memcpy(buffer, out, ent->bytes);
m_buffer.consume(ent->bytes);
} }
if (size >= ent->bytes)
memcpy(buffer, ent, ent->bytes);
return ent->bytes; return ent->bytes;
} }
@@ -168,9 +138,3 @@ void fatal(logs area, const char *fmt, ...)
} }
} // namespace log } // namespace log
void logger_init()
{
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer));
}

View File

@@ -5,7 +5,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <util/bip_buffer.h> #include <util/counted.h>
#include <util/spinlock.h> #include <util/spinlock.h>
#include "objects/event.h" #include "objects/event.h"
@@ -19,6 +19,9 @@ enum class logs : uint8_t {
namespace log { namespace log {
/// Size of the log ring buffer. Must be a power of two.
inline constexpr unsigned log_pages = 16;
enum class level : uint8_t { enum class level : uint8_t {
silent, fatal, error, warn, info, verbose, spam, max silent, fatal, error, warn, info, verbose, spam, max
}; };
@@ -29,26 +32,16 @@ constexpr unsigned areas_count =
class logger class logger
{ {
public: public:
/// Size of the log ring buffer
static constexpr unsigned log_pages = 16;
/// Default constructor. Creates a logger without a backing store. /// Default constructor. Creates a logger without a backing store.
logger(); logger();
/// Constructor. Logs are written to the given buffer. /// Constructor. Logs are written to the given buffer.
/// \arg buffer Buffer to which logs are written /// \arg buffer Buffer to which logs are written
/// \arg size Size of `buffer`, in bytes logger(util::buffer buffer);
logger(uint8_t *buffer, size_t size);
/// Get the default logger. /// Get the default logger.
inline logger & get() { return *s_log; } inline logger & get() { return *s_log; }
/// Get the registered name for a given area
inline const char * area_name(logs area) const { return s_area_names[static_cast<unsigned>(area)]; }
/// Get the name of a level
inline const char * level_name(level l) const { return s_level_names[static_cast<unsigned>(l)]; }
/// Write to the log /// Write to the log
/// \arg severity The severity of the message /// \arg severity The severity of the message
/// \arg area The log area to write to /// \arg area The log area to write to
@@ -64,23 +57,18 @@ public:
va_end(args); va_end(args);
} }
struct entry /// Get the next log entry from the buffer. Blocks the current thread until
{ /// a log arrives if there are no entries newer than `seen`.
uint8_t bytes; /// \arg seen The id of the last-seen log entry, or 0 for none
logs area;
level severity;
char message[0];
};
/// Get the next log entry from the buffer
/// \arg buffer The buffer to copy the log message into /// \arg buffer The buffer to copy the log message into
/// \arg size Size of the passed-in buffer, in bytes /// \arg size Size of the passed-in buffer, in bytes
/// \returns The size of the log entry (if larger than the /// \returns The size of the log entry (if larger than the
/// buffer, then no data was copied) /// buffer, then no data was copied)
size_t get_entry(void *buffer, size_t size); size_t get_entry(uint64_t seen, void *buffer, size_t size);
/// Get whether there is currently data in the log buffer /// Check whether or not there's a new log entry to get
inline bool has_log() const { return m_buffer.size(); } /// \arg seen The id of the last-seen log entry, or 0 for none
inline bool has_entry(uint64_t seen) { return seen < m_count; }
private: private:
friend void spam (logs area, const char *fmt, ...); friend void spam (logs area, const char *fmt, ...);
@@ -100,16 +88,22 @@ private:
return m_levels[static_cast<unsigned>(area)]; return m_levels[static_cast<unsigned>(area)];
} }
obj::event m_event; inline size_t offset(size_t i) const { return i & (m_buffer.count - 1); }
inline size_t start() const { return offset(m_start); }
inline size_t end() const { return offset(m_end); }
inline size_t free() const { return m_buffer.count - (m_end - m_start); }
level m_levels[areas_count]; level m_levels[areas_count];
util::bip_buffer m_buffer; util::buffer m_buffer;
size_t m_start, m_end;
uint64_t m_count;
wait_queue m_waiting;
util::spinlock m_lock; util::spinlock m_lock;
static logger *s_log; static logger *s_log;
static const char *s_area_names[areas_count+1];
static const char *s_level_names[static_cast<unsigned>(level::max)];
}; };
void spam (logs area, const char *fmt, ...); void spam (logs area, const char *fmt, ...);
@@ -119,8 +113,6 @@ void warn (logs area, const char *fmt, ...);
void error (logs area, const char *fmt, ...); void error (logs area, const char *fmt, ...);
void fatal (logs area, const char *fmt, ...); void fatal (logs area, const char *fmt, ...);
extern log::logger &g_logger;
} // namespace log } // namespace log
void logger_init(); extern log::logger &g_logger;

View File

@@ -3,26 +3,3 @@
namespace std { namespace std {
enum class __attribute__ ((__type_visibility("default"))) align_val_t : size_t { }; enum class __attribute__ ((__type_visibility("default"))) align_val_t : size_t { };
} }
// Implementation of memset and memcpy because we're not
// linking libc into the kernel
extern "C" {
void *
memset(void *s, uint8_t v, size_t n)
{
uint8_t *p = reinterpret_cast<uint8_t *>(s);
for (size_t i = 0; i < n; ++i) p[i] = v;
return s;
}
void *
memcpy(void *dest, const void *src, size_t n)
{
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
uint8_t *d = reinterpret_cast<uint8_t *>(dest);
for (size_t i = 0; i < n; ++i) d[i] = s[i];
return d;
}
}

View File

@@ -88,3 +88,6 @@ constexpr uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + f
void initialize(bootproto::args &args); void initialize(bootproto::args &args);
} // namespace mem } // namespace mem
static constexpr bool __debug_heap_allocation = false;

View File

@@ -1,11 +1,8 @@
#include <new>
#include <utility>
#include <arch/memory.h> #include <arch/memory.h>
#include <bootproto/kernel.h> #include <bootproto/kernel.h>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "assert.h" #include "kassert.h"
#include "capabilities.h" #include "capabilities.h"
#include "device_manager.h" #include "device_manager.h"
#include "frame_allocator.h" #include "frame_allocator.h"
@@ -40,6 +37,9 @@ frame_allocator &g_frame_allocator = __g_frame_allocator_storage.value;
static util::no_construct<obj::vm_area_untracked> __g_kernel_heap_area_storage; static util::no_construct<obj::vm_area_untracked> __g_kernel_heap_area_storage;
obj::vm_area_untracked &g_kernel_heap_area = __g_kernel_heap_area_storage.value; obj::vm_area_untracked &g_kernel_heap_area = __g_kernel_heap_area_storage.value;
static util::no_construct<obj::vm_area_ring> __g_kernel_log_area_storage;
obj::vm_area_ring &g_kernel_log_area = __g_kernel_log_area_storage.value;
static util::no_construct<obj::vm_area_untracked> __g_kernel_heapmap_area_storage; static util::no_construct<obj::vm_area_untracked> __g_kernel_heapmap_area_storage;
obj::vm_area_untracked &g_kernel_heapmap_area = __g_kernel_heapmap_area_storage.value; obj::vm_area_untracked &g_kernel_heapmap_area = __g_kernel_heapmap_area_storage.value;
@@ -78,7 +78,7 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
page_table *kpml4 = static_cast<page_table*>(kargs.pml4); page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
// Initialize the frame allocator
frame_block *blocks = reinterpret_cast<frame_block*>(mem::bitmap_offset); frame_block *blocks = reinterpret_cast<frame_block*>(mem::bitmap_offset);
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count}; new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
@@ -91,25 +91,36 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
reg = reg->next; reg = reg->next;
} }
// Initialize the kernel "process" and vm_space
obj::process *kp = obj::process::create_kernel_process(kpml4); obj::process *kp = obj::process::create_kernel_process(kpml4);
vm_space &vm = kp->space(); vm_space &vm = kp->space();
// Create the heap space and heap allocator
obj::vm_area *heap = new (&g_kernel_heap_area) obj::vm_area *heap = new (&g_kernel_heap_area)
obj::vm_area_untracked(mem::heap_size, vm_flags::write); obj::vm_area_untracked(mem::heap_size, vm_flags::write);
obj::vm_area *heap_map = new (&g_kernel_heapmap_area) obj::vm_area *heap_map = new (&g_kernel_heapmap_area)
obj::vm_area_untracked(mem::heapmap_size, vm_flags::write); obj::vm_area_untracked(mem::heapmap_size, vm_flags::write);
vm.add(mem::heap_offset, heap); vm.add(mem::heap_offset, heap, vm_flags::exact);
vm.add(mem::heapmap_offset, heap_map); vm.add(mem::heapmap_offset, heap_map, vm_flags::exact);
new (&g_kernel_heap) heap_allocator {mem::heap_offset, mem::heap_size, mem::heapmap_offset}; new (&g_kernel_heap) heap_allocator {mem::heap_offset, mem::heap_size, mem::heapmap_offset};
// Set up the log area and logger
size_t log_buffer_size = log::log_pages * arch::frame_size;
obj::vm_area *logs = new (&g_kernel_log_area)
obj::vm_area_ring(log_buffer_size, vm_flags::write);
vm.add(mem::logs_offset, logs, vm_flags::exact);
new (&g_logger) log::logger(
util::buffer::from(mem::logs_offset, log_buffer_size));
// Set up the capability tables
obj::vm_area *caps = new (&g_cap_table_area) obj::vm_area *caps = new (&g_cap_table_area)
obj::vm_area_untracked(mem::caps_size, vm_flags::write); obj::vm_area_untracked(mem::caps_size, vm_flags::write);
vm.add(mem::caps_offset, caps); vm.add(mem::caps_offset, caps, vm_flags::exact);
new (&g_cap_table) cap_table {mem::caps_offset}; new (&g_cap_table) cap_table {mem::caps_offset};
@@ -118,7 +129,7 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
mem::kernel_stack_pages, mem::kernel_stack_pages,
mem::stacks_size, mem::stacks_size,
vm_flags::write}; vm_flags::write};
vm.add(mem::stacks_offset, &g_kernel_stacks); vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flags::exact);
// Clean out any remaning bootloader page table entries // Clean out any remaning bootloader page table entries
for (unsigned i = 0; i < arch::kernel_root_index; ++i) for (unsigned i = 0; i < arch::kernel_root_index; ++i)
@@ -129,7 +140,7 @@ void
memory_initialize_post_ctors(bootproto::args &kargs) memory_initialize_post_ctors(bootproto::args &kargs)
{ {
vm_space &vm = vm_space::kernel_space(); vm_space &vm = vm_space::kernel_space();
vm.add(mem::buffers_offset, &g_kernel_buffers); vm.add(mem::buffers_offset, &g_kernel_buffers, vm_flags::exact);
g_frame_allocator.free( g_frame_allocator.free(
get_physical_page(kargs.page_tables.pointer), get_physical_page(kargs.page_tables.pointer),

View File

@@ -1,89 +0,0 @@
#include <string.h>
#include "assert.h"
#include "memory.h"
#include "objects/channel.h"
#include "objects/thread.h"
#include "objects/vm_area.h"
extern obj::vm_area_guarded g_kernel_buffers;
namespace obj {
constexpr size_t buffer_bytes = mem::kernel_buffer_pages * mem::frame_size;
channel::channel() :
m_len {0},
m_data {g_kernel_buffers.get_section()},
m_closed {false},
m_buffer {reinterpret_cast<uint8_t*>(m_data), buffer_bytes},
kobject {kobject::type::channel}
{
}
channel::~channel()
{
if (!closed()) close();
}
size_t
channel::enqueue(const util::buffer &data)
{
util::scoped_lock lock {m_close_lock};
if (closed()) return 0;
size_t len = data.count;
void *buffer = nullptr;
len = m_buffer.reserve(len, &buffer);
memcpy(buffer, data.pointer, len);
m_buffer.commit(len);
if (len) {
thread *t = m_queue.pop_next();
lock.release();
if (t) t->wake();
}
return len;
}
size_t
channel::dequeue(util::buffer buffer, bool block)
{
util::scoped_lock lock {m_close_lock};
if (closed()) return 0;
void *data = nullptr;
size_t avail = m_buffer.get_block(&data);
if (!avail && block) {
thread &cur = thread::current();
m_queue.add_thread(&cur);
lock.release();
cur.block();
lock.reacquire();
avail = m_buffer.get_block(&data);
}
size_t len = buffer.count > avail ? avail : buffer.count;
memcpy(buffer.pointer, data, len);
m_buffer.consume(len);
return len;
}
void
channel::close()
{
util::scoped_lock lock {m_close_lock};
m_queue.clear();
g_kernel_buffers.return_section(m_data);
m_closed = true;
}
} // namespace obj

View File

@@ -1,54 +0,0 @@
#pragma once
/// \file channel.h
/// Definition of channel objects and related functions
#include <j6/cap_flags.h>
#include <util/bip_buffer.h>
#include <util/counted.h>
#include <util/spinlock.h>
#include "objects/kobject.h"
#include "wait_queue.h"
namespace obj {
/// Channels are uni-directional means of sending data
class channel :
public kobject
{
public:
/// Capabilities on a newly constructed channel handle
static constexpr j6_cap_t creation_caps = j6_cap_channel_all;
static constexpr kobject::type type = kobject::type::channel;
channel();
virtual ~channel();
/// Put a message into the channel
/// \arg data Buffer of data to write
/// \returns The number of bytes successfully written
size_t enqueue(const util::buffer &data);
/// Get a message from the channel, copied into a provided buffer
/// \arg buffer The buffer to copy data into
/// \arg block If true, block the calling thread until there is data
/// \returns The number of bytes copied into the provided buffer
size_t dequeue(util::buffer buffer, bool block = false);
/// Mark this channel as closed, all future calls to enqueue or
/// dequeue messages will fail with j6_status_closed.
void close();
/// Check if this channel has been closed.
inline bool closed() const { return m_closed; }
private:
size_t m_len;
uintptr_t m_data;
bool m_closed;
util::bip_buffer m_buffer;
util::spinlock m_close_lock;
wait_queue m_queue;
};
} // namespace obj

View File

@@ -28,22 +28,15 @@ event::wait()
// Wait for event::signal() to wake us with a value // Wait for event::signal() to wake us with a value
thread &current = thread::current(); thread &current = thread::current();
m_queue.add_thread(&current); m_queue.add_thread(&current);
return current.block(); current.block();
return read();
} }
void void
event::wake_observer() event::wake_observer()
{ {
util::scoped_lock lock {m_queue.get_lock()}; thread *t = m_queue.pop_next();
thread *t = m_queue.get_next_unlocked(); if (t) t->wake();
if (!t) return;
uint64_t value = read();
if (value) {
m_queue.pop_next_unlocked();
lock.release();
t->wake(value);
}
} }
} // namespace obj } // namespace obj

View File

@@ -1,7 +1,8 @@
#include <j6/errors.h> #include <j6/errors.h>
#include <j6/types.h> #include <j6/types.h>
#include "assert.h" #include "kassert.h"
#include "logger.h"
#include "objects/kobject.h" #include "objects/kobject.h"
#include "objects/thread.h" #include "objects/thread.h"
@@ -14,6 +15,13 @@ static uint32_t next_oids [types_count] = { 0 };
static_assert(types_count <= (1 << kobject::koid_type_bits), static_assert(types_count <= (1 << kobject::koid_type_bits),
"kobject::koid_type_bits cannot represent all kobject types"); "kobject::koid_type_bits cannot represent all kobject types");
static const char *type_names[] = {
#define OBJECT_TYPE( name ) #name ,
#include <j6/tables/object_types.inc>
#undef OBJECT_TYPE
nullptr
};
static uint32_t static uint32_t
oid_generate(kobject::type t) oid_generate(kobject::type t)
{ {
@@ -26,13 +34,25 @@ kobject::kobject(type t) :
m_handle_count {0}, m_handle_count {0},
m_type {t}, m_type {t},
m_obj_id {oid_generate(t)} m_obj_id {oid_generate(t)}
{} {
log::spam(logs::objs, "%s[%02lx] created @ 0x%lx", type_name(m_type), m_obj_id, this);
}
kobject::~kobject() {} kobject::~kobject()
{
log::spam(logs::objs, "%s[%02lx] deleted", type_name(m_type), m_obj_id);
}
const char *
kobject::type_name(type t)
{
return type_names[static_cast<int>(t)];
}
void void
kobject::on_no_handles() kobject::on_no_handles()
{ {
log::verbose(logs::objs, "Deleting %s[%02lx] on no handles", type_name(m_type), m_obj_id);
delete this; delete this;
} }

View File

@@ -16,7 +16,7 @@ public:
/// Types of kernel objects. /// Types of kernel objects.
enum class type : uint8_t enum class type : uint8_t
{ {
#define OBJECT_TYPE( name, val ) name = val, #define OBJECT_TYPE( name ) name ,
#include <j6/tables/object_types.inc> #include <j6/tables/object_types.inc>
#undef OBJECT_TYPE #undef OBJECT_TYPE
@@ -38,6 +38,8 @@ public:
return static_cast<type>((koid >> koid_type_shift) & koid_type_mask); return static_cast<type>((koid >> koid_type_shift) & koid_type_mask);
} }
static const char * type_name(type t);
/// Get this object's type /// Get this object's type
inline type get_type() const { return m_type; } inline type get_type() const { return m_type; }

View File

@@ -1,4 +1,6 @@
#include <util/basic_types.h>
#include <util/counted.h> #include <util/counted.h>
#include <j6/memutils.h>
#include "objects/mailbox.h" #include "objects/mailbox.h"
#include "objects/thread.h" #include "objects/thread.h"
@@ -26,6 +28,10 @@ mailbox::close()
m_callers.clear(j6_status_closed); m_callers.clear(j6_status_closed);
m_responders.clear(j6_status_closed); m_responders.clear(j6_status_closed);
util::scoped_lock lock {m_reply_lock};
for (auto &waiting : m_reply_map)
waiting.thread->wake(j6_status_closed);
} }
j6_status_t j6_status_t
@@ -45,7 +51,7 @@ mailbox::call()
} }
j6_status_t j6_status_t
mailbox::receive(thread::message_data &data, reply_tag_t &reply_tag, bool block) mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
{ {
if (closed()) if (closed())
return j6_status_closed; return j6_status_closed;
@@ -77,7 +83,7 @@ mailbox::receive(thread::message_data &data, reply_tag_t &reply_tag, bool block)
} }
j6_status_t j6_status_t
mailbox::reply(reply_tag_t reply_tag, const thread::message_data &data) mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
{ {
if (closed()) if (closed())
return j6_status_closed; return j6_status_closed;
@@ -91,7 +97,7 @@ mailbox::reply(reply_tag_t reply_tag, const thread::message_data &data)
m_reply_map.erase(reply_tag); m_reply_map.erase(reply_tag);
lock.release(); lock.release();
caller->get_message_data() = data; caller->set_message_data(util::move(data));
caller->wake(j6_status_ok); caller->wake(j6_status_ok);
return j6_status_ok; return j6_status_ok;
} }

View File

@@ -8,10 +8,9 @@
#include <util/spinlock.h> #include <util/spinlock.h>
#include "heap_allocator.h" #include "heap_allocator.h"
#include "ipc_message.h"
#include "memory.h" #include "memory.h"
#include "objects/kobject.h" #include "objects/kobject.h"
#include "objects/thread.h"
#include "slab_allocated.h"
#include "wait_queue.h" #include "wait_queue.h"
namespace obj { namespace obj {
@@ -23,6 +22,7 @@ class mailbox :
public kobject public kobject
{ {
public: public:
using reply_tag_t = uint64_t; using reply_tag_t = uint64_t;
/// Capabilities on a newly constructed mailbox handle /// Capabilities on a newly constructed mailbox handle
@@ -43,23 +43,23 @@ public:
/// Send a message to a thread waiting to receive on this mailbox, and block the /// Send a message to a thread waiting to receive on this mailbox, and block the
/// current thread awaiting a response. The message contents should be in the calling /// current thread awaiting a response. The message contents should be in the calling
/// thread's message_data. /// thread's message data.
/// \returns j6_status_ok if a reply was received /// \returns j6_status_ok if a reply was received
j6_status_t call(); j6_status_t call();
/// Receive the next available message, optionally blocking if no messages are available. /// Receive the next available message, optionally blocking if no messages are available.
/// \arg data [out] a thread::message_data structure to fill /// \arg data [out] an ipc::message structure to fill
/// \arg reply_tag [out] the reply_tag to use when replying to this message /// \arg reply_tag [out] the reply_tag to use when replying to this message
/// \arg block True if this call should block when no messages are available. /// \arg block True if this call should block when no messages are available.
/// \returns j6_status_ok if a message was received /// \returns j6_status_ok if a message was received
j6_status_t receive(thread::message_data &data, reply_tag_t &reply_tag, bool block); j6_status_t receive(ipc::message &data, reply_tag_t &reply_tag, bool block);
/// Find a given pending message to be responded to. Returns a replyer object, which will /// Find a given pending message to be responded to. Returns a replyer object, which will
/// wake the calling thread upon destruction. /// wake the calling read upon destruction.
/// \arg reply_tag The reply tag in the original message /// \arg reply_tag The reply tag in the original message
/// \arg data Message data to pass on to the caller /// \arg data Message data to pass on to the caller
/// \returns j6_status_ok if the reply was successfully sent /// \returns j6_status_ok if the reply was successfully sent
j6_status_t reply(reply_tag_t reply_tag, const thread::message_data &data); j6_status_t reply(reply_tag_t reply_tag, ipc::message &&data);
private: private:
wait_queue m_callers; wait_queue m_callers;

View File

@@ -1,8 +1,6 @@
#include <new>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "assert.h" #include "kassert.h"
#include "capabilities.h" #include "capabilities.h"
#include "cpu.h" #include "cpu.h"
#include "objects/process.h" #include "objects/process.h"
@@ -24,8 +22,6 @@ process::process() :
kobject {kobject::type::process}, kobject {kobject::type::process},
m_state {state::running} m_state {state::running}
{ {
m_self_handle = g_cap_table.create(this, process::self_caps);
add_handle(m_self_handle);
} }
// The "kernel process"-only constructor // The "kernel process"-only constructor
@@ -52,75 +48,80 @@ process::create_kernel_process(page_table *pml4)
void void
process::exit(int32_t code) process::exit(int32_t code)
{ {
// TODO: make this thread-safe if (m_state == state::exited)
return;
m_state = state::exited; m_state = state::exited;
m_return_code = code; m_return_code = code;
thread &current = thread::current();
util::scoped_lock lock {m_threads_lock};
for (auto *thread : m_threads) { for (auto *thread : m_threads) {
if (thread != &current) {
thread->exit(); thread->exit();
} }
if (this == current_cpu().process)
scheduler::get().schedule();
}
void
process::update()
{
kassert(m_threads.count() > 0, "process::update with zero threads!");
size_t i = 0;
while (i < m_threads.count()) {
thread *th = m_threads[i];
if (th->has_state(thread::state::exited)) {
m_threads.remove_swap_at(i);
continue;
}
i++;
} }
if (m_threads.count() == 0) lock.release();
exit(-1); if (&current.parent() == this)
current.exit();
} }
thread * thread *
process::create_thread(uintptr_t rsp3, uint8_t priority) process::create_thread(uintptr_t rsp3, uint8_t priority)
{ {
util::scoped_lock lock {m_threads_lock};
if (m_state == state::exited)
return nullptr;
if (priority == default_priority) if (priority == default_priority)
priority = scheduler::default_priority; priority = scheduler::default_priority;
thread *th = new thread(*this, priority); thread *th = new thread(*this, priority);
kassert(th, "Failed to create thread!"); kassert(th, "Failed to create thread!");
th->handle_retain();
if (rsp3) if (rsp3)
th->tcb()->rsp3 = rsp3; th->tcb()->rsp3 = rsp3;
if (this != &g_kernel_process)
th->init_xsave_area();
m_threads.append(th); m_threads.append(th);
scheduler::get().add_thread(th->tcb()); scheduler::get().add_thread(th->tcb());
return th; return th;
} }
bool void
process::thread_exited(thread *th) process::thread_exited(thread *th)
{ {
kassert(&th->m_parent == this, "Process got thread_exited for non-child!"); kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
if (m_state != state::exited) {
// if we're already going away, just release
// the thread's handle and skip all this
util::scoped_lock lock {m_threads_lock};
m_threads.remove_swap(th); m_threads.remove_swap(th);
remove_handle(th->self_handle());
delete th;
// TODO: delete the thread's stack VMA // TODO: delete the thread's stack VMA
if (m_threads.empty()) {
if (m_threads.count() == 0) { lock.release();
exit(-1); exit(-1);
return true; }
} }
return false; th->handle_release();
} }
void void
process::add_handle(j6_handle_t handle) process::add_handle(j6_handle_t handle)
{ {
// Passing the invalid handle is fine, just don't add anything
if (handle == j6_handle_invalid)
return;
capability *c = g_cap_table.retain(handle); capability *c = g_cap_table.retain(handle);
kassert(c, "Trying to add a non-existant handle to a process!"); kassert(c, "Trying to add a non-existant handle to a process!");

View File

@@ -20,9 +20,6 @@ public:
/// Capabilities on a newly constructed process handle /// Capabilities on a newly constructed process handle
static constexpr j6_cap_t creation_caps = j6_cap_process_all; static constexpr j6_cap_t creation_caps = j6_cap_process_all;
/// Capabilities on a process to itself
static constexpr j6_cap_t self_caps = j6_cap_process_all;
/// Top of memory area where thread stacks are allocated /// Top of memory area where thread stacks are allocated
static constexpr uintptr_t stacks_top = 0x0000800000000000; static constexpr uintptr_t stacks_top = 0x0000800000000000;
@@ -47,9 +44,6 @@ public:
/// \arg code The return code to exit with. /// \arg code The return code to exit with.
void exit(int32_t code); void exit(int32_t code);
/// Update internal bookkeeping about threads.
void update();
/// Get the process' virtual memory space /// Get the process' virtual memory space
vm_space & space() { return m_space; } vm_space & space() { return m_space; }
@@ -81,11 +75,7 @@ public:
/// Inform the process of an exited thread /// Inform the process of an exited thread
/// \args th The thread which has exited /// \args th The thread which has exited
/// \returns True if this thread ending has ended the process void thread_exited(thread *th);
bool thread_exited(thread *th);
/// Get the handle for this process to refer to itself
inline j6_handle_t self_handle() const { return m_self_handle; }
/// Get the process object that owns kernel threads and the /// Get the process object that owns kernel threads and the
/// kernel address space /// kernel address space
@@ -105,12 +95,12 @@ private:
// This constructor is called by create_kernel_process // This constructor is called by create_kernel_process
process(page_table *kpml4); process(page_table *kpml4);
j6_handle_t m_self_handle;
int32_t m_return_code; int32_t m_return_code;
vm_space m_space; vm_space m_space;
util::vector<thread*> m_threads; util::vector<thread*> m_threads;
util::spinlock m_threads_lock;
util::node_set<j6_handle_t, j6_handle_invalid, heap_allocated> m_handles; util::node_set<j6_handle_t, j6_handle_invalid, heap_allocated> m_handles;
util::spinlock m_handles_lock; util::spinlock m_handles_lock;

View File

@@ -1,5 +1,7 @@
#include <util/basic_types.h>
#include <util/pointers.h> #include <util/pointers.h>
#include "kassert.h"
#include "capabilities.h" #include "capabilities.h"
#include "cpu.h" #include "cpu.h"
#include "logger.h" #include "logger.h"
@@ -8,8 +10,9 @@
#include "objects/process.h" #include "objects/process.h"
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "scheduler.h" #include "scheduler.h"
#include "xsave.h"
extern "C" void kernel_to_user_trampoline(); extern "C" void initialize_user_cpu();
extern obj::vm_area_guarded &g_kernel_stacks; extern obj::vm_area_guarded &g_kernel_stacks;
@@ -22,6 +25,7 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
m_wake_value {0}, m_wake_value {0},
m_wake_timeout {0} m_wake_timeout {0}
{ {
parent.handle_retain();
parent.space().initialize_tcb(m_tcb); parent.space().initialize_tcb(m_tcb);
m_tcb.priority = pri; m_tcb.priority = pri;
m_tcb.thread = this; m_tcb.thread = this;
@@ -32,13 +36,24 @@ thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
m_tcb.rsp0 = rsp0; m_tcb.rsp0 = rsp0;
m_creator = current_cpu().thread; m_creator = current_cpu().thread;
m_self_handle = g_cap_table.create(this, thread::parent_caps);
parent.add_handle(m_self_handle); asm volatile ( "stmxcsr %0" : "=m"(m_mxcsr) );
m_mxcsr
.clear(mxcsr::IE)
.clear(mxcsr::DE)
.clear(mxcsr::ZE)
.clear(mxcsr::OE)
.clear(mxcsr::UE)
.clear(mxcsr::PE);
} }
thread::~thread() thread::~thread()
{ {
if (m_tcb.xsave)
delete [] reinterpret_cast<uint8_t*>(m_tcb.xsave);
g_kernel_stacks.return_section(m_tcb.kernel_stack); g_kernel_stacks.return_section(m_tcb.kernel_stack);
m_parent.handle_release();
} }
thread & thread::current() { return *current_cpu().thread; } thread & thread::current() { return *current_cpu().thread; }
@@ -52,6 +67,18 @@ thread::block()
return m_wake_value; return m_wake_value;
} }
uint64_t
thread::block(util::scoped_lock &lock)
{
kassert(current_cpu().thread == this,
"unlocking block() called on non-current thread");
clear_state(state::ready);
lock.release();
scheduler::get().schedule();
return m_wake_value;
}
j6_status_t j6_status_t
thread::join() thread::join()
{ {
@@ -84,11 +111,15 @@ thread::wake_only()
set_state(state::ready); set_state(state::ready);
} }
void thread::set_message_data(ipc::message &&md) { m_message = util::move(md); }
ipc::message && thread::get_message_data() { return util::move(m_message); }
void void
thread::exit() thread::exit()
{ {
m_wake_timeout = 0; m_wake_timeout = 0;
set_state(state::exited); set_state(state::exited);
m_parent.thread_exited(this);
m_join_queue.clear(); m_join_queue.clear();
block(); block();
} }
@@ -113,7 +144,7 @@ thread::add_thunk_kernel(uintptr_t rip)
} }
void void
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags) thread::add_thunk_user(uintptr_t rip3, uint64_t arg0, uint64_t arg1, uintptr_t rip0, uint64_t flags)
{ {
// This sets up the stack to: // This sets up the stack to:
// a) come out of task_switch and return to rip0 (default is the // a) come out of task_switch and return to rip0 (default is the
@@ -126,22 +157,33 @@ thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
flags |= 0x200; flags |= 0x200;
m_tcb.rflags3 = flags; m_tcb.rflags3 = flags;
m_tcb.rsp -= sizeof(uintptr_t) * 7; m_tcb.rsp -= sizeof(uintptr_t) * 9;
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp); uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
stack[6] = rip3; // return rip in rcx stack[8] = rip3; // return rip in rcx
stack[5] = m_tcb.rsp3; // rbp stack[7] = m_tcb.rsp3; // rbp
stack[4] = 0xbbbbbbbb; // rbx stack[6] = 0xbbbbbbbb; // rbx
stack[3] = 0x12121212; // r12 stack[5] = 0x12121212; // r12
stack[2] = 0x13131313; // r13 stack[4] = 0x13131313; // r13
stack[1] = 0x14141414; // r14 stack[3] = 0x14141414; // r14
stack[0] = 0x15151515; // r15 stack[2] = 0x15151515; // r15
stack[1] = arg1; // rsi
stack[0] = arg0; // rdi
static const uintptr_t trampoline = static const uintptr_t trampoline =
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline); reinterpret_cast<uintptr_t>(initialize_user_cpu);
add_thunk_kernel(rip0 ? rip0 : trampoline); add_thunk_kernel(rip0 ? rip0 : trampoline);
} }
void
thread::init_xsave_area()
{
void *xsave_area = new uint8_t [xsave_size];
memset(xsave_area, 0, xsave_size);
m_tcb.xsave = reinterpret_cast<uintptr_t>(xsave_area);
}
void void
thread::setup_kernel_stack() thread::setup_kernel_stack()
{ {
@@ -149,8 +191,8 @@ thread::setup_kernel_stack()
using mem::kernel_stack_pages; using mem::kernel_stack_pages;
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size; static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
constexpr unsigned null_frame_entries = 2; static constexpr unsigned null_frame_entries = 2;
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); static constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
uintptr_t stack_addr = g_kernel_stacks.get_section(); uintptr_t stack_addr = g_kernel_stacks.get_section();
uintptr_t stack_end = stack_addr + stack_bytes; uintptr_t stack_end = stack_addr + stack_bytes;
@@ -168,12 +210,19 @@ thread::setup_kernel_stack()
} }
thread * thread *
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0) thread::create_idle_thread(process &kernel, uintptr_t rsp0)
{ {
thread *idle = new thread(kernel, pri, rsp0); thread *idle = new thread(kernel, scheduler::idle_priority, rsp0);
idle->set_state(state::constant); idle->set_state(state::constant);
idle->set_state(state::ready); idle->set_state(state::ready);
return idle; return idle;
} }
} // namespace obj } // namespace obj
uint32_t
__current_thread_id()
{
cpu_data &cpu = current_cpu();
return cpu.thread ? cpu.thread->obj_id() : -1u;
}

View File

@@ -7,10 +7,11 @@
#include <util/linked_list.h> #include <util/linked_list.h>
#include <util/spinlock.h> #include <util/spinlock.h>
#include "cpu.h"
#include "ipc_message.h"
#include "objects/kobject.h" #include "objects/kobject.h"
#include "wait_queue.h" #include "wait_queue.h"
struct cpu_data;
struct page_table; struct page_table;
namespace obj { namespace obj {
@@ -26,6 +27,7 @@ struct TCB
uintptr_t rsp3; uintptr_t rsp3;
uintptr_t rflags3; uintptr_t rflags3;
uintptr_t pml4; uintptr_t pml4;
uintptr_t xsave;
// End of area used by asembly // End of area used by asembly
obj::thread* thread; obj::thread* thread;
@@ -57,9 +59,6 @@ public:
/// Capabilities on a newly constructed thread handle /// Capabilities on a newly constructed thread handle
static constexpr j6_cap_t creation_caps = j6_cap_thread_all; static constexpr j6_cap_t creation_caps = j6_cap_thread_all;
/// Capabilities the parent process gets on new thread handles
static constexpr j6_cap_t parent_caps = j6_cap_thread_all;
static constexpr kobject::type type = kobject::type::thread; static constexpr kobject::type type = kobject::type::thread;
enum class state : uint8_t { enum class state : uint8_t {
@@ -99,6 +98,11 @@ public:
/// \returns The value passed to wake() /// \returns The value passed to wake()
uint64_t block(); uint64_t block();
/// Block this thread, waiting for a value
/// \arg held A held lock to unlock when blocking
/// \returns The value passed to wake()
uint64_t block(util::scoped_lock &held);
/// Block the calling thread until this thread exits /// Block the calling thread until this thread exits
j6_status_t join(); j6_status_t join();
@@ -118,13 +122,8 @@ public:
/// \returns The clock time at which to wake. 0 for no timeout. /// \returns The clock time at which to wake. 0 for no timeout.
inline uint64_t wake_timeout() const { return m_wake_timeout; } inline uint64_t wake_timeout() const { return m_wake_timeout; }
struct message_data void set_message_data(ipc::message &&md);
{ ipc::message && get_message_data();
uint64_t tag, subtag;
j6_handle_t handle;
};
message_data & get_message_data() { return m_message_data; }
inline bool has_state(state s) const { inline bool has_state(state s) const {
return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) & return __atomic_load_n(reinterpret_cast<const uint8_t*>(&m_state), __ATOMIC_ACQUIRE) &
@@ -152,29 +151,28 @@ public:
/// Add a stack header that returns to the given address in user space /// Add a stack header that returns to the given address in user space
/// via a function in kernel space. /// via a function in kernel space.
/// \arg rip3 The user space address to return to /// \arg rip3 The user space address to return to
/// \arg arg0 An argument passed to the userspace function
/// \arg arg1 An argument passed to the userspace function
/// \arg rip0 The kernel function to pass through, optional /// \arg rip0 The kernel function to pass through, optional
/// \arg flags Extra RFLAGS values to set, optional /// \arg flags Extra RFLAGS values to set, optional
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0); void add_thunk_user(
uintptr_t rip3,
/// Get the handle representing this thread to its process uint64_t arg0 = 0,
j6_handle_t self_handle() const { return m_self_handle; } uint64_t arg1 = 0,
uintptr_t rip0 = 0,
uint64_t flags = 0);
/// Create the kernel idle thread /// Create the kernel idle thread
/// \arg kernel The process object that owns kernel tasks /// \arg kernel The process object that owns kernel tasks
/// \arg pri The idle thread priority value
/// \arg rsp The existing stack for the idle thread /// \arg rsp The existing stack for the idle thread
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp); static thread * create_idle_thread(process &kernel, uintptr_t rsp);
protected:
/// Don't delete a thread on no handles, the scheduler takes
/// care of that.
virtual void on_no_handles() override {}
private: private:
thread() = delete; thread() = delete;
thread(const thread &other) = delete; thread(const thread &other) = delete;
thread(const thread &&other) = delete; thread(const thread &&other) = delete;
friend class process; friend class process;
friend void ::cpu_initialize_thread_state();
/// Constructor. Used when a kernel stack already exists. /// Constructor. Used when a kernel stack already exists.
/// \arg parent The process which owns this thread /// \arg parent The process which owns this thread
@@ -182,6 +180,9 @@ private:
/// \arg rsp0 The existing kernel stack rsp, 0 for none /// \arg rsp0 The existing kernel stack rsp, 0 for none
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0); thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
/// Set up the XSAVE saved processor state area for this thread
void init_xsave_area();
/// Set up a new empty kernel stack for this thread. /// Set up a new empty kernel stack for this thread.
void setup_kernel_stack(); void setup_kernel_stack();
@@ -191,13 +192,16 @@ private:
thread *m_creator; thread *m_creator;
state m_state; state m_state;
util::bitset32 m_mxcsr;
uint64_t m_wake_value; uint64_t m_wake_value;
uint64_t m_wake_timeout; uint64_t m_wake_timeout;
message_data m_message_data;
ipc::message m_message;
wait_queue m_join_queue; wait_queue m_join_queue;
j6_handle_t m_self_handle;
}; };
} // namespace obj } // namespace obj
extern "C" uint32_t __current_thread_id();

View File

@@ -1,9 +1,8 @@
#include "assert.h" #include "kassert.h"
#include "frame_allocator.h" #include "frame_allocator.h"
#include "memory.h" #include "memory.h"
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "page_tree.h"
#include "vm_space.h" #include "vm_space.h"
namespace obj { namespace obj {
@@ -86,7 +85,7 @@ vm_area_fixed::resize(size_t size)
} }
bool bool
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys) vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
{ {
if (offset > m_size) if (offset > m_size)
return false; return false;
@@ -106,11 +105,16 @@ vm_area_untracked::~vm_area_untracked()
} }
bool bool
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys) vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
{ {
if (offset > m_size) if (offset > m_size)
return false; return false;
if (!alloc) {
phys = 0;
return true;
}
return frame_allocator::get().allocate(1, &phys); return frame_allocator::get().allocate(1, &phys);
} }
@@ -136,9 +140,17 @@ vm_area_open::~vm_area_open()
} }
bool bool
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys) vm_area_open::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
{ {
if (alloc)
return page_tree::find_or_add(m_mapped, offset, phys); return page_tree::find_or_add(m_mapped, offset, phys);
else
return page_tree::find(m_mapped, offset, &phys);
}
void
vm_area_open::add_existing(uintptr_t offset, uintptr_t phys)
{
} }
@@ -166,7 +178,7 @@ vm_area_guarded::return_section(uintptr_t addr)
} }
bool bool
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys) vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
{ {
if (offset >= m_stacks.end()) if (offset >= m_stacks.end())
return false; return false;
@@ -176,7 +188,23 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
if ((offset >> 12) % m_pages == 0) if ((offset >> 12) % m_pages == 0)
return false; return false;
return vm_area_open::get_page(offset, phys); return vm_area_open::get_page(offset, phys, alloc);
}
vm_area_ring::vm_area_ring(size_t size, vm_flags flags) :
vm_area_open {size * 2, flags},
m_bufsize {size}
{
}
vm_area_ring::~vm_area_ring() {}
bool
vm_area_ring::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
{
if (offset > m_bufsize)
offset -= m_bufsize;
return vm_area_open::get_page(offset, phys, alloc);
} }
} // namespace obj } // namespace obj

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