21 Commits
v0.8.0 ... main

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

https://stackoverflow.com/a/28384866

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

Good riddance to this damned bug.
2024-08-11 12:40:13 -07:00
Justin C. Miller
e345cdd1a7 [util] Add util::bitset::set(i, val)
Allow for easier callsite code instead of having to switch calling
set(i) vs clear(i) - now set(i, boolean) is an option.
2024-08-10 23:33:26 -07:00
Justin C. Miller
fca570a163 [scripts] Make j6libc.py into the codegen package
j6libc.py was initially made for libc to generate stdint.h, but it
gained a few things that aren't libc-specific. Move it to a package and
split the int-types-specific code into codegen.int_types.
2024-08-10 23:29:21 -07:00
Justin C. Miller
ff64d1989f [docs] Update docs: kernel mem, process init
Updated documentation: Added documentation on the kernel address space
layout, process initialization, and rebuilt the syscall docs.
2024-08-10 23:11:14 -07:00
Justin C. Miller
d3f5db2479 [crt0] Actually pass argc, argv, envp to main()s
With the new SysV style process init args, it's a bit easier to finally
parse out argc, argv, and envp from the stack and pass them on to main
functions.
2024-08-10 17:37:34 -07:00
Justin C. Miller
fa587060f1 [libj6] Change to a more SysV style process init args
Pull out the old linked list of args structures in favor of doing things
the SysV ABI-specified way.
2024-08-09 19:14:44 -07:00
Justin C. Miller
b137c75eb2 [kernel] Use box-drawing characters in debugcon
Slight change to debugcon display format that replaces | with the
box-drawing character equivalent to seperate area/level from log
messages.
2024-08-08 19:46:32 -07:00
Justin C. Miller
05c1361283 [libc] Implement setjmp/longjmp
Add a very simple setjmp/longjmp implementation. No destructors or other
cleanup is handled.
2024-08-08 19:31:20 -07:00
Justin C. Miller
c6835dad70 [tools] Update telnet to nc in qemu.sh
The telnet utility seems to have stopped existing on my wsl
installation. That's fine, netcat is the more correct tool here anyways.
2024-08-04 12:14:42 -07:00
Justin C. Miller
e7fa1dde97 [ld.so] Mark main module to not run ctors
Forgot to include this in the change for ld.so to run global ctors for
all modules. The main module itself will have its ctors run last, by
crt0, so mark it as not needing ctors in ld.so.
2024-04-30 22:24:34 -07:00
Justin C. Miller
eb62588b79 [6s] Allow 6s to know about filesystems
Added a j6_proto_vfs_tag/_get_tag pair of messages to the VFS protocol
to allow filesystems to label themselves, and gave 6s the concept of
current fs and current working directory.
2024-04-30 22:23:04 -07:00
Justin C. Miller
29332cbd45 [libc] Update errno and strto* files for compatibility
Most of the strto* functions are only stubbed out, but they're there for
things that link to them but don't call them.
2024-04-30 22:20:41 -07:00
Justin C. Miller
172eb70551 [6s] Break out builtin commands into a list of structs
The 6s builtin commands are now in a separate file, with a list of name,
description, and function pointers.
2024-04-29 01:11:15 -07:00
Justin C. Miller
7322df98f5 [ld.so] Call all image global ctors, not just libc
With the move to dynamic executables, crt0's _start was only ever
calling libc's __init_libc, which only ran libc's init_array list. Now
make crt0 itself (which is statically linked into every executable) call
it's own init_array list and have ld.so call every other image's ctor
lists.
2024-04-29 01:07:18 -07:00
99 changed files with 2146 additions and 654 deletions

View File

@@ -21,6 +21,7 @@ ccflags: [
cxxflags: [ cxxflags: [
"-fno-exceptions", "-fno-exceptions",
"-fno-rtti", "-fno-rtti",
"-isystem", "${source_root}/sysroot/include/c++/v1",
] ]
ldflags: [ ldflags: [

View File

@@ -21,6 +21,7 @@ ccflags: [
cxxflags: [ cxxflags: [
"-fno-exceptions", "-fno-exceptions",
"-fno-rtti", "-fno-rtti",
"-isystem", "${source_root}/sysroot/include/c++/v1",
] ]
ldflags: [ ldflags: [

View File

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

View File

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

View File

@@ -19,4 +19,5 @@ help:
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile %: Makefile
PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c syscall_interface.rst PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c syscall_interface.rst
PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c kernel_memory.rst
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

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

178
docs/kernel_memory.rst Normal file
View File

@@ -0,0 +1,178 @@
.. jsix syscall interface.
.. Automatically updated from the definition files using cog!
.. [[[cog code generation
.. from os.path import join
.. from memory import Layout, unit
..
.. layout = Layout(join(definitions_path, "memory_layout.yaml"))
.. l = max([len(r.name) for r in layout.regions])
.. ]]]
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
Kernel memory
=============
While jsix probably should eventually use KASLR to randomize its memory layout,
currently the layout is mostly fixed. (Kernel code locations are not consistent
but aren't explicitly randomized.)
.. [[[cog code generation
.. line_size = 128 * 1024**3 # Each line represents up to 32 GiB
.. max_lines = 32
.. totals = sum([r.size for r in layout.regions])
.. remain = unit((128 * 1024**4) - totals)
..
.. def split(val):
.. return f"0x {val >> 48:04x} {(val >> 32) & 0xffff:04x} {(val >> 16) & 0xffff:04x} {val & 0xffff:04x}"
..
.. cog.outl()
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
.. cog.outl(f"| | Address | Size | Use |")
.. cog.outl(f"+=+=============================+==========+=======================================+")
..
.. for region in layout.regions:
.. cog.outl(f"| | ``{split(region.start)}`` | {unit(region.size):>8} | {region.desc:37} |")
.. lines = min(max_lines, region.size // line_size)
.. for i in range(1, lines):
.. cog.outl(f"+-+ | | |")
.. cog.outl(f"| | | | |")
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
..
.. cog.outl(f"| | ... | | |")
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
.. cog.outl(f"| | ``0x ffff 0000 0000 0000`` | | Kernel code / headers |")
.. cog.outl(f"+-+-----------------------------+----------+---------------------------------------+")
.. cog.outl("")
.. cog.outl("")
.. cog.outl(f"Un-reserved virtual memory address space in the higher half: {remain}")
.. cog.outl("")
..
.. ]]]
+-+-----------------------------+----------+---------------------------------------+
| | Address | Size | Use |
+=+=============================+==========+=======================================+
| | ``0x ffff c000 0000 0000`` | 64 TiB | Linearly-mapped physical memory |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bf00 0000 0000`` | 1 TiB | Used/free page tracking bitmap |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff be00 0000 0000`` | 1 TiB | Per-page state tracking structures |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+ | | |
| | | | |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bdf8 0000 0000`` | 32 GiB | Kernel heap accounting structures |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bdf0 0000 0000`` | 32 GiB | Kernel heap |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bde8 0000 0000`` | 32 GiB | Capabilities accounting structures |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bde0 0000 0000`` | 32 GiB | Capabilities |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bdd0 0000 0000`` | 64 GiB | Kernel thread stacks |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bdc0 0000 0000`` | 64 GiB | Kernel buffers |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff bdbf 8000 0000`` | 2 GiB | Kernel logs circular buffer |
+-+-----------------------------+----------+---------------------------------------+
| | ... | | |
+-+-----------------------------+----------+---------------------------------------+
| | ``0x ffff 0000 0000 0000`` | | Kernel code / headers |
+-+-----------------------------+----------+---------------------------------------+
Un-reserved virtual memory address space in the higher half: 61 TiB
.. [[[end]]] (checksum: 8c336cc8151beba1a79c8d3b653f1109)
* :ref:`genindex`
* :ref:`search`

View File

@@ -0,0 +1,42 @@
.. jsix process initialization in userspace
Process Initialization
======================
jsix follows the `System V ABI`_ on the ``amd64`` architecture. All arguments
needed for program initialization are passed to the program's initial thread on
the stack.
Note that jsix adds a number of additional auxiliary vector entry types for
passing jsix-specific data to a program. The jsix-specific auxiliary vector type
codes (what the ABI document refers to as ``a_type``) start from ``0xf000``. See
the header file ``<j6/init.h>`` for more detail.
.. _System V ABI: https://gitlab.com/x86-psABIs/x86-64-ABI
The initial stack frame
-----------------------
============== ==================== ============ =======
Address Value Bytes Notes
============== ==================== ============ =======
``top`` Stack top (out of stack bounds)
``top`` - 16 0 16 Stack sentinel
\ ``envp`` string data ?
\ ``argv`` string data ?
\ ... ? Possible padding
\ 0, 0 (``AT_NULL``) 16 Aux vector sentinel
\ Aux vectors 16 * `m` ``AT_NULL``-terminated array of Aux vectors
\ 0 8 Environment sentinel
\ ``envp`` 8 * `n` 0-terminated array of environment
string pointers
\ 0 8 Args sentinel
\ ``argv`` 8 * ``argc`` Pointers to argument strings
``rsp`` ``argc`` 8 Number of elements in argv
============== ==================== ============ =======
* :ref:`genindex`
* :ref:`search`

View File

@@ -1,5 +1,5 @@
.. jsix syscall interface. .. jsix syscall interface.
.. Automatically update from the definition files using cog! .. Automatically updated from the definition files using cog!
.. [[[cog code generation .. [[[cog code generation
.. from textwrap import indent .. from textwrap import indent
@@ -14,11 +14,11 @@
.. ]]] .. ]]]
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e) .. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
The jsix syscall interface Syscall interface
========================== =================
The jsix kernel's syscall design is based around object handles. Object handles The jsix kernel's syscall design is based around object handles. Object handles
are also a collections capabilities, encoding certain rights over the object are also a collection of capabilities, encoding certain rights over the object
they reference. they reference.
Very few syscalls in jsix can be made without some handle, and most of them are Very few syscalls in jsix can be made without some handle, and most of them are
@@ -133,7 +133,7 @@ as shared memory channels, but more flexible.
:param self: Handle to the mailbox object :param self: Handle to the mailbox object
.. cpp:function:: j6_result_t j6_mailbox_call (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_in_len, j6_handle_t * handles, size_t * handles_count) .. cpp:function:: j6_result_t j6_mailbox_call (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_size, j6_handle_t * handles, size_t * handles_count, size_t handles_size)
Send a message to the reciever, and block until a response is Send a message to the reciever, and block until a response is
sent. Note that getting this response does not require the sent. Note that getting this response does not require the
@@ -144,10 +144,11 @@ as shared memory channels, but more flexible.
:param self: Handle to the mailbox object :param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented :param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented :param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input :param data_size: number of total bytes in data buffer
:param handles: *[optional, inout, handle, list]* Undocumented :param handles: *[optional, inout, handle, list]* Undocumented
:param handles_size: total size of handles buffer
.. cpp:function:: j6_result_t j6_mailbox_respond (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_in_len, j6_handle_t * handles, size_t * handles_count, uint64_t * reply_tag, uint64_t flags) .. cpp:function:: j6_result_t j6_mailbox_respond (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_size, j6_handle_t * handles, size_t * handles_count, size_t handles_size, uint64_t * reply_tag, uint64_t flags)
Respond to a message sent using call, and wait for another Respond to a message sent using call, and wait for another
message to arrive. Note that this does not require the send message to arrive. Note that this does not require the send
@@ -159,8 +160,9 @@ as shared memory channels, but more flexible.
:param self: Handle to the mailbox object :param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented :param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented :param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input :param data_size: number of total bytes in data buffer
:param handles: *[optional, inout, handle, list]* Undocumented :param handles: *[optional, inout, handle, list]* Undocumented
:param handles_size: total size of handles buffer
:param reply_tag: *[inout]* Undocumented :param reply_tag: *[inout]* Undocumented
:param flags: Undocumented :param flags: Undocumented
@@ -185,7 +187,7 @@ control over the threads, handles, and virtual memory space of that process.
:param self: Handle to the process object :param self: Handle to the process object
.. cpp:function:: j6_result_t j6_process_exit (int32_t result) .. cpp:function:: j6_result_t j6_process_exit (int64_t result)
Stop all threads and exit the current process Stop all threads and exit the current process
@@ -353,7 +355,7 @@ necessarily mean that it is mapped into that process' virtual memory space.
:param self: Handle to the vma object :param self: Handle to the vma object
:param size: *[inout]* New size for the VMA, or 0 to query the current size without changing :param size: *[inout]* New size for the VMA, or 0 to query the current size without changing
.. [[[end]]] (checksum: fe448e541c009a1bcf8bdc44f4760e32) .. [[[end]]] (checksum: cb17f54e443d1d3b85995870f3e8dbf2)
Non-object syscalls Non-object syscalls
------------------- -------------------
@@ -411,6 +413,12 @@ either do not require an object handle, or operate generically on handles.
:param clone: *[out]* The new handle :param clone: *[out]* The new handle
:param mask: The capability bitmask :param mask: The capability bitmask
.. cpp:function:: j6_result_t j6_handle_close (j6_handle_t hnd)
Close the handle to an object
:param hnd: *[handle]* The handle to close
.. cpp:function:: j6_result_t j6_futex_wait (const uint32_t * address, uint32_t current, uint64_t timeout) .. cpp:function:: j6_result_t j6_futex_wait (const uint32_t * address, uint32_t current, uint64_t timeout)
Block waiting on a futex Block waiting on a futex
@@ -432,5 +440,5 @@ either do not require an object handle, or operate generically on handles.
:param exit_code: Undocumented :param exit_code: Undocumented
.. [[[end]]] (checksum: b8b12e99a4a00c99b3859f05000a7bfd) .. [[[end]]] (checksum: 0b9d051972abcbb6de408f411331785f)

View File

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

View File

@@ -0,0 +1,21 @@
import cog
supported_architectures = {
"amd64": "__amd64__",
}
def arch_includes(header, root=""):
from pathlib import Path
root = Path(root)
header = Path(header)
prefix = "if"
for arch, define in supported_architectures.items():
path = root / "arch" / arch / header
cog.outl(f"#{prefix} defined({define})")
cog.outl(f"#include <{path}>")
prefix = "elif"
cog.outl("#else")
cog.outl('#error "Unsupported platform"')
cog.outl("#endif")

View File

@@ -1,18 +1,5 @@
import cog import cog
supported_architectures = {
"amd64": "__amd64__",
}
def arch_includes(header):
prefix = "if"
for arch, define in supported_architectures.items():
cog.outl(f"#{prefix} defined({define})")
cog.outl(f"#include <__j6libc/arch/{arch}/{header}>")
prefix = "elif"
cog.outl("#endif")
int_widths = (8, 16, 32, 64) int_widths = (8, 16, 32, 64)
int_mods = ("fast", "least") int_mods = ("fast", "least")

View File

@@ -1,6 +1,18 @@
def unit(size):
units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']
o = 0
while size >= 1024 and o < len(units):
o += 1
size = size >> 10
return f"{size} {units[o]}"
class Layout: class Layout:
from collections import namedtuple from collections import namedtuple
Region = namedtuple("Region", ("name", "start", "size", "shared")) Region = namedtuple("Region", ("name", "desc", "start", "size", "shared"))
sizes = {'G': 1024 ** 3, 'T': 1024 ** 4} sizes = {'G': 1024 ** 3, 'T': 1024 ** 4}
@@ -26,7 +38,7 @@ class Layout:
for r in data: for r in data:
size = Layout.get_size(r["size"]) size = Layout.get_size(r["size"])
addr -= size addr -= size
regions.append(Layout.Region(r["name"], addr, size, regions.append(Layout.Region(r["name"], r["desc"], addr, size,
r.get("shared", False))) r.get("shared", False)))
self.regions = tuple(regions) self.regions = tuple(regions)

View File

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

View File

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

View File

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

View File

@@ -21,6 +21,7 @@ constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + mem::linear_offset;
extern "C" { extern "C" {
void isr_handler(cpu_state*); void isr_handler(cpu_state*);
void irq_handler(cpu_state*); void irq_handler(cpu_state*);
void _reload_cr3();
} }
uint8_t uint8_t
@@ -187,6 +188,11 @@ isr_handler(cpu_state *regs)
scheduler::get().schedule(); scheduler::get().schedule();
break; break;
case isr::ipiShootdown:
// TODO: Real shootdown algorithm
_reload_cr3();
break;
default: default:
util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt); util::format({message, sizeof(message)}, "Unknown interrupt 0x%lx", regs->interrupt);
kassert(false, message, regs); kassert(false, message, regs);

View File

@@ -5,7 +5,7 @@ kernel = module("kernel",
basename = "jsix", basename = "jsix",
targets = [ "kernel" ], targets = [ "kernel" ],
description = "jsix kernel", description = "jsix kernel",
deps = [ "util", "cpu", "bootproto", "j6" ], deps = [ "util", "cpu", "bootproto", "j6", "acpi" ],
static = True, static = True,
ld_script = "kernel.ld", ld_script = "kernel.ld",
sources = [ sources = [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,79 @@
#include <acpi/acpi.h>
#include <acpi/tables.h>
#include <j6/syslog.hh>
namespace acpi {
system::system(const void* phys, const void *virt) :
m_offset { util::get_offset(phys, virt) },
m_root { reinterpret_cast<const rsdp2*>(virt) }
{}
system::iterator
system::begin() const
{
const xsdt *sdt =
acpi::check_get_table<xsdt>(m_root->xsdt_address);
if (!sdt)
return {nullptr, 0};
return {&sdt->headers[0], m_offset};
}
system::iterator
system::end() const
{
const xsdt *sdt =
acpi::check_get_table<xsdt>(m_root->xsdt_address);
if (!sdt)
return {nullptr, 0};
size_t nheaders = table_entries<xsdt>(sdt, sizeof(table_header*));
return {&sdt->headers[nheaders], m_offset};
}
#if 0
void
load_acpi(j6_handle_t sys, const bootproto::module *mod)
{
const bootproto::acpi *info = mod->data<bootproto::acpi>();
const util::const_buffer &region = info->region;
map_phys(sys, region.pointer, region.count);
const void *root_table = info->root;
if (!root_table) {
j6::syslog(j6::logs::srv, j6::log_level::error, "null ACPI root table pointer");
return;
}
const acpi::rsdp2 *acpi2 =
reinterpret_cast<const acpi::rsdp2 *>(root_table);
const auto *xsdt =
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
for (size_t i = 0; i < num_tables; ++i) {
const acpi::table_header *header = xsdt->headers[i];
if (!header->validate()) {
j6::syslog(j6::logs::srv, j6::log_level::error, "ACPI table at %lx failed validation", header);
continue;
}
switch (header->type) {
case acpi::mcfg::type_id:
load_mcfg(sys, header);
break;
default:
break;
}
}
}
#endif
} // namespace acpi

View File

@@ -0,0 +1,12 @@
# vim: ft=python
module("acpi",
kind = "lib",
deps = [ "util", "j6" ],
sources = [
"acpi.cpp",
],
public_headers = [
"acpi/acpi.h",
"acpi/tables.h",
])

View File

@@ -0,0 +1,53 @@
#pragma once
/// \file acpi.h
/// Routines for loading and parsing ACPI tables
#include <stddef.h>
#include <acpi/tables.h>
#include <util/pointers.h>
namespace acpi {
struct system
{
system(const void* phys, const void *virt);
/// Iterator for all tables in the system
struct iterator
{
iterator(const table_header *const *addr, ptrdiff_t off) : m_addr {addr}, m_off {off} {};
operator const table_header *() const { return m_addr ? offset(*m_addr) : nullptr; }
const table_header * operator*() const { return m_addr ? offset(*m_addr) : nullptr; }
const table_header * operator->() const { return m_addr ? offset(*m_addr) : nullptr; }
iterator & operator++() { increment(); return *this; }
iterator operator++(int) { iterator old = *this; increment(); return old; }
friend bool operator==(const iterator &a, const iterator &b) { return a.m_addr == b.m_addr; }
friend bool operator!=(const iterator &a, const iterator &b) { return a.m_addr != b.m_addr; }
private:
inline void increment() { if (m_addr) ++m_addr; }
table_header const * const * m_addr;
ptrdiff_t m_off;
inline const table_header * offset(const table_header * addr) const {
return util::offset_pointer(addr, m_off);
}
};
iterator begin() const;
iterator end() const;
private:
const xsdt * get_xsdt() {
return check_get_table<xsdt>(util::offset_pointer(m_root->xsdt_address, m_offset));
}
ptrdiff_t m_offset;
const rsdp2* m_root;
};
} // namespace acpi

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,6 +51,14 @@ public:
return *this; return *this;
} }
template <typename T>
__attribute__ ((always_inline))
inline bitset & set(T i, bool value) {
if (value) set(i);
else clear(i);
return *this;
}
template <typename T> template <typename T>
__attribute__ ((always_inline)) __attribute__ ((always_inline))
inline bitset & clear(T i) { inline bitset & clear(T i) {

View File

@@ -1,9 +1,12 @@
#pragma once #pragma once
#include <stddef.h>
#include <stdint.h>
namespace util { namespace util {
/// Reverse the order of bytes in a 32 bit integer /// Reverse the order of bytes in a 32 bit integer
constexpr uint32_t constexpr inline uint32_t
byteswap32(uint32_t x) { byteswap32(uint32_t x) {
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00) return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000); | ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
@@ -14,7 +17,7 @@ byteswap32(uint32_t x) {
/// \arg p The start of the memory region /// \arg p The start of the memory region
/// \arg len The number of bytes in the region /// \arg len The number of bytes in the region
/// \arg off An optional offset into the region /// \arg off An optional offset into the region
uint8_t checksum(const void *p, size_t len, size_t off = 0) { inline uint8_t checksum(const void *p, size_t len, size_t off = 0) {
uint8_t sum = 0; uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p); const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (size_t i = off; i < len; ++i) sum += c[i]; for (size_t i = off; i < len; ++i) sum += c[i];

View File

@@ -16,6 +16,15 @@ inline T* offset_pointer(T* input, ptrdiff_t offset) {
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset); return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
} }
/// Get the offset from ptr1 to ptr2.
/// \arg ptr1 The base pointer
/// \arg ptr2 The offset pointer
/// \returns Offset from pt1 to ptr2
template <typename T, typename U>
inline ptrdiff_t get_offset(T* ptr1, U* ptr2) {
return reinterpret_cast<const char*>(ptr2) - reinterpret_cast<const char*>(ptr1);
}
/// Return a pointer with the given bits masked out /// Return a pointer with the given bits masked out
/// \arg input The original pointer /// \arg input The original pointer
/// \arg mask A bitmask of bits to clear from p /// \arg mask A bitmask of bits to clear from p

View File

@@ -5,5 +5,7 @@ module("6s",
deps = [ "libc", "edit" ], deps = [ "libc", "edit" ],
description = "j6 shell", description = "j6 shell",
sources = [ sources = [
"commands.cpp",
"main.cpp", "main.cpp",
"shell.cpp",
]) ])

59
src/user/6s/commands.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include <edit/line.h>
#include <util/format.h>
#include "commands.h"
#include "shell.h"
static void
fslist(shell &s)
{
char line[100];
util::counted<char> output { line, sizeof(line) };
for (auto &f : s.filesystems()) {
size_t len = util::format(output, "%s\r\n", f.tag);
s.write(line, len);
}
}
static void
help(shell &s)
{
char line[100];
util::counted<char> output { line, sizeof(line) };
for (unsigned i = 0; i < g_builtins_count; ++i) {
builtin &cmd = g_builtins[i];
size_t len = util::format(output, "%20s - %s\r\n", cmd.name(), cmd.description());
s.write(line, len);
}
}
static void
pwd(shell &s)
{
char line[100];
util::counted<char> output { line, sizeof(line) };
size_t len = util::format(output, "%s:%s\r\n", s.cfs(), s.cwd());
s.write(line, len);
}
static void
version(shell &s)
{
static const char *gv = GIT_VERSION;
char line[100];
util::counted<char> output { line, sizeof(line) };
size_t len = util::format(output, "jsix version %s\r\n", gv);
s.write(line, len);
}
builtin g_builtins[] = {
{ "fslist", "list available filesystems", fslist },
{ "help", "list available commands", help },
{ "pwd", "print current working directory", pwd },
{ "version", "print current jsix version", version },
};
size_t g_builtins_count = sizeof(g_builtins) / sizeof(g_builtins[0]);

28
src/user/6s/commands.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <stddef.h>
struct shell;
class builtin
{
public:
using runf = void (*)(shell &);
builtin(const char *name, const char *desc, runf func) :
m_name {name}, m_desc {desc}, m_func {func} {}
const char * name() const { return m_name; }
const char * description() const { return m_desc; }
void run(shell &s) { m_func(s); }
private:
const char *m_name;
const char *m_desc;
runf m_func;
};
extern builtin g_builtins[];
extern size_t g_builtins_count;

View File

@@ -11,11 +11,13 @@
#include <j6/types.h> #include <j6/types.h>
#include <edit/line.h> #include <edit/line.h>
#include "shell.h"
extern "C" { extern "C" {
int main(int, const char **); int main(int, const char **);
} }
j6_handle_t g_handle_sys = j6_handle_invalid; //j6_handle_t g_handle_sys = j6_handle_invalid;
const char prompt[] = "\x1b[1;32mj6> \x1b[0m"; const char prompt[] = "\x1b[1;32mj6> \x1b[0m";
static constexpr size_t prompt_len = sizeof(prompt) - 1; static constexpr size_t prompt_len = sizeof(prompt) - 1;
@@ -29,7 +31,6 @@ const char greeting[] = "\x1b[2J\x1b[H\x1b[1;30m\
static constexpr size_t greeting_len = sizeof(greeting) - 1; static constexpr size_t greeting_len = sizeof(greeting) - 1;
void handle_command(edit::source &s, const char *command, size_t len);
class channel_source : class channel_source :
public edit::source public edit::source
@@ -45,9 +46,13 @@ public:
void write(char const *data, size_t len) override { void write(char const *data, size_t len) override {
uint8_t *outp; uint8_t *outp;
m_chan.reserve(len, &outp); while (len) {
memcpy(outp, data, len); size_t size = m_chan.reserve(len, &outp);
m_chan.commit(len); size_t n = len < size ? len : size;
memcpy(outp, data, n);
m_chan.commit(n);
len -= n;
}
} }
private: private:
@@ -61,14 +66,20 @@ main(int argc, const char **argv)
j6_handle_t event = j6_handle_invalid; j6_handle_t event = j6_handle_invalid;
j6_status_t result = j6_status_ok; j6_status_t result = j6_status_ok;
/*
g_handle_sys = j6_find_init_handle(0); g_handle_sys = j6_find_init_handle(0);
if (g_handle_sys == j6_handle_invalid) if (g_handle_sys == j6_handle_invalid)
return 1; return 1;
*/
j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id); j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id);
if (slp == j6_handle_invalid) if (slp == j6_handle_invalid)
return 3; return 3;
j6_handle_t vfs = j6_find_init_handle(j6::proto::vfs::id);
if (vfs == j6_handle_invalid)
return 4;
uint64_t proto_id = "jsix.protocol.stream.ouput"_id; uint64_t proto_id = "jsix.protocol.stream.ouput"_id;
j6::proto::sl::client slp_client {slp}; j6::proto::sl::client slp_client {slp};
@@ -93,6 +104,9 @@ main(int argc, const char **argv)
channel_source source {*cout}; channel_source source {*cout};
edit::line editor {source}; edit::line editor {source};
shell sh { source };
sh.add_filesystem(vfs);
static constexpr size_t bufsize = 256; static constexpr size_t bufsize = 256;
char buffer [bufsize]; char buffer [bufsize];
@@ -100,25 +114,6 @@ main(int argc, const char **argv)
while (true) { while (true) {
size_t len = editor.read(buffer, bufsize, prompt, prompt_len); size_t len = editor.read(buffer, bufsize, prompt, prompt_len);
handle_command(source, buffer, len); sh.handle_command(buffer, len);
}
}
void
handle_command(edit::source &s, const char *command, size_t len)
{
j6::syslog(j6::logs::app, j6::log_level::info, "Command: %s", command);
if (len == 0)
return;
if (strncmp(command, "help", len) == 0) {
s.write("help", 4);
s.write("\r\n", 2);
s.write("version", 7);
s.write("\r\n", 2);
} else if (!strncmp(command, "version", len)) {
static const char *version = GIT_VERSION;
s.write(version, strlen(version));
s.write("\r\n", 2);
} }
} }

43
src/user/6s/shell.cpp Normal file
View File

@@ -0,0 +1,43 @@
#include <string.h>
#include <j6/errors.h>
#include <j6/syslog.hh>
#include "commands.h"
#include "shell.h"
void
shell::add_filesystem(j6_handle_t mb)
{
char tag_buf [100];
size_t size = sizeof(tag_buf);
j6::proto::vfs::client c {mb};
j6_status_t s = c.get_tag(tag_buf, size);
if (s != j6_status_ok)
return;
char *name = new char [size + 1];
memcpy(name, tag_buf, size);
name[size] = 0;
m_fs.push_back({name, c});
if (!m_cfs)
m_cfs = name;
}
void
shell::handle_command(const char *command, size_t len)
{
j6::syslog(j6::logs::app, j6::log_level::info, "Command: %s", command);
if (len == 0)
return;
for (unsigned i = 0; i < g_builtins_count; ++i) {
builtin &cmd = g_builtins[i];
if (strncmp(command, cmd.name(), len) == 0) {
cmd.run(*this);
return;
}
}
static const char unknown[] = "\x1b[1;33mUnknown command.\x1b[0m\r\n";
write(unknown, sizeof(unknown)-1);
}

38
src/user/6s/shell.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <vector>
#include <j6/protocols/vfs.hh>
#include <edit/line.h>
/// Shell state
class shell
{
public:
struct fs {
const char *tag;
j6::proto::vfs::client client;
};
using fs_list = std::vector<fs>;
shell(edit::source &source) : m_source {source}, m_cwd {"/"}, m_cfs {nullptr} {}
const char * cwd() const { return m_cwd; }
const char * cfs() const { return m_cfs; }
// Transparently use source read/write functions
inline void consume(size_t len) { m_source.consume(len); }
inline void write(char const *data, size_t len) { m_source.write(data, len); }
inline size_t read(char const **data) { return m_source.read(data); }
void add_filesystem(j6_handle_t mb);
inline const fs_list & filesystems() const { return m_fs; }
void handle_command(const char *command, size_t len);
private:
edit::source &m_source;
char m_cwd [256];
char const *m_cfs;
fs_list m_fs;
};

View File

@@ -20,7 +20,7 @@
#include "serial.h" #include "serial.h"
extern "C" { extern "C" {
int main(int, const char **); int main(int, const char **, const char **envp);
} }
j6_handle_t g_handle_sys = j6_handle_invalid; j6_handle_t g_handle_sys = j6_handle_invalid;
@@ -36,7 +36,7 @@ uint8_t com2_out[out_buf_size];
constexpr uintptr_t stack_top = 0xf80000000; constexpr uintptr_t stack_top = 0xf80000000;
int int
main(int argc, const char **argv) main(int argc, const char **argv, const char **envp)
{ {
j6::syslog(j6::logs::srv, j6::log_level::info, "uart driver starting"); j6::syslog(j6::logs::srv, j6::log_level::info, "uart driver starting");

View File

@@ -17,6 +17,8 @@
extern "C" void _ldso_plt_lookup(); extern "C" void _ldso_plt_lookup();
extern image_list all_images; extern image_list all_images;
// From crt0, which ld.so also links
extern void __run_ctor_list(uintptr_t start, uintptr_t end);
// Can't use strcmp because it's from another library, and // Can't use strcmp because it's from another library, and
// this needs to be used as part of relocation or symbol lookup // this needs to be used as part of relocation or symbol lookup
@@ -327,15 +329,28 @@ image::parse_rela_table(const util::counted<const rela> &table, image_list &ctx)
void void
image::relocate(image_list &ctx) image::relocate(image_list &ctx)
{ {
if (relocated) if (!relocated) {
return; parse_rela_table(dynrel, ctx);
parse_rela_table(jmprel, ctx);
parse_rela_table(dynrel, ctx); got[1] = reinterpret_cast<uintptr_t>(this);
parse_rela_table(jmprel, ctx); got[2] = reinterpret_cast<uintptr_t>(&_ldso_plt_lookup);
relocated = true;
}
got[1] = reinterpret_cast<uintptr_t>(this); if (!ctors) {
got[2] = reinterpret_cast<uintptr_t>(&_ldso_plt_lookup); uintptr_t pre_init_start = lookup("__preinit_array_start");
relocated = true; uintptr_t pre_init_end = lookup("__preinit_array_end");
if (pre_init_start && pre_init_end)
__run_ctor_list(pre_init_start, pre_init_end);
uintptr_t init_start = lookup("__init_array_start");
uintptr_t init_end = lookup("__init_array_end");
if (init_start && init_end)
__run_ctor_list(init_start, init_end);
ctors = true;
}
} }
image_list::item_type * image_list::item_type *

View File

@@ -28,6 +28,7 @@ struct image
gnu_hash_table const *gnu_hash = nullptr; gnu_hash_table const *gnu_hash = nullptr;
bool relocated = false; bool relocated = false;
bool ctors = false;
/// Look up a string table entry in this image's string table. /// Look up a string table entry in this image's string table.
const char * string(unsigned index) const { const char * string(unsigned index) const {

View File

@@ -12,28 +12,37 @@
image_list all_images; image_list all_images;
extern "C" uintptr_t extern "C" uintptr_t
ldso_init(j6_arg_header *stack_args, uintptr_t *got) ldso_init(const uint64_t *stack, uintptr_t *got)
{ {
j6_arg_loader *arg_loader = nullptr; j6_arg_loader const *arg_loader = nullptr;
j6_arg_handles *arg_handles = nullptr; j6_arg_handles const *arg_handles = nullptr;
j6_arg_header *arg = stack_args; // Walk the stack to get the aux vector
while (arg) { uint64_t argc = *stack++;
switch (arg->type) stack += argc + 1; // Skip argv's and sentinel
while (*stack++); // Skip envp's and sentinel
j6_aux const *aux = reinterpret_cast<const j6_aux*>(stack);
bool more = true;
while (aux && more) {
switch (aux->type)
{ {
case j6_arg_type_loader: case j6_aux_null:
arg_loader = reinterpret_cast<j6_arg_loader*>(arg); more = false;
break; break;
case j6_arg_type_handles: case j6_aux_loader:
arg_handles = reinterpret_cast<j6_arg_handles*>(arg); arg_loader = reinterpret_cast<const j6_arg_loader*>(aux->pointer);
break;
case j6_aux_handles:
arg_handles = reinterpret_cast<const j6_arg_handles*>(aux->pointer);
break; break;
default: default:
break; break;
} }
++aux;
arg = arg->next;
} }
if (!arg_loader) { if (!arg_loader) {
@@ -43,7 +52,7 @@ ldso_init(j6_arg_header *stack_args, uintptr_t *got)
j6_handle_t vfs = j6_handle_invalid; j6_handle_t vfs = j6_handle_invalid;
if (arg_handles) { if (arg_handles) {
for (size_t i = 0; i < arg_handles->nhandles; ++i) { for (size_t i = 0; i < arg_handles->nhandles; ++i) {
j6_arg_handle_entry &ent = arg_handles->handles[i]; const j6_arg_handle_entry &ent = arg_handles->handles[i];
if (ent.proto == j6::proto::vfs::id) { if (ent.proto == j6::proto::vfs::id) {
vfs = ent.handle; vfs = ent.handle;
break; break;
@@ -51,7 +60,6 @@ ldso_init(j6_arg_header *stack_args, uintptr_t *got)
} }
} }
// First relocate ld.so itself. It cannot have any dependencies // First relocate ld.so itself. It cannot have any dependencies
image_list::item_type ldso_image; image_list::item_type ldso_image;
ldso_image.base = arg_loader->loader_base; ldso_image.base = arg_loader->loader_base;
@@ -66,6 +74,7 @@ ldso_init(j6_arg_header *stack_args, uintptr_t *got)
image_list::item_type target_image; image_list::item_type target_image;
target_image.base = arg_loader->image_base; target_image.base = arg_loader->image_base;
target_image.got = arg_loader->got; target_image.got = arg_loader->got;
target_image.ctors = true; // crt0 will call the ctors
target_image.read_dyn_table( target_image.read_dyn_table(
reinterpret_cast<const dyn_entry*>(arg_loader->got[0] + arg_loader->image_base)); reinterpret_cast<const dyn_entry*>(arg_loader->got[0] + arg_loader->image_base));

View File

@@ -17,7 +17,7 @@ _ldso_start:
; Call ldso_init with the loader-provided stack data and ; Call ldso_init with the loader-provided stack data and
; also the address of the GOT, since clang refuses to take ; also the address of the GOT, since clang refuses to take
; the address of it, only dereference it. ; the address of it, only dereference it.
mov rdi, [rbp] mov rdi, rbp
lea rsi, [rel _GLOBAL_OFFSET_TABLE_] lea rsi, [rel _GLOBAL_OFFSET_TABLE_]
call ldso_init call ldso_init
@@ -32,17 +32,6 @@ _ldso_start:
pop rsi pop rsi
pop rdi pop rdi
; Pop all the loader args
pop rsp ; Point the stack at the first arg
mov rax, 0
mov rbx, 0
.poploop:
mov eax, [dword rsp] ; size
mov ebx, [dword rsp+4] ; type
add rsp, rax
cmp ebx, 0
jne .poploop
mov rbp, rsp mov rbp, rsp
jmp r11 jmp r11
.end: .end:

View File

@@ -1,104 +0,0 @@
#include <arch/acpi/tables.h>
#include <bootproto/acpi.h>
#include <bootproto/init.h>
#include <j6/syslog.hh>
#include "acpi.h"
#include "loader.h"
#include "pci.h"
inline constexpr size_t bus_mmio_size = 0x1000'0000;
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
void
probe_pci(j6_handle_t sys, pci_group &pci)
{
j6::syslog(j6::logs::srv, j6::log_level::info, "Probing PCI group at base %016lx", pci.base);
map_phys(sys, pci.base, bus_mmio_size, mmio_flags);
for (unsigned b = pci.bus_start; b <= pci.bus_end; ++b) {
uint8_t bus = static_cast<uint8_t>(b);
for (uint8_t dev = 0; dev < 32; ++dev) {
if (!pci.has_device(bus, dev, 0)) continue;
pci_device d0 {pci, bus, dev, 0};
if (!d0.multi()) continue;
for (uint8_t i = 1; i < 8; ++i) {
if (pci.has_device(bus, dev, i))
pci_device dn {pci, bus, dev, i};
}
}
}
}
void
load_mcfg(j6_handle_t sys, const acpi::table_header *header)
{
const auto *mcfg = acpi::check_get_table<acpi::mcfg>(header);
size_t count = acpi::table_entries(mcfg, sizeof(acpi::mcfg_entry));
/*
m_pci.set_size(count);
m_devices.set_capacity(16);
*/
for (unsigned i = 0; i < count; ++i) {
const acpi::mcfg_entry &mcfge = mcfg->entries[i];
pci_group group = {
.group = mcfge.group,
.bus_start = mcfge.bus_start,
.bus_end = mcfge.bus_end,
.base = reinterpret_cast<uint32_t*>(mcfge.base),
};
probe_pci(sys, group);
j6::syslog(j6::logs::srv, j6::log_level::info, " Found MCFG entry: base %lx group %d bus %d-%d",
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
}
//probe_pci();
}
void
load_acpi(j6_handle_t sys, const bootproto::module *mod)
{
const bootproto::acpi *info = mod->data<bootproto::acpi>();
const util::const_buffer &region = info->region;
map_phys(sys, region.pointer, region.count);
const void *root_table = info->root;
if (!root_table) {
j6::syslog(j6::logs::srv, j6::log_level::error, "null ACPI root table pointer");
return;
}
const acpi::rsdp2 *acpi2 =
reinterpret_cast<const acpi::rsdp2 *>(root_table);
const auto *xsdt =
acpi::check_get_table<acpi::xsdt>(acpi2->xsdt_address);
size_t num_tables = acpi::table_entries(xsdt, sizeof(void*));
for (size_t i = 0; i < num_tables; ++i) {
const acpi::table_header *header = xsdt->headers[i];
if (!header->validate()) {
j6::syslog(j6::logs::srv, j6::log_level::error, "ACPI table at %lx failed validation", header);
continue;
}
switch (header->type) {
case acpi::mcfg::type_id:
load_mcfg(sys, header);
break;
default:
break;
}
}
}

View File

@@ -1,12 +0,0 @@
#pragma once
/// \file acpi.h
/// Routines for loading and parsing ACPI tables
#include <util/counted.h>
#include <j6/types.h>
namespace bootproto {
struct module;
}
void load_acpi(j6_handle_t sys, const bootproto::module *mod);

View File

@@ -0,0 +1,96 @@
#include <j6/syslog.hh>
#include <j6/flags.h>
#include <pci/config.h>
#include "device_manager.h"
#include "loader.h"
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
device_manager::device_manager(j6_handle_t sys, const void *root) :
m_sys {sys}, m_acpi {root, root}
{
load_mcfg();
}
void
device_manager::load_mcfg()
{
m_groups.clear();
for (const auto header : m_acpi) {
if (header->type != acpi::mcfg::type_id)
continue;
const auto *mcfg = acpi::check_get_table<acpi::mcfg>(header);
if (!mcfg) {
j6::syslog(j6::logs::srv, j6::log_level::error, "MCFG table at %lx failed validation", header);
continue;
}
size_t count = acpi::table_entries(mcfg, sizeof(acpi::mcfg_entry));
for (unsigned i = 0; i < count; ++i) {
const acpi::mcfg_entry &mcfge = mcfg->entries[i];
pci::group group = {
.group_id = mcfge.group,
.bus_start = mcfge.bus_start,
.bus_end = mcfge.bus_end,
.base = reinterpret_cast<pci::header_unknown*>(mcfge.base),
};
j6::syslog(j6::logs::srv, j6::log_level::info, "Found MCFG entry: base %lx group %d bus %d-%d",
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
map_phys(m_sys, group.base, pci::group::config_size, mmio_flags);
m_groups.push_back(group);
}
}
}
std::vector<device_info>
device_manager::find_devices(uint8_t klass, uint8_t subclass, uint8_t subsubclass)
{
std::vector<device_info> found;
for (auto group : m_groups) {
for (unsigned b = group.bus_start; b <= group.bus_end; ++b) {
uint8_t bus = static_cast<uint8_t>(b - group.bus_start);
for (uint8_t dev = 0; dev < 32; ++dev) {
pci::bus_addr a {0, dev, bus};
if (!group.has_device(a)) continue;
pci::header_unknown *h = reinterpret_cast<pci::header_unknown*>(group.device_base(a));
j6::syslog(j6::logs::srv, j6::log_level::info,
" Found PCI device at %02d:%02d:%d class %x.%x.%x id %04x:%04x",
a.bus, a.device, a.function,
h->device_class[2], h->device_class[1], h->device_class[0],
h->vendor, h->device);
found.push_back({h, a});
if (!h->multi_device) continue;
for (uint8_t i = 1; i < 8; ++i) {
pci::bus_addr fa {i, dev, bus};
if (group.has_device(fa)) {
pci::header_unknown *fh =
reinterpret_cast<pci::header_unknown*>(group.device_base(fa));
j6::syslog(j6::logs::srv, j6::log_level::info,
" found PCI func at %02d:%02d:%d class %x.%x.%x id %04x:%04x",
fa.bus, fa.device, fa.function,
fh->device_class[2], fh->device_class[1], fh->device_class[0],
fh->vendor, fh->device);
found.push_back({fh, fa});
}
}
}
}
}
return found;
}

View File

@@ -0,0 +1,29 @@
#pragma once
/// \file device_manager.h
/// APCI and PCI device lookup
#include <vector>
#include <j6/types.h>
#include <acpi/acpi.h>
#include <pci/group.h>
struct device_info
{
pci::header_unknown *base;
pci::bus_addr addr;
};
class device_manager
{
public:
device_manager(j6_handle_t sys, const void *root);
std::vector<device_info> find_devices(uint8_t klass, uint8_t subclass, uint8_t subsubclass);
private:
void load_mcfg();
j6_handle_t m_sys;
acpi::system m_acpi;
std::vector<pci::group> m_groups;
};

View File

@@ -2,18 +2,17 @@
init = module("srv.init", init = module("srv.init",
targets = [ "init" ], targets = [ "init" ],
deps = [ "libc", "elf", "bootproto", "zstd" ], deps = [ "libc", "elf", "bootproto", "zstd", "acpi", "pci" ],
static = True, static = True,
description = "Init server", description = "Init server",
ld_script = "init.ld", ld_script = "init.ld",
sources = [ sources = [
"acpi.cpp", "device_manager.cpp",
"initfs.cpp", "initfs.cpp",
"j6romfs.cpp", "j6romfs.cpp",
"loader.cpp", "loader.cpp",
"main.cpp", "main.cpp",
"modules.cpp", "modules.cpp",
"pci.cpp",
"service_locator.cpp", "service_locator.cpp",
"start.s", "start.s",
]) ])

View File

@@ -1,6 +1,7 @@
#include <stdint.h> #include <stdint.h>
#include <j6/flags.h> #include <j6/flags.h>
#include <j6/errors.h> #include <j6/errors.h>
#include <j6/memutils.h>
#include <j6/protocols/vfs.h> #include <j6/protocols/vfs.h>
#include <j6/syscalls.h> #include <j6/syscalls.h>
#include <j6/syslog.hh> #include <j6/syslog.hh>
@@ -11,6 +12,9 @@
static uint64_t initfs_running = 1; static uint64_t initfs_running = 1;
static constexpr size_t buffer_size = 2048; static constexpr size_t buffer_size = 2048;
static char fs_tag[] = "init";
static constexpr size_t fs_tag_len = sizeof(fs_tag) - 1;
j6_status_t j6_status_t
handle_load_request(j6romfs::fs &fs, const char *path, j6_handle_t &vma) handle_load_request(j6romfs::fs &fs, const char *path, j6_handle_t &vma)
{ {
@@ -82,6 +86,14 @@ initfs_start(j6romfs::fs &fs, j6_handle_t mb)
tag = j6_proto_vfs_file; tag = j6_proto_vfs_file;
break; break;
case j6_proto_vfs_get_tag:
out_len = fs_tag_len;
handles_count = 0;
tag = j6_proto_vfs_tag;
memcpy(buffer, fs_tag, fs_tag_len);
break;
default: default:
tag = j6_proto_base_status; tag = j6_proto_base_status;
*reinterpret_cast<j6_status_t*>(buffer) = j6_err_invalid_arg; *reinterpret_cast<j6_status_t*>(buffer) = j6_err_invalid_arg;
@@ -90,7 +102,6 @@ initfs_start(j6romfs::fs &fs, j6_handle_t mb)
handles_count = 0; handles_count = 0;
} }
out_len = buffer_size;
s = j6_mailbox_respond(mb, &tag, s = j6_mailbox_respond(mb, &tag,
buffer, &out_len, buffer_size, buffer, &out_len, buffer_size,
&give_handle, &handles_count, max_handles, &give_handle, &handles_count, max_handles,

View File

@@ -10,6 +10,7 @@
#include <j6/protocols.h> #include <j6/protocols.h>
#include <j6/syscalls.h> #include <j6/syscalls.h>
#include <j6/syslog.hh> #include <j6/syslog.hh>
#include <util/vector.h>
#include <util/xoroshiro.h> #include <util/xoroshiro.h>
#include "j6/types.h" #include "j6/types.h"
@@ -26,10 +27,10 @@ static util::xoroshiro256pp rng {0x123456};
inline uintptr_t align_up(uintptr_t a) { return ((a-1) & ~(MiB-1)) + MiB; } inline uintptr_t align_up(uintptr_t a) { return ((a-1) & ~(MiB-1)) + MiB; }
class stack_pusher class stack_builder
{ {
public: public:
stack_pusher(uint8_t *local_top, uintptr_t child_top) : stack_builder(uint8_t *local_top, uintptr_t child_top) :
m_local_top {local_top}, m_child_top {child_top}, m_used {0}, m_last_arg {0} { m_local_top {local_top}, m_child_top {child_top}, m_used {0}, m_last_arg {0} {
memset(local_top - 4096, 0, 4096); // Zero top page memset(local_top - 4096, 0, 4096); // Zero top page
} }
@@ -41,20 +42,53 @@ public:
return reinterpret_cast<T*>(local_pointer()); return reinterpret_cast<T*>(local_pointer());
} }
void add_arg(const char *arg) {
size_t len = strlen(arg);
char * argp = push<char>(len);
strncpy(argp, arg, len + 1); // Copy the 0
m_argv.append(child_pointer());
}
void add_env(const char *key, const char *value) {
size_t klen = strlen(key);
size_t vlen = strlen(value);
char * envp = push<char>(klen + vlen + 1); // add =
strncpy(envp, key, klen);
envp[klen] = '=';
strncpy(envp+klen+1, value, vlen + 1); // Copy the 0
m_envp.append(child_pointer());
}
void add_aux(j6_aux_type type, uint64_t value) {
m_aux.append({type, value});
}
template <typename T> template <typename T>
T * push_arg(size_t extra = 0) { T * add_aux_data(j6_aux_type type, size_t extra = 0) {
T * arg = push<T>(extra); T * arg = push<T>(extra);
arg->header.size = sizeof(T) + extra; add_aux(type, child_pointer());
arg->header.type = T::type_id;
arg->header.next = reinterpret_cast<j6_arg_header*>(m_last_arg);
m_last_arg = child_pointer();
return arg; return arg;
} }
void push_current_pointer() { void build() {
uintptr_t addr = child_pointer(); *push<uint64_t, 16>() = 0;
uintptr_t *ptr = push<uintptr_t, 16>();
*ptr = addr; if ((m_argv.count() + m_envp.count()) % 2 == 0)
*push<uint64_t>() = 0; // Pad for 16-byte alignment
*push<j6_aux>() = {0, 0};
for(j6_aux &aux : m_aux)
*push<j6_aux>() = aux;
*push<uint64_t>() = 0;
for (uintptr_t addr : m_envp)
*push<uintptr_t>() = addr;
*push<uint64_t>() = 0;
for (uintptr_t addr : m_argv)
*push<uintptr_t>() = addr;
*push<size_t>() = m_argv.count();
} }
uint8_t * local_pointer() { return m_local_top - m_used; } uint8_t * local_pointer() { return m_local_top - m_used; }
@@ -65,6 +99,10 @@ private:
uintptr_t m_child_top; uintptr_t m_child_top;
size_t m_used; size_t m_used;
uintptr_t m_last_arg; uintptr_t m_last_arg;
util::vector<uintptr_t> m_argv;
util::vector<uintptr_t> m_envp;
util::vector<j6_aux> m_aux;
}; };
j6_handle_t j6_handle_t
@@ -154,10 +192,10 @@ give_handle(j6_handle_t proc, j6_handle_t h, const char *name)
} }
static j6_handle_t static j6_handle_t
create_process(j6_handle_t sys, j6_handle_t slp, j6_handle_t vfs) create_process(j6_handle_t sys, j6_handle_t slp, j6_handle_t vfs, const char *path)
{ {
j6_handle_t proc = j6_handle_invalid; j6_handle_t proc = j6_handle_invalid;
j6_status_t res = j6_process_create(&proc); j6_status_t res = j6_process_create(&proc, path);
if (res != j6_status_ok) { if (res != j6_status_ok) {
j6::syslog(j6::logs::srv, j6::log_level::error, "error loading program: creating process: %lx", res); j6::syslog(j6::logs::srv, j6::log_level::error, "error loading program: creating process: %lx", res);
return j6_handle_invalid; return j6_handle_invalid;
@@ -193,7 +231,7 @@ load_program(
const module *arg) const module *arg)
{ {
j6::syslog(j6::logs::srv, j6::log_level::info, "Loading program '%s' into new process", path); j6::syslog(j6::logs::srv, j6::log_level::info, "Loading program '%s' into new process", path);
j6_handle_t proc = create_process(sys, slp, vfs); j6_handle_t proc = create_process(sys, slp, vfs, path);
if (proc == j6_handle_invalid) if (proc == j6_handle_invalid)
return false; return false;
@@ -227,15 +265,17 @@ load_program(
return false; return false;
} }
stack_pusher stack { stack_builder stack {
reinterpret_cast<uint8_t*>(stack_addr + stack_size), reinterpret_cast<uint8_t*>(stack_addr + stack_size),
stack_top, stack_top,
}; };
// Push program's arg sentinel stack.add_arg(path);
stack.push_arg<j6_arg_none>(); stack.add_env("jsix_version", GIT_VERSION);
j6_arg_handles *handles_arg = stack.push_arg<j6_arg_handles>(3 * sizeof(j6_arg_handle_entry)); static constexpr size_t nhandles = 3;
static constexpr size_t handles_extra = 3 * sizeof(j6_arg_handle_entry);
j6_arg_handles *handles_arg = stack.add_aux_data<j6_arg_handles>(j6_aux_handles, handles_extra);
handles_arg->nhandles = 3; handles_arg->nhandles = 3;
handles_arg->handles[0].handle = sys; handles_arg->handles[0].handle = sys;
handles_arg->handles[0].proto = 0; handles_arg->handles[0].proto = 0;
@@ -246,23 +286,17 @@ load_program(
if (arg) { if (arg) {
size_t data_size = arg->bytes - sizeof(*arg); size_t data_size = arg->bytes - sizeof(*arg);
j6_arg_driver *driver_arg = stack.push_arg<j6_arg_driver>(data_size); j6_arg_driver *driver_arg = stack.add_aux_data<j6_arg_driver>(j6_aux_device, data_size);
driver_arg->device = arg->type_id; driver_arg->device = arg->type_id;
const uint8_t *arg_data = arg->data<uint8_t>(); const uint8_t *arg_data = arg->data<uint8_t>();
memcpy(driver_arg->data, arg_data, data_size); memcpy(driver_arg->data, arg_data, data_size);
} }
// Add an aligned pointer to the program's args list
stack.push_current_pointer();
uintptr_t entrypoint = program_elf.entrypoint() + program_image_base; uintptr_t entrypoint = program_elf.entrypoint() + program_image_base;
if (dyn) { if (dyn) {
// Push loaders's arg sentinel j6_arg_loader *loader_arg = stack.add_aux_data<j6_arg_loader>(j6_aux_loader);
stack.push_arg<j6_arg_none>();
j6_arg_loader *loader_arg = stack.push_arg<j6_arg_loader>();
loader_arg->image_base = program_image_base; loader_arg->image_base = program_image_base;
loader_arg->entrypoint = program_elf.entrypoint(); // ld.so will offset the entrypoint, don't do it here. loader_arg->entrypoint = program_elf.entrypoint(); // ld.so will offset the entrypoint, don't do it here.
@@ -270,9 +304,6 @@ load_program(
if (got_section) if (got_section)
loader_arg->got = reinterpret_cast<uintptr_t*>(program_image_base + got_section->addr); loader_arg->got = reinterpret_cast<uintptr_t*>(program_image_base + got_section->addr);
// Add an aligned pointer to the loaders's args list
stack.push_current_pointer();
uintptr_t ldso_image_base = (eop & ~(MiB-1)) + MiB; uintptr_t ldso_image_base = (eop & ~(MiB-1)) + MiB;
for (auto seg : program_elf.segments()) { for (auto seg : program_elf.segments()) {
@@ -300,6 +331,8 @@ load_program(
} }
} }
stack.build();
uintptr_t stack_base = stack_top-stack_size; uintptr_t stack_base = stack_top-stack_size;
res = j6_vma_map(stack_vma, proc, &stack_base, j6_vm_flag_exact); res = j6_vma_map(stack_vma, proc, &stack_base, j6_vm_flag_exact);
if (res != j6_status_ok) { if (res != j6_status_ok) {

View File

@@ -10,11 +10,14 @@
#include <j6/thread.hh> #include <j6/thread.hh>
#include <j6/types.h> #include <j6/types.h>
#include <acpi/acpi.h>
#include <bootproto/acpi.h> #include <bootproto/acpi.h>
#include <bootproto/init.h> #include <bootproto/init.h>
#include <bootproto/devices/framebuffer.h> #include <bootproto/devices/framebuffer.h>
#include <pci/config.h>
#include <pci/group.h>
#include "acpi.h" #include "device_manager.h"
#include "initfs.h" #include "initfs.h"
#include "j6romfs.h" #include "j6romfs.h"
#include "loader.h" #include "loader.h"
@@ -24,13 +27,19 @@
using bootproto::module; using bootproto::module;
using bootproto::module_type; using bootproto::module_type;
constexpr uintptr_t stack_top = 0xf80000000; inline constexpr uintptr_t stack_top = 0xf80000000;
inline constexpr j6_vm_flags mmio_flags = (j6_vm_flags)(j6_vm_flag_write | j6_vm_flag_mmio);
void load_acpi(j6_handle_t sys, const bootproto::module *mod);
int int
main(int argc, const char **argv, const char **env) main(int argc, const char **argv, const char **env)
{ {
j6_status_t s; j6_status_t s;
// argv[0] is not a char* for init, but the modules pointer
uintptr_t modules_addr = reinterpret_cast<uintptr_t>(argv[0]);
j6_handle_t slp_mb = j6_handle_invalid; j6_handle_t slp_mb = j6_handle_invalid;
j6_handle_t slp_mb_child = j6_handle_invalid; j6_handle_t slp_mb_child = j6_handle_invalid;
@@ -42,7 +51,22 @@ main(int argc, const char **argv, const char **env)
j6::syslog(j6::logs::srv, j6::log_level::info, "srv.init starting"); j6::syslog(j6::logs::srv, j6::log_level::info, "srv.init starting");
sys = j6_find_first_handle(j6_object_type_system); // Since we had no parent to set up a j6_arg_handles object,
// ask the kernel for our handles and look in that list for
// the system handle.
static constexpr size_t num_init_handles = 16;
j6_handle_descriptor handles[num_init_handles];
size_t num_handles = num_init_handles;
s = j6_handle_list(handles, &num_handles);
if (s != j6_status_ok)
return s;
for (unsigned i = 0; i < num_handles; ++i) {
if (handles[i].type == j6_object_type_system) {
sys = handles[i].handle;
break;
}
}
if (sys == j6_handle_invalid) if (sys == j6_handle_invalid)
return 1; return 1;
@@ -74,9 +98,6 @@ main(int argc, const char **argv, const char **env)
if (s != j6_status_ok) if (s != j6_status_ok)
return s; return s;
const j6_init_args *initp = j6_get_init_args();
uintptr_t modules_addr = initp->argv[0];
std::vector<const module*> mods; std::vector<const module*> mods;
load_modules(modules_addr, sys, 0, mods); load_modules(modules_addr, sys, 0, mods);
@@ -126,7 +147,11 @@ main(int argc, const char **argv, const char **env)
j6::thread vfs_thread {[=, &initrd](){ initfs_start(initrd, vfs_mb); }, stack_top}; j6::thread vfs_thread {[=, &initrd](){ initfs_start(initrd, vfs_mb); }, stack_top};
j6_status_t result = vfs_thread.start(); j6_status_t result = vfs_thread.start();
load_acpi(sys, acpi_module); // Load ACPI into device_manager
const bootproto::acpi *acpi = acpi_module->data<bootproto::acpi>();
map_phys(sys, acpi->region.pointer, acpi->region.count);
device_manager dm {sys, acpi->root};
load_program("/jsix/drivers/drv.uart.elf", initrd, sys_child, slp_mb_child, vfs_mb_child); load_program("/jsix/drivers/drv.uart.elf", initrd, sys_child, slp_mb_child, vfs_mb_child);
@@ -141,6 +166,8 @@ main(int argc, const char **argv, const char **env)
} }
} }
auto ahci_drives = dm.find_devices(1, 6, 1);
initrd.for_each("/jsix/services", initrd.for_each("/jsix/services",
[=](const j6romfs::inode *in, const char *name) { [=](const j6romfs::inode *in, const char *name) {
if (in->type != j6romfs::inode_type::file) if (in->type != j6romfs::inode_type::file)
@@ -154,4 +181,3 @@ main(int argc, const char **argv, const char **env)
service_locator_start(slp_mb); service_locator_start(slp_mb);
return 0; return 0;
} }

View File

@@ -1,139 +0,0 @@
#pragma once
/// \file pci.h
/// PCI devices and groups
#include <stdint.h>
#include <util/pointers.h>
struct pci_group;
enum class isr : uint8_t;
struct pci_cap
{
enum class type : uint8_t
{
msi = 0x05,
msix = 0x11
};
type id;
uint8_t next;
} __attribute__ ((packed));
/// Information about a discovered PCIe device
class pci_device
{
public:
/// Default constructor creates an empty object.
pci_device();
/// Constructor
/// \arg group The group of this device's bus
/// \arg bus The bus number this device is on
/// \arg device The device number of this device
/// \arg func The function number of this device
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
/// Check if this device is multi-function.
/// \returns True if this device is multi-function
inline bool multi() const { return m_multi; }
/// Get the bus number this device is on.
/// \returns The bus number
inline uint8_t bus() const { return (m_bus_addr >> 8); }
/// Get the device number of this device on its bus
/// \returns The device number
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
/// Get the function number of this device on its device
/// \returns The function number
inline uint8_t function() const { return m_bus_addr & 0x7; }
/// Get the device class
/// \returns The PCI device class
inline uint8_t devclass() const { return m_class; }
/// Get the device subclass
/// \returns The PCI device subclass
inline uint8_t subclass() const { return m_subclass; }
/// Get the device program interface
/// \returns The PCI device program interface
inline uint8_t progif() const { return m_progif; }
/// Read one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \returns The contents of the BAR
uint32_t get_bar(unsigned i);
/// Write one of the device's Base Address Registers
/// \arg i Which BAR to read (up to 5 for non-bridges)
/// \arg val The value to write
void set_bar(unsigned i, uint32_t val);
/// Write to the MSI registers
/// \arg addr The address to write to the MSI address registers
/// \arg data The value to write to the MSI data register
void write_msi_regs(uintptr_t addr, uint16_t data);
/// Get a bus address, given the bus/device/function numbers.
/// \arg bus Number of the bus
/// \arg device Index of the device on the bus
/// \arg func The function number within the device
/// \returns The computed bus_addr
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
{
return bus << 8 | device << 3 | func;
}
private:
uint32_t *m_base;
pci_cap *m_msi;
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
uint16_t m_bus_addr;
uint16_t m_vendor;
uint16_t m_device;
uint8_t m_class;
uint8_t m_subclass;
uint8_t m_progif;
uint8_t m_revision;
bool m_multi;
uint8_t m_header_type;
};
/// Represents data about a PCI bus group from the ACPI MCFG
struct pci_group
{
uint16_t group;
uint16_t bus_start;
uint16_t bus_end;
uint32_t *base;
/// Get the base address of the MMIO configuration registers for a device
/// \arg bus The bus number of the device (relative to this group)
/// \arg device The device number on the given bus
/// \arg func The function number on the device
/// \returns A pointer to the memory-mapped configuration registers
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
{
return util::offset_pointer(base,
pci_device::bus_addr(bus, device, func) << 12);
}
/// Check if the given device function is present.
/// \arg bus The bus number of the device (relative to this group)
/// \arg device The device number on the given bus
/// \arg func The function number on the device
/// \returns True if the device function is present
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
};

View File

@@ -19,11 +19,17 @@ _start:
; stack in BSS and assign that to be init's first stack ; stack in BSS and assign that to be init's first stack
mov rsp, init_stack_top mov rsp, init_stack_top
; Push a fake j6_arg_none ; Push fake initial stack
push 0x00 ; pad for 16-byte alignment push 0x00 ; stack sentinel (16 bytes)
push 0x00 ; no next arg push 0x00 ;
push 0x10 ; size 16 bytes, type 0 (none) push 0x00 ; alignment padding
push rsp push 0x00 ; auxv sentinel (16 bytes)
push 0x00 ;
push 0x00 ; envp sentinel (8 bytes)
push 0x00 ; argv sentinel (8 bytes)
push rsi ; argv[1] -- for init, not actually a char*
push rdi ; argv[0] -- for init, not actually a char*
push 0x02 ; argc
jmp _libc_crt0_start jmp _libc_crt0_start
.end: .end: