24 Commits

Author SHA1 Message Date
70af5a31cd [6s] Add simple command handling
Super simple little strncmp-based command lookup, adding 'help' and
'version' commands to 6s.
2024-04-28 19:36:27 -07:00
0ab2f59fe8 [libc] Fix a strncmp bug
The strncmp implementation too eagerly advanced the string pointers,
such that it would compare the next characters after the ones that
didn't match.
2024-04-28 19:35:12 -07:00
ab84cdb790 [edit] Change libedit API
The edit::line API now takes a source object for I/O, qnd allows getting
a single command from the editor.
2024-04-28 17:24:15 -07:00
da9041823b [edit] Add line-editing library, libedit
Now used by the 6s shell to enable backspace, return, etc. No cursor
movement yet or anything complex.
2024-04-27 17:53:18 -07:00
2d7cfc6c59 [scripts] Fix mkj6romfs for real
I overwrote it last time.
2024-04-27 16:20:23 -07:00
cee4fe67fc [tools] Let GDB quit easily if inferior already quit
I had defined hook-quit in the GDB script to kill the inferior. But if
the inferior had already terminated, then trying to quit GDB only
printed an error. Now hook-quit is smarter and checks if the inferior is
running before trying to kill it.
2024-04-27 15:41:05 -07:00
53f90f5a1d [scripts] Fix initrd directory compression
The mkj6romfs.py script always wrote uncompessed directory info to the
initrd image, even if compressed was smaller - but it would write the
metadata as if it had compressed it.
2024-04-27 13:55:05 -07:00
bab2dd5c69 [libc] Change exit status from int to long
Slightly breaking the C standard, but in a way that's unlikely to break
things - allow 64-bit process exit status codes.
2024-04-27 12:59:02 -07:00
3b9efc11d0 [drv.uart] Read everything from the channel in uartnnMake sure to keep reading until the channel is empty so output isn't behindnby a keystroke. 2024-04-26 00:32:06 -07:00
182d3b0a6a [kernel] Add the handle_close syscallnnAdd a generic handle_close syscall and use it in j6::channel when failing to open 2024-04-24 15:48:00 -07:00
Justin C. Miller
ed38e989b1 [srv.logger] Update logger for new channel/SLP
Update srv.logger to build correctly with new channel and service
locator changes.
2024-04-23 23:59:06 -07:00
Justin C. Miller
1bc6f422c5 [kernel] Fix a bug with auto-assigned VMA addresses
When calling vma_map or vma_create_map with an address of 0, the kernel would
return colliding addresses.
2024-04-23 23:47:52 -07:00
Justin C. Miller
4909d15c20 [boot] Fix panic handler not installing
The switch to util::bitset introduced a typo that caused panic handlers
to never load.
2024-04-23 23:46:31 -07:00
Justin C. Miller
e725795a17 [6s] Add 6s shell, make channels full-duplex
This commit adds the 6s shell, and a bunch of supporting work for it.
Major changes include:

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

Known issues:

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

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

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

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

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

56
.github/workflows/sphinx_deploy.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Deploy docs site with Sphinx
on:
push:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Default to bash
defaults:
run:
shell: bash
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
- name: Sphinx build
uses: jsix-os/sphinx-action@master
with:
docs-folder: "docs/"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./docs/_build/html
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

1
.gitignore vendored
View File

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

View File

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

View File

@@ -501,6 +501,18 @@ class LinkedListPrinter:
return self.items return self.items
class IsRunning(gdb.Function):
def __init__(self):
super(IsRunning, self).__init__("is_running")
def invoke(self):
inferior = gdb.selected_inferior()
return \
inferior is not None and \
inferior.is_valid() and \
len(inferior.threads()) > 0
def build_pretty_printers(): def build_pretty_printers():
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix") pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
pp.add_printer("cap table", '^cap_table$', CapTablePrinter) pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
@@ -520,9 +532,10 @@ GetThreadsCommand()
PrintProfilesCommand() PrintProfilesCommand()
DumpLogCommand() DumpLogCommand()
ShowCurrentProcessCommand() ShowCurrentProcessCommand()
IsRunning()
gdb.execute("display/i $rip") gdb.execute("display/i $rip")
gdb.execute("define hook-quit\nkill\nend") gdb.execute("define hook-quit\nif $is_running()\n kill\nend\nend")
if not gdb.selected_inferior().was_attached: if not gdb.selected_inferior().was_attached:
gdb.execute("add-symbol-file build/panic.serial.elf") gdb.execute("add-symbol-file build/panic.serial.elf")
gdb.execute("target remote :1234") gdb.execute("target remote :1234")

View File

@@ -0,0 +1,14 @@
---
location: jsix
init: srv.init
initrd:
name: initrd.dat
format: zstd
panic:
- panic.serial
services:
- srv.logger
drivers:
- drv.uart
libs:
- ld.so

View File

@@ -0,0 +1,14 @@
---
location: jsix
init: srv.init
initrd:
name: initrd.dat
format: zstd
panic:
- panic.serial
services:
- 6s
drivers:
- drv.uart
libs:
- ld.so

View File

@@ -1,3 +1,6 @@
# An ``event`` is a simple synchronization object. It contains up to 64 signals
# that threads can wait for and signal in parallel.
object event : object { object event : object {
uid f441e03da5516b1a uid f441e03da5516b1a

View File

@@ -20,8 +20,9 @@ object mailbox : object {
method call [cap:send] { method call [cap:send] {
param tag uint64 [inout] param tag uint64 [inout]
param data buffer [optional inout] param data buffer [optional inout]
param data_in_len size # number of bytes in data used for input param data_size size # number of total bytes in data buffer
param handles ref object [optional inout handle list] param handles ref object [optional inout handle list]
param handles_size size # total size of handles buffer
} }
# Respond to a message sent using call, and wait for another # Respond to a message sent using call, and wait for another
@@ -31,8 +32,9 @@ object mailbox : object {
method respond [cap:receive] { method respond [cap:receive] {
param tag uint64 [inout] param tag uint64 [inout]
param data buffer [optional inout] param data buffer [optional inout]
param data_in_len size # number of bytes in data used for input param data_size size # number of total bytes in data buffer
param handles ref object [optional inout handle list] param handles ref object [optional inout handle list]
param handles_size size # total size of handles buffer
param reply_tag uint64 [inout] param reply_tag uint64 [inout]
param flags uint64 param flags uint64
} }

View File

@@ -1,4 +1,6 @@
# The base type of all kernel-exposed objects # All kernel-exposed objects inherit from the base ``object`` type, so the
# ``object`` syscalls can be used with any object's handle.
object object [virtual] { object object [virtual] {
uid 667f61fb2cd57bb4 uid 667f61fb2cd57bb4
cname kobject cname kobject

View File

@@ -1,7 +1,7 @@
import "objects/object.def" import "objects/object.def"
# Processes are a collection of handles and a virtual memory # A ``process`` object represents a process running on the system, and allows
# space inside which threads are run. # control over the threads, handles, and virtual memory space of that process.
object process : object { object process : object {
uid 0c69ee0b7502ba31 uid 0c69ee0b7502ba31
@@ -19,7 +19,7 @@ object process : object {
# Stop all threads and exit the current process # Stop all threads and exit the current process
method exit [static noreturn] { method exit [static noreturn] {
param result int32 # The result to retrun to the parent process param result int64 # The result to retrun to the parent process
} }
# Give the given process a handle that points to the same # Give the given process a handle that points to the same

View File

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

View File

@@ -1,3 +1,7 @@
# A ``thread`` object represents a thread of execution within a process running
# on the system. The actual thread does not need to be currently running to
# hold a handle to it.
object thread : object { object thread : object {
uid 11f23e593d5761bd uid 11f23e593d5761bd

View File

@@ -1,5 +1,9 @@
import "objects/process.def" import "objects/process.def"
# A ``vma`` object represents a single virtual memory area, which may be shared
# between several processes. A process having a handle to a ``vma`` does not
# necessarily mean that it is mapped into that process' virtual memory space.
object vma : object { object vma : object {
uid d6a12b63b3ed3937 uid d6a12b63b3ed3937
cname vm_area cname vm_area

View File

@@ -24,6 +24,8 @@ interface syscalls [syscall] {
# Write a message to the kernel log # Write a message to the kernel log
function log { function log {
param area uint8
param severity uint8
param message string param message string
} }
@@ -42,6 +44,11 @@ interface syscalls [syscall] {
param mask uint32 # The capability bitmask param mask uint32 # The capability bitmask
} }
# Close the handle to an object
function handle_close {
param hnd ref object [handle] # The handle to close
}
# Block waiting on a futex # Block waiting on a futex
function futex_wait [static] { function futex_wait [static] {
param address uint32* # Address of the futex value param address uint32* # Address of the futex value

22
docs/Makefile Normal file
View File

@@ -0,0 +1,22 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
ROOTDIR = $(SOURCEDIR)/..
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
PYTHONPATH=$(ROOTDIR)/scripts cog -r -D definitions_path=$(ROOTDIR)/definitions -c syscall_interface.rst
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

254
docs/_static/custom.css vendored Normal file
View File

@@ -0,0 +1,254 @@
/* custom.css - jsix version */
:root {
--background-color: #181820;
--link-color: #7070e0;
--link-hover-color: #9090ff;
--text-color: #3d3d3d;
--text-literal-color: #d26a98;
}
.wy-nav-side {
background: var(--background-color);
}
@media screen and (min-width: 1100px) {
.wy-nav-content-wrap {
background: var(--background-color);
}
}
a {
color: var(--link-color);
}
a:hover {
color: var(--link-hover-color);
}
a:visited {
color: var(--link-color);
}
.rst-content {
color: var(--text-color);
}
.rst-content code.literal {
color: var(--text-literal-color);
}
.rst-content tt.literal {
color: var(--text-literal-color);
}
.rst-content .note {
color: #003274;
background: #ccddf3;
padding: 1rem;
margin-bottom: 1rem;
}
.rst-content .note .admonition-title {
display: none;
}
.rst-content .warning {
color: #605000;
background: #fcf4cc;
padding: 1rem;
margin-bottom: 1rem;
}
.rst-content .warning .admonition-title {
display: none;
}
.rst-content .highlight {
background: #f5f5f5;
}
.wy-side-scroll {
background-color: var(--background-color);
}
.wy-side-nav-search {
background-color: var(--background-color);
}
.wy-side-nav-search input[type="text"] {
width: 100%;
border-radius: 0px;
padding: 6px 12px;
border-color: var(--background-color);
}
.wy-menu-vertical a {
font-size: 100%;
color: #d9d9d9;
padding-top: 0.6rem;
padding-bottom: 0.6rem;
background-color: inherit;
}
.wy-menu-vertical a:hover {
background-color: unset;
opacity: 1;
}
.wy-menu-vertical li.current > a {
background-color: var(--background-color);
color: white;
}
.wy-menu-vertical li.current > a span.toctree-expand {
display: block;
font-size: inherit;
line-height: inherit;
color: inherit;
}
.wy-menu-vertical li.current > a span.toctree-expand:before {
display: block;
font-size: inherit;
line-height: inherit;
color: inherit;
}
.wy-menu-vertical li.current > a span.toctree-expand:hover {
color: white;
}
.wy-menu-vertical li.current > a:hover {
background-color: var(--background-color);
color: white;
}
.wy-menu-vertical li.current > a:hover span.toctree-expand {
color: white;
}
.wy-menu-vertical .toctree-l1 {
opacity: 0.5;
}
.wy-menu-vertical .toctree-l1:hover {
opacity: 1;
background-color: inherit;
}
.wy-menu-vertical li.toctree-l1.current {
opacity: 1;
background-color: inherit;
}
.wy-menu-vertical li.toctree-l1.current > a {
border: 0px;
}
.wy-menu-vertical .toctree-l2:hover {
background-color: #566673;
}
.wy-menu-vertical li.toctree-l2.current > a {
background-color: #566673;
color: white;
}
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
background-color: #e4e7ea;
color: #838383;
}
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover {
color: var(--text-color);
}
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover span.toctree-expand {
color: var(--text-color);
}
.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current > a {
color: var(--text-color);
}
.wy-menu-vertical li.toctree-l2 a {
border: 0px;
background-color: #566673;
color: #d9d9d9;
}
.wy-menu-vertical li.toctree-l2 a span.toctree-expand {
display: block;
font-size: inherit;
line-height: inherit;
color: inherit;
}
.wy-menu-vertical li.toctree-l2 a span.toctree-expand:before {
display: block;
font-size: inherit;
line-height: inherit;
color: inherit;
}
.wy-menu-vertical li.toctree-l2 a span.toctree-expand:hover {
color: white;
}
.wy-menu-vertical li.toctree-l2 a:hover {
color: white;
background-color: #566673;
}
.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand {
color: white;
}
.wy-menu-vertical li.toctree-l3.current > a {
background-color: #e4e7ea;
color: #838383;
}
.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a {
background-color: #e4e7ea;
color: #838383;
}
.wy-menu-vertical li.toctree-l3.current li.toctree-l4.current > a {
color: var(--text-color);
}
.wy-nav-top {
background-color: var(--background-color);
}
.btn {
display: inline-block;
font-weight: 400;
line-height: 1.5;
color: var(--text-color);
text-align: center;
text-decoration: none;
vertical-align: middle;
cursor: pointer;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 1rem;
border-radius: 0;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
box-shadow: unset;
}
.btn-neutral {
background: unset !important;
color: #838383 !important;
}
.btn-neutral:active {
padding: 0.375rem 0.75rem;
box-shadow: unset;
}

50
docs/conf.py Normal file
View File

@@ -0,0 +1,50 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'jsix'
copyright = '2024, Justin C. Miller'
author = 'Justin C. Miller'
release = '0.8'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = ['sphinx.ext.todo']
primary_domain = 'cpp'
todo_include_todos = True
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'renku'
html_title = 'jsix'
html_logo = 'jsix_transparent.svg'
html_static_path = ['_static']
html_css_files = ['custom.css']
html_theme_options = {
"description": "The jsix description",
"github_repo": "https://github.com/justinian/jsix",
"logo_only": True,
"footer_icons": [
{
"name": "GitHub",
"url": "https://github.com/justinian/jsix",
"html": """
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
</svg>
""",
"class": "",
},
],
}

79
docs/index.rst Normal file
View File

@@ -0,0 +1,79 @@
.. jsix documentation master file
.. |amd64| replace:: :abbr:`amd64 (aka x86_64)`
The jsix Operating System
=========================
Introduction
------------
**jsix** is a custom multi-core x64 operating system being built from scratch,
supporting modern [#]_ Intel or AMD CPUs, and UEFI firmware. It was initially
created out of a desire to explore UEFI and to explore what's possible with a
microkernel architecture on modern 64-bit architectures.
Most of jsix is written in C++ (C++17, using `LLVM <https://llvm.org>`_), but
you'll also find some assembly (in `NASM <https://nasm.us>`_ syntax) and Python
for development tooling.
jsix can be found `on GitHub <https://github.com/justinian/jsix>`_, and is
released under the terms of the `MPL 2.0 <https://mozilla.org/MPL/2.0/>`_.
.. admonition:: A note on the name
This kernel was originally named Popcorn, but I have since discovered that
the Popcorn Linux project is also developing a kernel with that name,
started around the same time as this project. So I've renamed this kernel
jsix as an homage to L4, xv6, and my wonderful wife.
The name jsix is always styled *jsix* or ``j6``, never capitalized.
.. [#] jsix aims to support amd64 (x86_64) CPUs released in the last 10 years.
Current Features
----------------
The jsix kernel is quite far along now, but the userland systems are still lacking.
- Platform: |amd64|
- UEFI bootloader
- Multi-core & multi-tasking microkernel
- Work-stealing SMP scheduler
- Pluggable panic handler modules
- Capability-style object-oriented syscall API
- Custom IDL for specifying and documenting syscalls
- Virtual memory based on sharable Virtual Memory Area objects (VMAs)
- Kernel API library (libj6), also provides features built on kernel primitives:
- Channels (async stream IPC) built on shared memory and futexes
- Ring buffers via doubly-mapped pages
- Custom libc
- Runtime dynamic linker
- Init service
- Built-in VFS service for the initrd
- ELF loader
- Service-lookup protocol service
- Userland UART driver
- Userland UEFI framebuffer driver
- Userland kernel log output service
- Userland unit test runner
- Build configuration system (bonnibel)
.. toctree::
:maxdepth: 1
:caption: Site Contents:
syscall_interface
* :ref:`genindex`
* :ref:`search`

12
docs/jsix_transparent.svg Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="176.562 356.069 211.11 113" width="211.11pt" height="113pt">
<g>
<g>
<g>
<path d=" M 212.981 372.36 L 219.564 376.16 L 226.147 379.961 L 226.147 387.563 L 226.147 395.164 L 219.564 398.965 L 212.981 402.766 L 206.398 398.965 L 199.815 395.164 L 199.815 387.563 L 199.815 379.961 L 206.398 376.16 L 212.981 372.36 L 212.981 372.36 L 212.981 372.36 Z M 256.292 397.366 L 262.875 401.166 L 269.458 404.967 L 269.458 412.569 L 269.458 420.17 L 262.875 423.971 L 256.292 427.772 L 249.709 423.971 L 243.126 420.17 L 243.126 412.569 L 243.126 404.967 L 249.709 401.166 L 256.292 397.366 L 256.292 397.366 Z M 183.622 387.283 L 205.52 374.64 L 227.418 361.997 L 249.316 374.64 L 271.214 387.283 L 271.214 412.569 L 271.214 437.854 L 249.316 450.497 L 227.418 463.14 L 205.52 450.497 L 183.622 437.854 L 183.622 412.569 L 183.622 387.283 L 183.622 387.283 L 183.622 387.283 Z M 241.855 372.36 L 248.438 376.16 L 255.021 379.961 L 255.021 387.563 L 255.021 395.164 L 248.438 398.965 L 241.855 402.766 L 235.272 398.965 L 228.689 395.164 L 228.689 387.563 L 228.689 379.961 L 235.272 376.16 L 241.855 372.36 Z " fill-rule="evenodd" fill="rgb(49,79,128)"/>
<path d=" M 298.642 379.579 L 291.621 379.579 L 291.621 372.558 L 298.642 372.558 L 298.642 379.579 Z M 285.214 446.718 L 285.214 441.452 L 287.32 441.452 L 287.32 441.452 Q 289.339 441.452 290.524 440.092 L 290.524 440.092 L 290.524 440.092 Q 291.708 438.731 291.708 436.625 L 291.708 436.625 L 291.708 387.039 L 298.729 387.039 L 298.729 436.011 L 298.729 436.011 Q 298.729 440.925 295.921 443.822 L 295.921 443.822 L 295.921 443.822 Q 293.113 446.718 288.286 446.718 L 288.286 446.718 L 285.214 446.718 Z M 306.628 432.676 L 306.628 427.41 L 314.088 427.41 L 314.088 427.41 Q 317.862 427.41 319.573 425.347 L 319.573 425.347 L 319.573 425.347 Q 321.285 423.285 321.285 419.95 L 321.285 419.95 L 321.285 419.95 Q 321.285 417.317 319.705 415.474 L 319.705 415.474 L 319.705 415.474 Q 318.125 413.631 314.966 411.174 L 314.966 411.174 L 314.966 411.174 Q 312.245 408.98 310.621 407.356 L 310.621 407.356 L 310.621 407.356 Q 308.998 405.732 307.813 403.319 L 307.813 403.319 L 307.813 403.319 Q 306.628 400.905 306.628 397.746 L 306.628 397.746 L 306.628 397.746 Q 306.628 393.095 309.744 390.067 L 309.744 390.067 L 309.744 390.067 Q 312.859 387.039 318.125 387.039 L 318.125 387.039 L 325.76 387.039 L 325.76 392.305 L 319.441 392.305 L 319.441 392.305 Q 313.21 392.305 313.21 398.185 L 313.21 398.185 L 313.21 398.185 Q 313.21 400.467 314.615 402.134 L 314.615 402.134 L 314.615 402.134 Q 316.019 403.802 319.003 406.083 L 319.003 406.083 L 319.003 406.083 Q 321.723 408.19 323.479 409.901 L 323.479 409.901 L 323.479 409.901 Q 325.234 411.613 326.463 414.202 L 326.463 414.202 L 326.463 414.202 Q 327.691 416.791 327.691 420.301 L 327.691 420.301 L 327.691 420.301 Q 327.691 426.532 324.4 429.604 L 324.4 429.604 L 324.4 429.604 Q 321.109 432.676 315.141 432.676 L 315.141 432.676 L 306.628 432.676 Z M 342.611 379.579 L 335.59 379.579 L 335.59 372.558 L 342.611 372.558 L 342.611 379.579 Z M 342.611 432.676 L 335.59 432.676 L 335.59 387.039 L 342.611 387.039 L 342.611 432.676 Z M 356.126 432.676 L 348.754 432.676 L 361.392 409.77 L 349.632 387.039 L 356.39 387.039 L 364.639 403.187 L 372.977 387.039 L 379.735 387.039 L 367.974 409.77 L 380.612 432.676 L 373.24 432.676 L 364.639 416.001 L 356.126 432.676 Z " fill="rgb(49,79,128)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

7
docs/modd.conf Normal file
View File

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

5
docs/requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
cogapp >= 3
pyyaml >= 5.4
lark == 0.12.0
sphinx
renku-sphinx-theme

436
docs/syscall_interface.rst Normal file
View File

@@ -0,0 +1,436 @@
.. jsix syscall interface.
.. Automatically update from the definition files using cog!
.. [[[cog code generation
.. from textwrap import indent
.. from definitions.context import Context
..
.. ctx = Context(definitions_path)
.. ctx.parse("syscalls.def")
.. syscalls = ctx.interfaces["syscalls"]
..
.. def caplist(caps):
.. return ', '.join([f"``{c}``" for c in caps])
.. ]]]
.. [[[end]]] (checksum: d41d8cd98f00b204e9800998ecf8427e)
The jsix syscall interface
==========================
The jsix kernel's syscall design is based around object handles. Object handles
are also a collections capabilities, encoding certain rights over the object
they reference.
Very few syscalls in jsix can be made without some handle, and most of them are
requests to the kernel to create a given kind of object. This is analogous to
methods on an object in an object-oriented programming language.
.. [[[cog code generation
.. cog.outl()
.. for obj in syscalls.exposes:
.. cog.outl(f"``{obj.name}`` syscalls")
.. cog.outl(f"-------------------------")
.. desc = obj.desc or "Undocumented"
.. cog.outl(desc)
.. cog.outl()
.. cog.outl(f":capabilites: {caplist(obj.caps)}")
.. cog.outl()
.. for method in obj.methods:
.. args = []
.. if method.constructor:
.. args.append("j6_handle_t *self")
.. elif not method.static:
.. args.append("j6_handle_t self")
..
.. for param in method.params:
.. for type, suffix in param.type.c_names(param.options):
.. args.append(f"{type} {param.name}{suffix}")
..
.. cog.outl(f".. cpp:function:: j6_result_t j6_{obj.name}_{method.name} ({', '.join(args)})")
.. cog.outl()
.. desc = method.desc or "Undocumented"
.. cog.outl(indent(desc, " "))
.. cog.outl()
.. if "cap" in method.options:
.. cog.outl(f" :capabilities: {caplist(method.options['cap'])}")
.. cog.outl()
.. if method.constructor:
.. cog.outl(f" :param self: *[out]* Handle to the new {obj.name} object")
.. elif not method.static:
.. cog.outl(f" :param self: Handle to the {obj.name} object")
.. for param in method.params:
.. opts = param.options and f"*[{', '.join(param.options)}]*" or ""
.. desc = param.desc or 'Undocumented'
.. cog.outl(f" :param {param.name}: {opts} {desc}")
.. cog.outl()
.. ]]]
``object`` syscalls
-------------------------
All kernel-exposed objects inherit from the base ``object`` type, so the
``object`` syscalls can be used with any object's handle.
:capabilites: ``clone``
.. cpp:function:: j6_result_t j6_object_koid (j6_handle_t self, uint64_t * koid)
Get the internal kernel object id of an object
:param self: Handle to the object object
:param koid: *[out]* Undocumented
``event`` syscalls
-------------------------
An ``event`` is a simple synchronization object. It contains up to 64 signals
that threads can wait for and signal in parallel.
:capabilites: ``signal``, ``wait``
.. cpp:function:: j6_result_t j6_event_create (j6_handle_t *self)
Undocumented
:param self: *[out]* Handle to the new event object
.. cpp:function:: j6_result_t j6_event_signal (j6_handle_t self, uint64_t signals)
Signal events on this object
:capabilities: ``signal``
:param self: Handle to the event object
:param signals: A bitset of which events to signal
.. cpp:function:: j6_result_t j6_event_wait (j6_handle_t self, uint64_t * signals, uint64_t timeout)
Wait for signaled events on this object
:capabilities: ``wait``
:param self: Handle to the event object
:param signals: *[out]* A bitset of which events were signaled
:param timeout: Wait timeout in nanoseconds
``mailbox`` syscalls
-------------------------
Mailboxes are objects that enable synchronous IPC via arbitrary
message-passing of tagged data and/or handles. Not as efficient
as shared memory channels, but more flexible.
:capabilites: ``send``, ``receive``, ``close``
.. cpp:function:: j6_result_t j6_mailbox_create (j6_handle_t *self)
Undocumented
:param self: *[out]* Handle to the new mailbox object
.. cpp:function:: j6_result_t j6_mailbox_close (j6_handle_t self)
Undocumented
:capabilities: ``close``
:param self: Handle to the mailbox object
.. cpp:function:: j6_result_t j6_mailbox_call (j6_handle_t self, uint64_t * tag, void * data, size_t * data_len, size_t data_in_len, j6_handle_t * handles, size_t * handles_count)
Send a message to the reciever, and block until a response is
sent. Note that getting this response does not require the
receive capability.
:capabilities: ``send``
:param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input
:param handles: *[optional, inout, handle, list]* Undocumented
.. 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)
Respond to a message sent using call, and wait for another
message to arrive. Note that this does not require the send
capability. A reply tag of 0 skips the reply and goes directly
to waiting for a new message.
:capabilities: ``receive``
:param self: Handle to the mailbox object
:param tag: *[inout]* Undocumented
:param data: *[optional, inout]* Undocumented
:param data_in_len: number of bytes in data used for input
:param handles: *[optional, inout, handle, list]* Undocumented
:param reply_tag: *[inout]* Undocumented
:param flags: Undocumented
``process`` syscalls
-------------------------
A ``process`` object represents a process running on the system, and allows
control over the threads, handles, and virtual memory space of that process.
:capabilites: ``kill``, ``create_thread``
.. cpp:function:: j6_result_t j6_process_create (j6_handle_t *self)
Create a new empty process
:param self: *[out]* Handle to the new process object
.. cpp:function:: j6_result_t j6_process_kill (j6_handle_t self)
Stop all threads and exit the given process
:capabilities: ``kill``
:param self: Handle to the process object
.. cpp:function:: j6_result_t j6_process_exit (int32_t result)
Stop all threads and exit the current process
:param result: The result to retrun to the parent process
.. cpp:function:: j6_result_t j6_process_give_handle (j6_handle_t self, j6_handle_t target)
Give the given process a handle that points to the same
object as the specified handle.
:param self: Handle to the process object
:param target: *[handle]* A handle in the caller process to send
``system`` syscalls
-------------------------
The singular ``system`` object represents a handle to kernel functionality
needed by drivers and other priviledged services.
:capabilites: ``get_log``, ``bind_irq``, ``map_phys``, ``change_iopl``
.. cpp:function:: j6_result_t j6_system_get_log (j6_handle_t self, uint64_t seen, void * buffer, size_t * buffer_len)
Get the next log line from the kernel log
:capabilities: ``get_log``
:param self: Handle to the system object
:param seen: Last seen log id
:param buffer: *[out, zero_ok]* Buffer for the log message data structure
.. cpp:function:: j6_result_t j6_system_bind_irq (j6_handle_t self, j6_handle_t dest, unsigned irq, unsigned signal)
Ask the kernel to send this process messages whenever
the given IRQ fires
:capabilities: ``bind_irq``
:param self: Handle to the system object
:param dest: Event object that will receive messages
:param irq: IRQ number to bind
:param signal: Signal number on the event to bind to
.. cpp:function:: j6_result_t j6_system_map_phys (j6_handle_t self, j6_handle_t * area, uintptr_t phys, size_t size, uint32_t flags)
Create a VMA and map an area of physical memory into it,
also mapping that VMA into the current process
:capabilities: ``map_phys``
:param self: Handle to the system object
:param area: *[out]* Receives a handle to the VMA created
:param phys: The physical address of the area
:param size: Size of the area, in bytes
:param flags: Flags to apply to the created VMA
.. cpp:function:: j6_result_t j6_system_request_iopl (j6_handle_t self, unsigned iopl)
Request the kernel change the IOPL for this process. The only values
that make sense are 0 and 3.
:capabilities: ``change_iopl``
:param self: Handle to the system object
:param iopl: The IOPL to set for this process
``thread`` syscalls
-------------------------
A ``thread`` object represents a thread of execution within a process running
on the system. The actual thread does not need to be currently running to
hold a handle to it.
:capabilites: ``kill``, ``join``
.. cpp:function:: j6_result_t j6_thread_create (j6_handle_t *self, j6_handle_t process, uintptr_t stack_top, uintptr_t entrypoint, uint64_t arg0, uint64_t arg1)
Undocumented
:param self: *[out]* Handle to the new thread object
:param process: *[optional, cap]* Undocumented
:param stack_top: Undocumented
:param entrypoint: Undocumented
:param arg0: Undocumented
:param arg1: Undocumented
.. cpp:function:: j6_result_t j6_thread_kill (j6_handle_t self)
Undocumented
:capabilities: ``kill``
:param self: Handle to the thread object
.. cpp:function:: j6_result_t j6_thread_join (j6_handle_t self)
Undocumented
:capabilities: ``join``
:param self: Handle to the thread object
.. cpp:function:: j6_result_t j6_thread_exit ()
Undocumented
.. cpp:function:: j6_result_t j6_thread_sleep (uint64_t duration)
Undocumented
:param duration: Undocumented
``vma`` syscalls
-------------------------
A ``vma`` object represents a single virtual memory area, which may be shared
between several processes. A process having a handle to a ``vma`` does not
necessarily mean that it is mapped into that process' virtual memory space.
:capabilites: ``map``, ``unmap``, ``resize``
.. cpp:function:: j6_result_t j6_vma_create (j6_handle_t *self, size_t size, uint32_t flags)
Undocumented
:param self: *[out]* Handle to the new vma object
:param size: Undocumented
:param flags: Undocumented
.. cpp:function:: j6_result_t j6_vma_create_map (j6_handle_t *self, size_t size, uintptr_t * address, uint32_t flags)
Undocumented
:capabilities: ``map``
:param self: *[out]* Handle to the new vma object
:param size: Undocumented
:param address: *[inout]* Undocumented
:param flags: Undocumented
.. cpp:function:: j6_result_t j6_vma_map (j6_handle_t self, j6_handle_t process, uintptr_t * address, uint32_t flags)
Undocumented
:capabilities: ``map``
:param self: Handle to the vma object
:param process: *[optional]* Undocumented
:param address: *[inout]* Undocumented
:param flags: Undocumented
.. cpp:function:: j6_result_t j6_vma_unmap (j6_handle_t self, j6_handle_t process)
Undocumented
:capabilities: ``unmap``
:param self: Handle to the vma object
:param process: *[optional]* Undocumented
.. cpp:function:: j6_result_t j6_vma_resize (j6_handle_t self, size_t * size)
Undocumented
:capabilities: ``resize``
:param self: Handle to the vma object
:param size: *[inout]* New size for the VMA, or 0 to query the current size without changing
.. [[[end]]] (checksum: fe448e541c009a1bcf8bdc44f4760e32)
Non-object syscalls
-------------------
The following are the system calls that aren't constructors for objects, and
either do not require an object handle, or operate generically on handles.
.. [[[cog code generation
.. cog.outl()
.. for func in syscalls.functions:
.. args = []
.. for param in func.params:
.. for type, suffix in param.type.c_names(param.options):
.. args.append(f"{type} {param.name}{suffix}")
..
.. cog.outl(f".. cpp:function:: j6_result_t j6_{func.name} ({', '.join(args)})")
.. cog.outl()
.. desc = func.desc or "Undocumented"
.. cog.outl(indent(desc, " "))
.. cog.outl()
.. for param in func.params:
.. opts = param.options and f"*[{', '.join(param.options)}]*" or ""
.. desc = param.desc or 'Undocumented'
.. cog.outl(f" :param {param.name}: {opts} {desc}")
.. cog.outl()
.. ]]]
.. cpp:function:: j6_result_t j6_noop ()
Simple no-op syscall for testing
.. cpp:function:: j6_result_t j6_log (uint8_t area, uint8_t severity, const char * message)
Write a message to the kernel log
:param area: Undocumented
:param severity: Undocumented
:param message: Undocumented
.. cpp:function:: j6_result_t j6_handle_list (struct j6_handle_descriptor * handles, size_t * handles_size)
Get a list of handles owned by this process. If the
supplied list is not big enough, will set the size
needed in `size` and return j6_err_insufficient
:param handles: *[list, inout, zero_ok]* A list of handles to be filled
.. cpp:function:: j6_result_t j6_handle_clone (j6_handle_t orig, j6_handle_t * clone, uint32_t mask)
Create a clone of an existing handle, possibly with
some capabilities masked out.
:param orig: *[handle, cap]* The handle to clone
:param clone: *[out]* The new handle
:param mask: The capability bitmask
.. cpp:function:: j6_result_t j6_futex_wait (const uint32_t * address, uint32_t current, uint64_t timeout)
Block waiting on a futex
:param address: Address of the futex value
:param current: Current value of the futex
:param timeout: Wait timeout in nanoseconds
.. cpp:function:: j6_result_t j6_futex_wake (const uint32_t * address, uint64_t count)
Wake threads waiting on a futex
:param address: Address of the futex value
:param count: Number of threads to wake, or 0 for all
.. cpp:function:: j6_result_t j6_test_finish (uint32_t exit_code)
Testing mode only: Have the kernel finish and exit QEMU with the given exit code
:param exit_code: Undocumented
.. [[[end]]] (checksum: b8b12e99a4a00c99b3859f05000a7bfd)

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ read_string(util::buffer &data)
static void static void
read_descriptor(descriptor &e, util::buffer &data) read_descriptor(descriptor &e, util::buffer &data)
{ {
e.flags = static_cast<desc_flags>(*util::read<uint16_t>(data)); e.flags = util::bitset16 {*util::read<uint16_t>(data)};
e.path = read_string(data); e.path = read_string(data);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -71,21 +71,19 @@ lapic::get_id()
} }
void void
lapic::send_ipi(ipi mode, isr vector, uint8_t dest) lapic::send_ipi(util::bitset32 mode, isr vector, uint8_t dest)
{ {
// Wait until the APIC is ready to send // Wait until the APIC is ready to send
ipi_wait(); ipi_wait();
uint32_t command = uint32_t command = util::bitset32::from(vector) | mode;
static_cast<uint32_t>(vector) |
static_cast<uint32_t>(mode);
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24); apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
apic_write(m_base, lapic_icr_low, command); apic_write(m_base, lapic_icr_low, command);
} }
void void
lapic::send_ipi_broadcast(ipi mode, bool self, isr vector) lapic::send_ipi_broadcast(util::bitset32 mode, bool self, isr vector)
{ {
// Wait until the APIC is ready to send // Wait until the APIC is ready to send
ipi_wait(); ipi_wait();

View File

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

View File

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

View File

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

View File

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

View File

@@ -123,16 +123,18 @@ isr_handler(cpu_state *regs)
uintptr_t cr2 = 0; uintptr_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
bool user = cr2 < mem::kernel_offset; // The zero page is always invalid
vm_space::fault_type ft = if (cr2 > mem::frame_size) {
static_cast<vm_space::fault_type>(regs->errorcode); bool user = cr2 < mem::kernel_offset;
util::bitset8 ft = regs->errorcode;
vm_space &space = user vm_space &space = user
? obj::process::current().space() ? obj::process::current().space()
: vm_space::kernel_space(); : vm_space::kernel_space();
if (cr2 && space.handle_fault(cr2, ft)) if (cr2 && space.handle_fault(cr2, ft))
break; break;
}
util::format({message, sizeof(message)}, util::format({message, sizeof(message)},
"Page fault: %016lx%s%s%s%s%s", cr2, "Page fault: %016lx%s%s%s%s%s", cr2,

View File

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

View File

@@ -5,14 +5,29 @@
#include <stdint.h> #include <stdint.h>
#include <j6/types.h> #include <j6/types.h>
#include <util/counted.h> #include <util/counted.h>
#include <util/pointers.h>
namespace ipc { namespace ipc {
static constexpr size_t message_size = 64;
struct message struct message
{ {
uint64_t tag; uint64_t tag;
util::buffer data; uint16_t data_size;
util::counted<j6_handle_t> handles;
uint16_t handle_count : 4;
uint16_t out_of_band : 1;
uint32_t _reserved;
uint8_t content[ message_size - 8 ];
util::buffer data();
util::const_buffer data() const;
util::counted<j6_handle_t> handles();
util::counted<const j6_handle_t> handles() const;
message(); message();
message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles); message(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
@@ -20,6 +35,13 @@ struct message
~message(); ~message();
message & operator=(message &&other); message & operator=(message &&other);
void set(uint64_t in_tag, const util::buffer &in_data, const util::counted<j6_handle_t> &in_handles);
private:
void clear_oob();
}; };
} // namespace ipc using message_ptr = util::unique_ptr<message>;
} // namespace ipc

View File

@@ -1,4 +1,3 @@
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <j6/memutils.h> #include <j6/memutils.h>
@@ -54,7 +53,7 @@ kernel_main(bootproto::args *args)
bsp_late_init(); bsp_late_init();
using bootproto::boot_flags; using bootproto::boot_flags;
bool enable_test = util::bits::has(args->flags, boot_flags::test); bool enable_test = args->flags.get(boot_flags::test);
syscall_initialize(enable_test); syscall_initialize(enable_test);
device_manager &devices = device_manager::get(); device_manager &devices = device_manager::get();
@@ -95,12 +94,15 @@ load_init_server(bootproto::program &program, uintptr_t modules_address)
vm_space &space = p->space(); vm_space &space = p->space();
for (const auto &sect : program.sections) { for (const auto &sect : program.sections) {
vm_flags flags = util::bitset32 flags = util::bitset32::of(vm_flags::exact);
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) | if (sect.type.get(section_flags::execute))
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none); flags.set(vm_flags::exec);
if (sect.type.get(section_flags::write))
flags.set(vm_flags::write);
obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags); obj::vm_area *vma = new obj::vm_area_fixed(sect.phys_addr, sect.size, flags);
space.add(sect.virt_addr, vma, obj::vm_flags::exact); space.add(sect.virt_addr, vma, flags);
} }
uint64_t iopl = (3ull << 12); uint64_t iopl = (3ull << 12);

View File

@@ -124,7 +124,8 @@ LOG_LEVEL_FUNCTION(info);
LOG_LEVEL_FUNCTION(warn); LOG_LEVEL_FUNCTION(warn);
LOG_LEVEL_FUNCTION(error); LOG_LEVEL_FUNCTION(error);
void fatal(logs area, const char *fmt, ...) void
fatal(logs area, const char *fmt, ...)
{ {
logger *l = logger::s_log; logger *l = logger::s_log;
if (!l) return; if (!l) return;
@@ -137,4 +138,19 @@ void fatal(logs area, const char *fmt, ...)
kassert(false, "log::fatal"); kassert(false, "log::fatal");
} }
void
log(logs area, level severity, const char *fmt, ...)
{
logger *l = logger::s_log;
if (!l) return;
level limit = l->get_level(area);
if (severity > limit) return;
va_list args;
va_start(args, fmt);
l->output(severity, area, fmt, args);
va_end(args);
}
} // namespace log } // namespace log

View File

@@ -77,6 +77,7 @@ private:
friend void warn (logs area, const char *fmt, ...); friend void warn (logs area, const char *fmt, ...);
friend void error (logs area, const char *fmt, ...); friend void error (logs area, const char *fmt, ...);
friend void fatal (logs area, const char *fmt, ...); friend void fatal (logs area, const char *fmt, ...);
friend void log (logs area, level severity, const char *fmt, ...);
void output(level severity, logs area, const char *fmt, va_list args); void output(level severity, logs area, const char *fmt, va_list args);
@@ -113,6 +114,8 @@ void warn (logs area, const char *fmt, ...);
void error (logs area, const char *fmt, ...); void error (logs area, const char *fmt, ...);
void fatal (logs area, const char *fmt, ...); void fatal (logs area, const char *fmt, ...);
void log (logs area, level severity, const char *fmt, ...);
} // namespace log } // namespace log
extern log::logger &g_logger; extern log::logger &g_logger;

View File

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

View File

@@ -2,6 +2,7 @@
#include <util/counted.h> #include <util/counted.h>
#include <j6/memutils.h> #include <j6/memutils.h>
#include "logger.h"
#include "objects/mailbox.h" #include "objects/mailbox.h"
#include "objects/thread.h" #include "objects/thread.h"
@@ -26,6 +27,8 @@ mailbox::close()
bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL); bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL);
if (was_closed) return; if (was_closed) return;
log::spam(logs::ipc, "mbx[%2x] closing...", obj_id());
m_callers.clear(j6_status_closed); m_callers.clear(j6_status_closed);
m_responders.clear(j6_status_closed); m_responders.clear(j6_status_closed);
@@ -44,14 +47,20 @@ mailbox::call()
m_callers.add_thread(&current); m_callers.add_thread(&current);
thread *responder = m_responders.pop_next(); thread *responder = m_responders.pop_next();
if (responder) if (responder) {
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] call() waking thread[%2x]...",
current.obj_id(), obj_id(), responder->obj_id());
responder->wake(j6_status_ok); responder->wake(j6_status_ok);
} else {
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] call() found no responder yet.",
current.obj_id(), obj_id());
}
return current.block(); return current.block();
} }
j6_status_t j6_status_t
mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block) mailbox::receive(ipc::message_ptr &data, reply_tag_t &reply_tag, bool block)
{ {
if (closed()) if (closed())
return j6_status_closed; return j6_status_closed;
@@ -67,6 +76,9 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
if (!block) if (!block)
return j6_status_would_block; return j6_status_would_block;
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] receive() blocking waiting for a caller",
current.obj_id(), obj_id());
m_responders.add_thread(&current); m_responders.add_thread(&current);
j6_status_t s = current.block(); j6_status_t s = current.block();
if (s != j6_status_ok) if (s != j6_status_ok)
@@ -78,12 +90,15 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
m_reply_map.insert({ reply_tag, caller }); m_reply_map.insert({ reply_tag, caller });
lock.release(); lock.release();
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] receive() found caller thread[%2x], rt = %x",
current.obj_id(), obj_id(), caller->obj_id(), reply_tag);
data = caller->get_message_data(); data = caller->get_message_data();
return j6_status_ok; return j6_status_ok;
} }
j6_status_t j6_status_t
mailbox::reply(reply_tag_t reply_tag, ipc::message &&data) mailbox::reply(reply_tag_t reply_tag, ipc::message_ptr data)
{ {
if (closed()) if (closed())
return j6_status_closed; return j6_status_closed;
@@ -97,6 +112,10 @@ mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
m_reply_map.erase(reply_tag); m_reply_map.erase(reply_tag);
lock.release(); lock.release();
thread &current = thread::current();
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] reply() to caller thread[%2x], rt = %x",
current.obj_id(), obj_id(), caller->obj_id(), reply_tag);
caller->set_message_data(util::move(data)); caller->set_message_data(util::move(data));
caller->wake(j6_status_ok); caller->wake(j6_status_ok);
return j6_status_ok; return j6_status_ok;

View File

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

View File

@@ -46,7 +46,7 @@ process::create_kernel_process(page_table *pml4)
} }
void void
process::exit(int32_t code) process::exit(int64_t code)
{ {
if (m_state == state::exited) if (m_state == state::exited)
return; return;

View File

@@ -42,7 +42,7 @@ public:
/// Terminate this process. /// Terminate this process.
/// \arg code The return code to exit with. /// \arg code The return code to exit with.
void exit(int32_t code); void exit(int64_t code);
/// Get the process' virtual memory space /// Get the process' virtual memory space
vm_space & space() { return m_space; } vm_space & space() { return m_space; }
@@ -95,7 +95,7 @@ private:
// This constructor is called by create_kernel_process // This constructor is called by create_kernel_process
process(page_table *kpml4); process(page_table *kpml4);
int32_t m_return_code; int64_t m_return_code;
vm_space m_space; vm_space m_space;

View File

@@ -111,9 +111,6 @@ thread::wake_only()
set_state(state::ready); set_state(state::ready);
} }
void thread::set_message_data(ipc::message &&md) { m_message = util::move(md); }
ipc::message && thread::get_message_data() { return util::move(m_message); }
void void
thread::exit() thread::exit()
{ {

View File

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

View File

@@ -9,7 +9,7 @@ namespace obj {
using mem::frame_size; using mem::frame_size;
vm_area::vm_area(size_t size, vm_flags flags) : vm_area::vm_area(size_t size, util::bitset32 flags) :
m_size {size}, m_size {size},
m_flags {flags}, m_flags {flags},
m_spaces {m_vector_static, 0, static_size}, m_spaces {m_vector_static, 0, static_size},
@@ -62,7 +62,7 @@ vm_area::can_resize(size_t size)
return true; return true;
} }
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) : vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, util::bitset32 flags) :
m_start {start}, m_start {start},
vm_area {size, flags} vm_area {size, flags}
{ {
@@ -70,7 +70,7 @@ vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
vm_area_fixed::~vm_area_fixed() vm_area_fixed::~vm_area_fixed()
{ {
if (m_flags && vm_flags::mmio) if (m_flags.get(vm_flags::mmio))
return; return;
size_t pages = mem::page_count(m_size); size_t pages = mem::page_count(m_size);
@@ -94,7 +94,7 @@ vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
return true; return true;
} }
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) : vm_area_untracked::vm_area_untracked(size_t size, util::bitset32 flags) :
vm_area {size, flags} vm_area {size, flags}
{ {
} }
@@ -127,7 +127,7 @@ vm_area_untracked::add_to(vm_space *space)
} }
vm_area_open::vm_area_open(size_t size, vm_flags flags) : vm_area_open::vm_area_open(size_t size, util::bitset32 flags) :
m_mapped {nullptr}, m_mapped {nullptr},
vm_area {size, flags} vm_area {size, flags}
{ {
@@ -154,7 +154,7 @@ vm_area_open::add_existing(uintptr_t offset, uintptr_t phys)
} }
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, vm_flags flags) : vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, util::bitset32 flags) :
m_pages {buf_pages + 1}, // Sections are N+1 pages for the leading guard page m_pages {buf_pages + 1}, // Sections are N+1 pages for the leading guard page
m_stacks {start, m_pages*mem::frame_size}, m_stacks {start, m_pages*mem::frame_size},
vm_area_open {size, flags} vm_area_open {size, flags}
@@ -191,7 +191,7 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
return vm_area_open::get_page(offset, phys, alloc); return vm_area_open::get_page(offset, phys, alloc);
} }
vm_area_ring::vm_area_ring(size_t size, vm_flags flags) : vm_area_ring::vm_area_ring(size_t size, util::bitset32 flags) :
vm_area_open {size * 2, flags}, vm_area_open {size * 2, flags},
m_bufsize {size} m_bufsize {size}
{ {

View File

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

View File

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

View File

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

View File

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

View File

@@ -57,8 +57,11 @@ start(cpu_data &bsp, void *kpml4)
// Copy the startup code somwhere the real mode trampoline can run // Copy the startup code somwhere the real mode trampoline can run
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
isr vector = static_cast<isr>(addr >> 12); isr vector = static_cast<isr>(addr >> 12);
obj::vm_area *vma = new obj::vm_area_fixed(addr, 0x1000, vm_flags::write);
vm_space::kernel_space().add(addr, vma, obj::vm_flags::exact); constexpr util::bitset32 flags = util::bitset32::of(vm_flags::write, vm_flags::exact);
obj::vm_area *vma = new obj::vm_area_fixed(addr, 0x1000, flags);
vm_space::kernel_space().add(addr, vma, flags);
memcpy( memcpy(
reinterpret_cast<void*>(addr), reinterpret_cast<void*>(addr),
reinterpret_cast<void*>(&ap_startup), reinterpret_cast<void*>(&ap_startup),
@@ -67,7 +70,7 @@ start(cpu_data &bsp, void *kpml4)
size_t free_stack_count = 0; size_t free_stack_count = 0;
lapic &apic = *bsp.apic; lapic &apic = *bsp.apic;
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert; util::bitset32 mode = lapic::ipi_init + lapic::ipi_flags::level + lapic::ipi_flags::assert;
apic.send_ipi_broadcast(mode, false, static_cast<isr>(0)); apic.send_ipi_broadcast(mode, false, static_cast<isr>(0));
for (uint8_t id : ids) { for (uint8_t id : ids) {
@@ -90,7 +93,7 @@ start(cpu_data &bsp, void *kpml4)
size_t current_count = ap_startup_count; size_t current_count = ap_startup_count;
log::verbose(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end); log::verbose(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert; util::bitset32 startup = lapic::ipi_sipi + lapic::ipi_flags::assert;
apic.send_ipi(startup, vector, id); apic.send_ipi(startup, vector, id);
for (unsigned i = 0; i < 20; ++i) { for (unsigned i = 0; i < 20; ++i) {

View File

@@ -32,4 +32,13 @@ handle_clone(j6_handle_t orig, j6_handle_t *clone, uint32_t mask)
return j6_status_ok; return j6_status_ok;
} }
j6_status_t
handle_close(j6_handle_t hnd)
{
process &p = process::current();
p.remove_handle(hnd);
return j6_status_ok;
}
} // namespace syscalls } // namespace syscalls

View File

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

View File

@@ -29,10 +29,10 @@ process_kill(process *self)
} }
j6_status_t j6_status_t
process_exit(int32_t status) process_exit(int64_t status)
{ {
process &p = process::current(); process &p = process::current();
log::info(logs::task, "Process <%02lx> exiting with code %d", p.obj_id(), status); log::info(logs::task, "Process <%02lx> exiting with code %#lx", p.obj_id(), status);
p.exit(status); p.exit(status);

View File

@@ -21,10 +21,11 @@ namespace syscalls {
using system = class ::system; using system = class ::system;
j6_status_t j6_status_t
log(const char *message) log(uint8_t area, uint8_t severity, const char *message)
{ {
thread &th = thread::current(); thread &th = thread::current();
log::info(logs::syscall, "Message <%02lx:%02lx>: %s", th.parent().obj_id(), th.obj_id(), message); log::log(static_cast<logs>(area), static_cast<log::level>(severity),
"<%02lx:%02lx>: %s", th.parent().obj_id(), th.obj_id(), message);
return j6_status_ok; return j6_status_ok;
} }
@@ -66,12 +67,11 @@ system_map_phys(system *self, j6_handle_t * area, uintptr_t phys, size_t size, u
{ {
// TODO: check to see if frames are already used? How would that collide with // TODO: check to see if frames are already used? How would that collide with
// the bootloader's allocated pages already being marked used? // the bootloader's allocated pages already being marked used?
if (!(flags & vm_flags::mmio)) util::bitset32 f = flags & vm_driver_mask;
if (!f.get(vm_flags::mmio))
frame_allocator::get().used(phys, mem::page_count(size)); frame_allocator::get().used(phys, mem::page_count(size));
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask); construct_handle<vm_area_fixed>(area, phys, size, f);
construct_handle<vm_area_fixed>(area, phys, size, vmf);
return j6_status_ok; return j6_status_ok;
} }

View File

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

View File

@@ -55,9 +55,9 @@ vm_space::vm_space() :
obj::vm_area *sysc = new obj::vm_area_fixed( obj::vm_area *sysc = new obj::vm_area_fixed(
g_sysconf_phys, g_sysconf_phys,
sizeof(system_config), sizeof(system_config),
vm_flags::none); 0);
add(sysconf_user_address, sysc, vm_flags::exact); add(sysconf_user_address, sysc, util::bitset32::of(vm_flags::exact));
} }
vm_space::~vm_space() vm_space::~vm_space()
@@ -81,21 +81,20 @@ vm_space::kernel_space()
} }
uintptr_t uintptr_t
vm_space::add(uintptr_t base, obj::vm_area *area, obj::vm_flags flags) vm_space::add(uintptr_t base, obj::vm_area *area, util::bitset32 flags)
{ {
if (!base) if (!base)
base = min_auto_address; base = min_auto_address;
uintptr_t end = base + area->size();
//TODO: optimize find/insert //TODO: optimize find/insert
bool exact = util::bits::has(flags, j6_vm_flag_exact); bool exact = flags.get(vm_flags::exact);
for (size_t i = 0; i < m_areas.count(); ++i) { for (size_t i = 0; i < m_areas.count(); ++i) {
const vm_space::area &a = m_areas[i]; const vm_space::area &a = m_areas[i];
uintptr_t aend = a.base + a.area->size(); uintptr_t aend = a.base + a.area->size();
if (base >= aend) if (base >= aend)
continue; continue;
uintptr_t end = base + area->size();
if (end <= a.base) if (end <= a.base)
break; break;
else if (exact) else if (exact)
@@ -192,7 +191,7 @@ vm_space::copy_from(const vm_space &source, const obj::vm_area &vma)
while (count--) { while (count--) {
uint64_t &e = dit.entry(page_table::level::pt); uint64_t &e = dit.entry(page_table::level::pt);
if (e & page_table::flag::present) { if (util::bitset64::from(e) & page_flags::present) {
// TODO: handle clobbering mapping // TODO: handle clobbering mapping
} }
e = sit.entry(page_table::level::pt); e = sit.entry(page_table::level::pt);
@@ -210,11 +209,11 @@ vm_space::page_in(const obj::vm_area &vma, uintptr_t offset, uintptr_t phys, siz
return; return;
uintptr_t virt = base + offset; uintptr_t virt = base + offset;
page_table::flag flags = util::bitset64 flags =
page_table::flag::present | page_flags::present |
(m_kernel ? page_table::flag::none : page_table::flag::user) | (m_kernel ? page_flags::none : page_flags::user) |
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) | (vma.flags().get(vm_flags::write) ? page_flags::write : page_flags::none) |
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none); (vma.flags().get(vm_flags::write_combine) ? page_flags::wc : page_flags::none);
page_table::iterator it {virt, m_pml4}; page_table::iterator it {virt, m_pml4};
@@ -222,7 +221,7 @@ vm_space::page_in(const obj::vm_area &vma, uintptr_t offset, uintptr_t phys, siz
uint64_t &entry = it.entry(page_table::level::pt); uint64_t &entry = it.entry(page_table::level::pt);
entry = (phys + i * frame_size) | flags; entry = (phys + i * frame_size) | flags;
log::spam(logs::paging, "Setting entry for %016llx: %016llx [%04llx]", log::spam(logs::paging, "Setting entry for %016llx: %016llx [%04llx]",
it.vaddress(), (phys + i * frame_size), flags); it.vaddress(), (phys + i * frame_size), flags.value());
++it; ++it;
} }
} }
@@ -247,11 +246,11 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
while (count--) { while (count--) {
uint64_t &e = it.entry(page_table::level::pt); uint64_t &e = it.entry(page_table::level::pt);
uintptr_t phys = e & ~0xfffull; uintptr_t phys = e & ~0xfffull;
util::bitset64 flags = e;
if (e & page_table::flag::present) { if (flags & page_flags::present) {
uint64_t orig = e;
e = 0; e = 0;
if (orig & page_table::flag::accessed) { 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" );
} }
@@ -290,11 +289,11 @@ vm_space::lock(const obj::vm_area &vma, uintptr_t offset, size_t count)
while (count--) { while (count--) {
uint64_t &e = it.entry(page_table::level::pt); uint64_t &e = it.entry(page_table::level::pt);
uintptr_t phys = e & ~0xfffull; uintptr_t phys = e & ~0xfffull;
util::bitset64 flags = e;
if (e & page_table::flag::present) { if (flags & page_flags::present) {
uint64_t orig = e;
e = locked_page_tag; e = locked_page_tag;
if (orig & page_table::flag::accessed) { if (flags & page_flags::accessed) {
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress()); auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
asm ( "invlpg %0" :: "m"(*addr) : "memory" ); asm ( "invlpg %0" :: "m"(*addr) : "memory" );
} }
@@ -338,10 +337,10 @@ vm_space::initialize_tcb(TCB &tcb)
} }
bool bool
vm_space::handle_fault(uintptr_t addr, fault_type fault) vm_space::handle_fault(uintptr_t addr, util::bitset8 fault)
{ {
// TODO: Handle more fult types // TODO: Handle more fult types
if (fault && fault_type::present) if (fault.get(fault_type::present))
return false; return false;
uintptr_t page = (addr & ~0xfffull); uintptr_t page = (addr & ~0xfffull);

View File

@@ -5,7 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <j6/flags.h> #include <j6/flags.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
#include <util/spinlock.h> #include <util/spinlock.h>
#include <util/vector.h> #include <util/vector.h>
@@ -39,7 +39,7 @@ public:
/// \arg area The area to add /// \arg area The area to add
/// \arg flags Flags for the operation (exact, clobber, etc) /// \arg flags Flags for the operation (exact, clobber, etc)
/// \returns The base address the area was added at /// \returns The base address the area was added at
uintptr_t add(uintptr_t base, obj::vm_area *area, obj::vm_flags flags); uintptr_t add(uintptr_t base, obj::vm_area *area, util::bitset32 flags);
/// Remove a virtual memory area from this address space /// Remove a virtual memory area from this address space
/// \arg area The area to remove /// \arg area The area to remove
@@ -88,15 +88,6 @@ public:
/// Set this space as the current active space /// Set this space as the current active space
void activate() const; void activate() const;
enum class fault_type : uint8_t {
none = 0x00,
present = 0x01,
write = 0x02,
user = 0x04,
reserved = 0x08,
fetch = 0x10
};
/// Allocate pages into virtual memory. May allocate less than requested. /// Allocate pages into virtual memory. May allocate less than requested.
/// \arg virt The virtual address at which to allocate /// \arg virt The virtual address at which to allocate
/// \arg count The number of pages to allocate /// \arg count The number of pages to allocate
@@ -104,11 +95,13 @@ public:
/// \returns The number of pages actually allocated /// \returns The number of pages actually allocated
size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys); size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys);
enum class fault_type { present, write, user, reserved, fetch };
/// Handle a page fault. /// Handle a page fault.
/// \arg addr Address which caused the fault /// \arg addr Address which caused the fault
/// \arg ft Flags from the interrupt about the kind of fault /// \arg ft Flags from the interrupt about the kind of fault
/// \returns True if the fault was successfully handled /// \returns True if the fault was successfully handled
bool handle_fault(uintptr_t addr, fault_type fault); bool handle_fault(uintptr_t addr, util::bitset8 fault);
/// Set up a TCB to operate in this address space. /// Set up a TCB to operate in this address space.
void initialize_tcb(TCB &tcb); void initialize_tcb(TCB &tcb);
@@ -155,6 +148,4 @@ private:
util::vector<area> m_areas; util::vector<area> m_areas;
util::spinlock m_lock; util::spinlock m_lock;
}; };
is_bitfield(vm_space::fault_type);

View File

@@ -3,15 +3,9 @@
/// Data structures for reading jsix_boot.dat /// Data structures for reading jsix_boot.dat
#include <stdint.h> #include <stdint.h>
#include <util/enum_bitfields.h>
namespace bootproto { namespace bootproto {
enum class desc_flags : uint16_t { enum class desc_flags { graphical, panic, symbols };
graphical = 0x0001,
panic = 0x0002,
symbols = 0x0004,
};
is_bitfield(desc_flags);
} // namespace bootproto } // namespace bootproto

View File

@@ -6,8 +6,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <util/bitset.h>
#include <util/counted.h> #include <util/counted.h>
#include <util/enum_bitfields.h>
namespace bootproto { namespace bootproto {
@@ -18,19 +18,13 @@ constexpr uint64_t header_magic = 0x4c454e52454b366aull; // 'j6KERNEL'
constexpr uint16_t header_version = 2; constexpr uint16_t header_version = 2;
constexpr uint16_t min_header_version = 2; constexpr uint16_t min_header_version = 2;
enum class section_flags : uint32_t { enum class section_flags { none, execute, write, read };
none = 0,
execute = 1,
write = 2,
read = 4,
};
is_bitfield(section_flags);
struct program_section { struct program_section {
uintptr_t phys_addr; uintptr_t phys_addr;
uintptr_t virt_addr; uintptr_t virt_addr;
uint32_t size; uint32_t size;
section_flags type; util::bitset32 type;
}; };
struct program { struct program {
@@ -112,18 +106,13 @@ struct frame_block
uint64_t *bitmap; uint64_t *bitmap;
}; };
enum class boot_flags : uint16_t { enum class boot_flags { none, debug, test };
none = 0x0000,
debug = 0x0001,
test = 0x0002,
};
is_bitfield(boot_flags);
struct args struct args
{ {
uint32_t magic; uint32_t magic;
uint16_t version; uint16_t version;
boot_flags flags; util::bitset16 flags;
void *pml4; void *pml4;
util::counted<void> page_tables; util::counted<void> page_tables;

View File

@@ -18,7 +18,7 @@ enum class feature {
max max
}; };
using features = util::bitset<(unsigned)feature::max>; using features = util::sized_bitset<(unsigned)feature::max>;
class cpu_id class cpu_id
{ {

View File

@@ -0,0 +1,11 @@
# vim: ft=python
module("edit",
kind = "lib",
deps = [ "j6", "libc" ],
sources = [
"line.cpp",
],
public_headers = [
"edit/line.h",
])

View File

@@ -0,0 +1,34 @@
#pragma once
/// \file line.h
/// Declaration of line-based editing class
#include <stddef.h>
#include <util/api.h>
namespace edit {
class API source
{
public:
virtual size_t read(char const **data) = 0;
virtual void consume(size_t size) = 0;
virtual void write(char const *data, size_t len) = 0;
};
class API line
{
public:
line(source &s);
~line();
/// Get a finished line from the editor.
size_t read(char *buffer, size_t size, char const *prompt = nullptr, size_t prompt_len = 0);
private:
size_t parse_command(char const *data, size_t len);
source &m_source;
};
} // namespace edit

111
src/libraries/edit/line.cpp Normal file
View File

@@ -0,0 +1,111 @@
#include <edit/line.h>
#include <j6/memutils.h>
#include <j6/syslog.hh>
static inline size_t min(size_t a, size_t b) { return a > b ? a : b; }
namespace edit {
static constexpr char ESC = '\x1b';
static constexpr char BACKSPACE = '\x08';
static constexpr char DEL = '\x7f';
static const char init_line[] = "\x1b[999D\x1b[K";
static const size_t init_line_len = sizeof(init_line) - 1;
static const char get_pos[] = "\x1b[n";
static const size_t get_pos_len = sizeof(get_pos) - 1;
static const char backspace[] = "\x1b[D\x1b[K";
static const size_t backspace_len = sizeof(backspace) - 1;
line::line(source &s) :
m_source {s}
{
}
line::~line()
{
}
size_t
line::read(char *buffer, size_t size, char const *prompt, size_t prompt_len)
{
m_source.write(init_line, init_line_len);
if (prompt && prompt_len)
m_source.write(prompt, prompt_len);
size_t in = 0;
while (in < size) {
char const *input = nullptr;
size_t inlen = m_source.read(&input);
size_t i = 0;
size_t sub_len = 0;
while (i < inlen) {
switch (input[i]) {
case ESC:
sub_len = parse_command(input + i, inlen - i);
if (!sub_len)
goto reread;
i += sub_len;
break;
case BACKSPACE:
case DEL:
if (in > 0) {
--in;
m_source.write(backspace, backspace_len);
}
++i;
break;
case '\r':
m_source.consume(++i);
m_source.write("\n", 1);
buffer[in] = 0;
return in;
default:
m_source.write(input + i, 1);
buffer[in++] = input[i++];
break;
}
}
reread:
m_source.consume(i);
}
return 0;
}
size_t
line::parse_command(char const *data, size_t len)
{
size_t i = 2;
char buf[60];
char *p = buf;
while (i < len) {
if ((data[i] >= '0' && data[i] <= '9') || data[i] == ';') {
*p++ = data[i];
} else {
*p++ = data[i];
*p = 0;
j6::syslog(j6::logs::app, j6::log_level::verbose,
"line edit got command: ESC[%s", buf);
return i;
}
++i;
}
return 0;
}
} // namespace line

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <util/enum_bitfields.h> #include <util/bitset.h>
namespace elf { namespace elf {
@@ -54,19 +54,12 @@ struct file_header
enum class segment_type : uint32_t { null, load, dynamic, interpreter, note }; enum class segment_type : uint32_t { null, load, dynamic, interpreter, note };
enum class segment_flags : uint32_t enum class segment_flags { exec, write, read };
{
none = 0x00,
exec = 0x01,
write = 0x02,
read = 0x04,
};
is_bitfield(segment_flags);
struct segment_header struct segment_header
{ {
segment_type type; segment_type type;
segment_flags flags; util::bitset32 flags;
uint64_t offset; uint64_t offset;
uint64_t vaddr; uint64_t vaddr;
@@ -80,18 +73,13 @@ struct segment_header
enum class section_type : uint32_t { null, progbits }; enum class section_type : uint32_t { null, progbits };
enum class section_flags : uint64_t enum class section_flags { write, alloc, exec };
{
write = 0x01,
alloc = 0x02,
exec = 0x04,
};
struct section_header struct section_header
{ {
uint32_t name_offset; uint32_t name_offset;
section_type type; section_type type;
section_flags flags; util::bitset64 flags;
uint64_t addr; uint64_t addr;
uint64_t offset; uint64_t offset;
uint64_t size; uint64_t size;
@@ -102,5 +90,3 @@ struct section_header
} __attribute__ ((packed)); } __attribute__ ((packed));
} // namespace elf } // namespace elf
is_bitfield(elf::section_flags);

View File

@@ -12,136 +12,180 @@
#include <j6/syscalls.h> #include <j6/syscalls.h>
#include <j6/syslog.hh> #include <j6/syslog.hh>
#include <util/spinlock.h> #include <util/spinlock.h>
#include <util/bip_buffer.h>
#include <util/new.h>
namespace j6 { namespace j6 {
static uintptr_t channel_addr = 0x6000'0000; struct channel_memory_area
static util::spinlock addr_spinlock;
struct channel::header
{ {
size_t size; j6::mutex mutex;
size_t read_index; j6::condition waiting;
size_t write_index; util::bip_buffer buf;
mutex mutex;
condition read_waiting;
condition write_waiting;
uint8_t data[0];
inline const void* read_at() const { return &data[read_index & (size - 1)]; }
inline void* write_at() { return &data[write_index & (size - 1)]; }
inline size_t read_avail() const { return write_index - read_index; }
inline size_t write_avail() const { return size - read_avail(); }
inline void consume(size_t n) { read_index += n; }
inline void commit(size_t n) { write_index += n; }
}; };
channel *
channel::create(size_t size) static bool
check_channel_size(size_t s)
{ {
j6_status_t result; if (s < arch::frame_size || (s & (s - 1)) != 0) {
j6_handle_t vma = j6_handle_invalid; syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s);
return false;
if (size < arch::frame_size || (size & (size - 1)) != 0) {
syslog("Bad channel size: %lx", size);
return nullptr;
} }
return true;
}
util::scoped_lock lock {addr_spinlock};
uintptr_t addr = channel_addr;
channel_addr += size * 2; // account for ring buffer virtual space doubling
lock.release();
result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write|j6_vm_flag_ring); static uintptr_t
create_channel_vma(j6_handle_t &vma, size_t size)
{
uintptr_t addr = 0;
j6_status_t result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write);
if (result != j6_status_ok) { if (result != j6_status_ok) {
syslog("Failed to create channel VMA. Error: %lx", result); syslog(j6::logs::ipc, j6::log_level::error, "Failed to create channel VMA. Error: %lx", result);
return 0;
}
syslog(j6::logs::ipc, j6::log_level::verbose, "Created channel VMA at 0x%lx size 0x%lx", addr, size);
return addr;
}
static void
init_channel_memory_area(channel_memory_area *area, size_t size)
{
new (&area->mutex) j6::mutex;
new (&area->waiting) j6::condition;
new (&area->buf) util::bip_buffer {size - sizeof(*area)};
}
channel *
channel::create(size_t tx_size, size_t rx_size)
{
if (!rx_size)
rx_size = tx_size;
if (!check_channel_size(tx_size) || !check_channel_size(rx_size))
return nullptr;
j6_status_t result;
j6_handle_t tx_vma = j6_handle_invalid;
j6_handle_t rx_vma = j6_handle_invalid;
uintptr_t tx_addr = create_channel_vma(tx_vma, tx_size);
if (!tx_addr)
return nullptr;
uintptr_t rx_addr = create_channel_vma(rx_vma, rx_size);
if (!rx_addr) {
j6_vma_unmap(tx_vma, 0);
j6_handle_close(tx_vma);
return nullptr; return nullptr;
} }
header *h = reinterpret_cast<header*>(addr); channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
memset(h, 0, sizeof(*h)); channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
h->size = size; init_channel_memory_area(tx, tx_size);
init_channel_memory_area(rx, rx_size);
return new channel {vma, h}; j6::syslog(logs::ipc, log_level::info, "Created new channel with handles {%x, %x}", tx_vma, rx_vma);
return new channel {{tx_vma, rx_vma}, *tx, *rx};
} }
channel * channel *
channel::open(j6_handle_t vma) channel::open(const channel_def &def)
{ {
j6_status_t result; j6_status_t result;
util::scoped_lock lock {addr_spinlock}; uintptr_t tx_addr = 0;
uintptr_t addr = channel_addr; result = j6_vma_map(def.tx, 0, &tx_addr, 0);
result = j6_vma_map(vma, 0, &addr, 0);
if (result != j6_status_ok) { if (result != j6_status_ok) {
syslog("Failed to map channel VMA. Error: %lx", result); syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
return nullptr; return nullptr;
} }
header *h = reinterpret_cast<header*>(addr); uintptr_t rx_addr = 0;
channel_addr += h->size; result = j6_vma_map(def.rx, 0, &rx_addr, 0);
lock.release(); if (result != j6_status_ok) {
j6_vma_unmap(def.tx, 0);
j6_handle_close(def.tx);
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
return nullptr;
}
return new channel {vma, h}; channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
j6::syslog(logs::ipc, log_level::info, "Opening existing channel with handles {%x:0x%lx, %x:0x%lx}", def.tx, tx, def.rx, rx);
return new channel { def, *tx, *rx };
} }
channel::channel(j6_handle_t vma, header *h) : channel::channel(
m_vma {vma}, const channel_def &def,
m_size {h->size}, channel_memory_area &tx,
m_header {h} channel_memory_area &rx) :
m_def {def},
m_tx {tx},
m_rx {rx}
{ {
} }
j6_status_t
channel::send(const void *buffer, size_t len, bool block) size_t
channel::reserve(size_t size, uint8_t **area, bool block)
{ {
if (len > m_header->size) if (size > m_tx.buf.buffer_size())
return j6_err_insufficient; return j6_err_insufficient;
j6::scoped_lock lock {m_header->mutex}; j6::scoped_lock lock {m_tx.mutex};
while (m_header->write_avail() < len) { while (m_tx.buf.write_available() < size) {
if (!block) if (!block) return 0;
return j6_status_would_block;
lock.release(); lock.release();
m_header->write_waiting.wait(); m_tx.waiting.wait();
lock.acquire(); lock.acquire();
} }
memcpy(m_header->write_at(), buffer, len); return m_tx.buf.reserve(size, area);
m_header->commit(len);
m_header->read_waiting.wake();
return j6_status_ok;
} }
j6_status_t void
channel::receive(void *buffer, size_t *size, bool block) channel::commit(size_t size)
{ {
j6::scoped_lock lock {m_header->mutex}; j6::scoped_lock lock {m_tx.mutex};
while (!m_header->read_avail()) { m_tx.buf.commit(size);
if (!block) if (!size) return;
return j6_status_would_block;
m_tx.waiting.wake();
j6::syslog(logs::ipc, log_level::spam,
"Sending %d bytes to channel on {%x}", size, m_def.tx);
}
size_t
channel::get_block(uint8_t const **area, bool block) const
{
j6::scoped_lock lock {m_rx.mutex};
while (!m_rx.buf.size()) {
if (!block) return 0;
lock.release(); lock.release();
m_header->read_waiting.wait(); m_rx.waiting.wait();
lock.acquire(); lock.acquire();
} }
size_t avail = m_header->read_avail(); return m_rx.buf.get_block(area);
size_t read = *size > avail ? avail : *size; }
memcpy(buffer, m_header->read_at(), read); void
m_header->consume(read); channel::consume(size_t size)
m_header->write_waiting.wake(); {
j6::scoped_lock lock {m_tx.mutex};
m_rx.buf.consume(size);
if (!size) return;
*size = read; m_rx.waiting.wake();
return j6_status_ok; j6::syslog(logs::ipc, log_level::spam,
"Read %d bytes from channel on {%x}", size, m_def.rx);
} }
} // namespace j6 } // namespace j6

View File

@@ -12,7 +12,7 @@ namespace j6 {
void void
condition::wait() condition::wait()
{ {
j6::syslog("Waiting on condition %lx", this); j6::syslog(j6::logs::app, j6::log_level::verbose, "Waiting on condition %lx", this);
uint32_t v = __atomic_add_fetch(&m_state, 1, __ATOMIC_ACQ_REL); uint32_t v = __atomic_add_fetch(&m_state, 1, __ATOMIC_ACQ_REL);
j6_status_t s = j6_futex_wait(&m_state, v, 0); j6_status_t s = j6_futex_wait(&m_state, v, 0);
while (s == j6_status_futex_changed) { while (s == j6_status_futex_changed) {
@@ -20,7 +20,7 @@ condition::wait()
if (v == 0) break; if (v == 0) break;
s = j6_futex_wait(&m_state, v, 0); s = j6_futex_wait(&m_state, v, 0);
} }
j6::syslog("Woke on condition %lx", this); j6::syslog(j6::logs::app, j6::log_level::verbose, "Woke on condition %lx", this);
} }
void void

View File

@@ -10,41 +10,66 @@
#include <j6/types.h> #include <j6/types.h>
#include <util/api.h> #include <util/api.h>
namespace util {
class bip_buffer;
}
namespace j6 { namespace j6 {
/// Descriptor of VMAs for a channel.
struct channel_def
{
j6_handle_t tx;
j6_handle_t rx;
};
struct channel_memory_area;
class API channel class API channel
{ {
public: public:
/// Create a new channel of the given size. /// Create a new channel.
static channel * create(size_t size); /// \arg tx_size Size of the transmit buffer (sending from this thread)
/// \arg rx_size Size of the receive buffer (sending from remote thread),
/// or 0 for equal sized buffers.
static channel * create(size_t tx_size, size_t rx_size = 0);
/// Open an existing channel for which we have a VMA handle /// Open an existing channel for which we have VMA handles
static channel * open(j6_handle_t vma); static channel * open(const channel_def &def);
/// Send data into the channel. /// Reserve an area of the output buffer for a write.
/// \arg buffer The buffer from which to read data /// \arg size Requested size, in bytes
/// \arg len The number of bytes to read from `buffer` /// \arg area [out] Pointer to returned area
/// \arg block If true, block this thread if there aren't `len` bytes of space available /// \arg block If true, block this thread until there are size bytes free
j6_status_t send(const void *buffer, size_t len, bool block = true); /// \returns Size of returned area, in bytes, or 0 on failure
size_t reserve(size_t size, uint8_t **area, bool block = true);
/// Read data out of the channel. /// Commit a pending write started by reserve()
/// \arg buffer The buffer to receive channel data /// \arg size Amount of data used, in bytes
/// \arg size [in] The size of `buffer` [out] the amount read into `buffer` void commit(size_t size);
/// \arg block If true, block this thread if there is no data to read yet
j6_status_t receive(void *buffer, size_t *size, bool block = true);
/// Get the VMA handle for sharing with other processes /// Get a pointer to a block of data in the buffer.
j6_handle_t handle() const { return m_vma; } /// \arg area [out] Pointer to the retuned area
/// \arg block If true, block this thread until there is data available
/// \returns Size of the returned area, in bytes
size_t get_block(uint8_t const **area, bool block = true) const;
/// Mark a number of bytes as consumed, freeing buffer space
/// \arg size Number of bytes to consume
void consume(size_t size);
/// Get the channel_def for creating the remote end
inline channel_def remote_def() const { return {m_def.rx, m_def.tx}; }
private: private:
struct header; channel(
const channel_def &def,
channel_memory_area &tx,
channel_memory_area &rx);
channel(j6_handle_t vma, header *h); channel_def m_def;
channel_memory_area &m_tx;
j6_handle_t m_vma; channel_memory_area &m_rx;
size_t m_size;
header *m_header;
}; };
} // namespace j6 } // namespace j6

View File

@@ -3,7 +3,8 @@
/// Enums used as flags for syscalls /// Enums used as flags for syscalls
enum j6_vm_flags { enum j6_vm_flags {
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v, j6_vm_flag_none = 0,
#define VM_FLAG(name, v) j6_vm_flag_ ## name = (1ul << v),
#include <j6/tables/vm_flags.inc> #include <j6/tables/vm_flags.inc>
#undef VM_FLAG #undef VM_FLAG
j6_vm_flag_MAX j6_vm_flag_MAX

View File

@@ -33,6 +33,13 @@ struct j6_arg_header
{ {
uint32_t size; uint32_t size;
uint16_t type; uint16_t type;
uint16_t reserved;
j6_arg_header *next;
};
struct j6_arg_none
{
add_header(none);
}; };
struct j6_arg_loader struct j6_arg_loader
@@ -43,7 +50,6 @@ struct j6_arg_loader
uintptr_t *got; uintptr_t *got;
uintptr_t entrypoint; uintptr_t entrypoint;
uintptr_t start_addr; uintptr_t start_addr;
uintptr_t other;
}; };
struct j6_arg_driver struct j6_arg_driver
@@ -68,14 +74,17 @@ struct j6_arg_handles
struct j6_init_args struct j6_init_args
{ {
uint64_t args[2]; uint64_t argv[2];
j6_arg_header *args;
}; };
/// Find the first handle of the given type held by this process /// 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); j6_handle_t API j6_find_first_handle(j6_object_type obj_type);
/// Find the first handle tagged with the given proto in the process init args
j6_handle_t API j6_find_init_handle(uint64_t proto);
/// Get the init args /// Get the init args
const j6_init_args * j6_get_init_args(); const j6_init_args * j6_get_init_args();

View File

@@ -1,6 +1,7 @@
#include <j6/protocols/service_locator.h> #include <j6/protocols/service_locator.h>
#include <j6/types.h> #include <j6/types.h>
#include <util/api.h> #include <util/api.h>
#include <util/counted.h>
namespace j6::proto::sl { namespace j6::proto::sl {
@@ -13,16 +14,16 @@ public:
/// Register a handle as a service with the locator. /// Register a handle as a service with the locator.
/// \arg proto_id The protocol this handle supports /// \arg proto_id The protocol this handle supports
/// \arg handle The mailbox or channel handle /// \arg handles The mailbox or channel handles
j6_status_t register_service(uint64_t proto_id, j6_handle_t handle); j6_status_t register_service(uint64_t proto_id, util::counted<j6_handle_t> handles);
/// Look up a handle with the locator service. /// Look up a handle with the locator service.
/// \arg proto_id The protocol to look for /// \arg proto_id The protocol to look for
/// \arg handle [out] The mailbox or channel handle /// \arg handles [out] The mailbox or channel handles
j6_status_t lookup_service(uint64_t proto_id, j6_handle_t &handle); j6_status_t lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles);
private: private:
j6_handle_t m_service; j6_handle_t m_service;
}; };
} // namespace j6::proto::sl } // namespace j6::proto::sl

View File

@@ -0,0 +1,55 @@
#pragma once
/// \file j6/ring_buffer.hh
/// Helper class for managing ring buffers in doubly-mapped VMAs
// The kernel depends on libj6 for some shared code,
// but should not include the user-specific code.
#ifndef __j6kernel
#include <stddef.h>
#include <j6/types.h>
#include <util/api.h>
namespace j6 {
API class ring_buffer
{
public:
ring_buffer(size_t pages);
inline bool valid() const { return m_data != nullptr; }
inline size_t size() const { return 1<<m_bits; }
inline size_t used() const { return m_write-m_read; }
inline size_t free() const { return size()-used(); }
inline const char * read_ptr() const { return get(m_read); }
inline char * write_ptr() { return get(m_write); }
inline void commit(size_t &n) {
if (n > free()) n = free();
m_write += n;
}
inline const char * consume(size_t &n) {
if (n > used()) n = used();
size_t i = m_read;
m_read += n;
return get(i);
}
private:
inline size_t mask() const { return (1<<m_bits)-1;}
inline size_t index(size_t i) const { return i & mask(); }
inline char * get(size_t i) { return m_data + index(i); }
inline const char * get(size_t i) const { return m_data + index(i); }
size_t m_bits;
size_t m_write;
size_t m_read;
j6_handle_t m_vma;
char *m_data;
};
} // namespace j6
#endif // __j6kernel

View File

@@ -2,15 +2,26 @@
/// \file j6/syslog.hh /// \file j6/syslog.hh
/// Utility function for writing messages to the kernel log /// Utility function for writing messages to the kernel log
#include <util/api.h>
// 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.
#ifndef __j6kernel #ifndef __j6kernel
#include <stdint.h>
#include <util/api.h>
namespace j6 { namespace j6 {
void API syslog(const char *fmt, ...); enum class logs : uint8_t {
#define LOG(name, lvl) name,
#include <j6/tables/log_areas.inc>
#undef LOG
COUNT
};
enum class log_level : uint8_t {
silent, fatal, error, warn, info, verbose, spam, max
};
void API syslog(logs area, log_level severity, const char *fmt, ...);
} // namespace j6 } // namespace j6

View File

@@ -1,6 +1,6 @@
LOG(apic, info) LOG(apic, info)
LOG(boot, info) LOG(boot, info)
LOG(device, spam) LOG(device, info)
LOG(irq, info) LOG(irq, info)
LOG(memory, info) LOG(memory, info)
LOG(objs, info) LOG(objs, info)
@@ -9,3 +9,7 @@ LOG(sched, info)
LOG(syscall,info) LOG(syscall,info)
LOG(task, verbose) LOG(task, verbose)
LOG(timer, info) LOG(timer, info)
LOG(ipc, spam)
LOG(app, spam)
LOG(proto, spam)
LOG(srv, info)

View File

@@ -1,15 +1,13 @@
VM_FLAG( none, 0x00000000 ) VM_FLAG( write, 0 )
VM_FLAG( exec, 1 )
VM_FLAG( write, 0x00000001 ) VM_FLAG( contiguous, 4 )
VM_FLAG( exec, 0x00000002 ) VM_FLAG( large_pages, 5 )
VM_FLAG( huge_pages, 6 )
VM_FLAG( contiguous, 0x00000010 ) VM_FLAG( write_combine, 8 )
VM_FLAG( large_pages, 0x00000020 )
VM_FLAG( huge_pages, 0x00000040 )
VM_FLAG( write_combine, 0x00000100 ) VM_FLAG( mmio, 12 )
VM_FLAG( mmio, 0x00001000 ) VM_FLAG( exact, 16 )
VM_FLAG( ring, 17 )
VM_FLAG( exact, 0x00010000 )
VM_FLAG( ring, 0x00020000 )

View File

@@ -12,7 +12,7 @@
namespace { namespace {
constexpr size_t static_arr_count = 32; constexpr size_t static_arr_count = 32;
j6_handle_descriptor handle_array[static_arr_count]; j6_handle_descriptor handle_array[static_arr_count];
j6_init_args init_args; j6_init_args init_args = { 0, 0, 0 };
} // namespace } // namespace
j6_handle_t j6_handle_t
@@ -36,6 +36,26 @@ j6_find_first_handle(j6_object_type obj_type)
return j6_handle_invalid; return j6_handle_invalid;
} }
j6_handle_t
j6_find_init_handle(uint64_t proto)
{
j6_arg_header *arg = init_args.args;
while (arg) {
if (arg->type == j6_arg_type_handles) {
j6_arg_handles *harg = reinterpret_cast<j6_arg_handles*>(arg);
for (unsigned i = 0; i < harg->nhandles; ++i) {
j6_arg_handle_entry &ent = harg->handles[i];
if (ent.proto == proto)
return ent.handle;
}
}
arg = arg->next;
}
return j6_handle_invalid;
}
const j6_init_args * API const j6_init_args * API
j6_get_init_args() j6_get_init_args()
{ {
@@ -43,10 +63,11 @@ j6_get_init_args()
} }
extern "C" void API extern "C" void API
__init_libj6(uint64_t arg0, uint64_t arg1) __init_libj6(uint64_t argv0, uint64_t argv1, j6_arg_header *args)
{ {
init_args.args[0] = arg0; init_args.argv[0] = argv0;
init_args.args[1] = arg1; init_args.argv[1] = argv1;
init_args.args = args;
} }

View File

@@ -8,10 +8,12 @@ j6 = module("j6",
"condition.cpp", "condition.cpp",
"init.cpp", "init.cpp",
"memutils.cpp", "memutils.cpp",
"memutils.s",
"mutex.cpp", "mutex.cpp",
"protocol_ids.cpp", "protocol_ids.cpp",
"protocols/service_locator.cpp", "protocols/service_locator.cpp",
"protocols/vfs.cpp", "protocols/vfs.cpp",
"ring_buffer.cpp",
"syscalls.s.cog", "syscalls.s.cog",
"sysconf.cpp.cog", "sysconf.cpp.cog",
"syslog.cpp", "syslog.cpp",
@@ -28,6 +30,7 @@ j6 = module("j6",
"j6/protocols.h", "j6/protocols.h",
"j6/protocols/service_locator.h", "j6/protocols/service_locator.h",
"j6/protocols/service_locator.hh", "j6/protocols/service_locator.hh",
"j6/ring_buffer.hh",
"j6/syscalls.h.cog", "j6/syscalls.h.cog",
"j6/sysconf.h.cog", "j6/sysconf.h.cog",
"j6/syslog.hh", "j6/syslog.hh",

View File

@@ -7,14 +7,6 @@
using namespace j6; using namespace j6;
using namespace __j6libc; using namespace __j6libc;
void *memcpy(void * restrict dst, const void * restrict src, size_t n) {
asm volatile ("rep movsb"
:
: "D"(dst), "S"(src), "c"(n)
: "memory");
return dst;
}
static void memmove_dispatch(char *s1, const char *s2, size_t n) { static void memmove_dispatch(char *s1, const char *s2, size_t n) {
if (s1 == s2) return; if (s1 == s2) return;

View File

@@ -0,0 +1,7 @@
global memcpy: function (memcpy.end - memcpy)
memcpy:
mov rax, rdi
mov rcx, rdx
rep movsb
ret
memcpy.end:

View File

@@ -13,16 +13,15 @@ client::client(j6_handle_t slp_mb) :
} }
j6_status_t j6_status_t
client::register_service(uint64_t proto_id, j6_handle_t handle) client::register_service(uint64_t proto_id, util::counted<j6_handle_t> handles)
{ {
uint64_t tag = j6_proto_sl_register; uint64_t tag = j6_proto_sl_register;
size_t handle_count = 1;
size_t data = proto_id; size_t data = proto_id;
size_t data_size = sizeof(proto_id); size_t data_size = sizeof(proto_id);
j6_status_t s = j6_mailbox_call(m_service, &tag, j6_status_t s = j6_mailbox_call(m_service, &tag,
&data, &data_size, data_size, &data, &data_size, data_size,
&handle, &handle_count); handles.pointer, &handles.count, handles.count);
if (s != j6_status_ok) if (s != j6_status_ok)
return s; return s;
@@ -34,31 +33,35 @@ client::register_service(uint64_t proto_id, j6_handle_t handle)
} }
j6_status_t j6_status_t
client::lookup_service(uint64_t proto_id, j6_handle_t &handle) client::lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles)
{ {
uint64_t tag = j6_proto_sl_find; uint64_t tag = j6_proto_sl_find;
size_t handle_count = 1;
size_t data = proto_id; size_t data = proto_id;
size_t data_size = sizeof(proto_id); size_t data_size = sizeof(proto_id);
handle = j6_handle_invalid; size_t handles_size = handles.count;
handles.count = 0;
j6::syslog("Looking up service for %x", proto_id); j6::syslog(j6::logs::proto, j6::log_level::verbose, "Looking up service for %x", proto_id);
j6_status_t s = j6_mailbox_call(m_service, &tag, j6_status_t s = j6_mailbox_call(m_service, &tag,
&data, &data_size, data_size, &data, &data_size, data_size,
&handle, &handle_count); handles.pointer, &handles.count, handles_size);
if (s != j6_status_ok) if (s != j6_status_ok) {
j6::syslog(j6::logs::proto, j6::log_level::error, "Received error %lx trying to call service lookup", s);
return s; return s;
}
if (tag == j6_proto_sl_result) if (tag == j6_proto_sl_result)
return j6_status_ok; // handle is already in `handle` return j6_status_ok; // handles are already in `handles`
else if (tag == j6_proto_base_status) else if (tag == j6_proto_base_status) {
j6::syslog(j6::logs::proto, j6::log_level::warn, "Received status %lx from service lookup", data);
return data; // contains a status return data; // contains a status
}
return j6_err_unexpected; return j6_err_unexpected;
} }
} // namespace j6::proto::sl } // namespace j6::proto::sl
#endif // __j6kernel #endif // __j6kernel

View File

@@ -21,7 +21,7 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
return j6_err_invalid_arg; return j6_err_invalid_arg;
uint64_t tag = j6_proto_vfs_load; uint64_t tag = j6_proto_vfs_load;
size_t handle_count = 1; size_t handle_count = 0;
vma = j6_handle_invalid; vma = j6_handle_invalid;
// Always need to send a big enough buffer for a status code // Always need to send a big enough buffer for a status code
@@ -38,12 +38,12 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
j6_status_t s = j6_mailbox_call(m_service, &tag, j6_status_t s = j6_mailbox_call(m_service, &tag,
data, &data_len, path_len, data, &data_len, path_len,
&vma, &handle_count); &vma, &handle_count, 1);
if (s != j6_status_ok) if (s != j6_status_ok)
return s; return s;
if (tag == j6_proto_vfs_file) { if (tag == j6_proto_vfs_file && handle_count == 1) {
size = 0; size = 0;
// Get the size into `size` // Get the size into `size`

View File

@@ -0,0 +1,38 @@
// The kernel depends on libj6 for some shared code,
// but should not include the user-specific code.
#ifndef __j6kernel
#include <arch/memory.h>
#include <j6/errors.h>
#include <j6/flags.h>
#include <j6/ring_buffer.hh>
#include <j6/syscalls.h>
#include <j6/types.h>
#include <util/util.h>
namespace j6 {
API
ring_buffer::ring_buffer(size_t pages) :
m_bits {util::log2(pages) + arch::frame_bits},
m_write {0},
m_read {0},
m_vma {j6_handle_invalid},
m_data {nullptr}
{
// Must be a power of 2
if (!util::is_pow2(pages))
return;
uintptr_t buffer_addr = 0;
size_t size = 1<<m_bits;
j6_status_t result = j6_vma_create_map(&m_vma, size, &buffer_addr, j6_vm_flag_ring|j6_vm_flag_write);
if (result != j6_status_ok)
return;
m_data = reinterpret_cast<char*>(buffer_addr);
}
} // namespace j6
#endif // __j6kernel

View File

@@ -9,7 +9,7 @@
namespace j6 { namespace j6 {
void void
syslog(const char *fmt, ...) syslog(logs area, log_level severity, const char *fmt, ...)
{ {
char buffer[200]; char buffer[200];
@@ -19,7 +19,7 @@ syslog(const char *fmt, ...)
va_end(va); va_end(va);
buffer[n] = 0; buffer[n] = 0;
j6_log(buffer); j6_log(static_cast<uint8_t>(area), static_cast<uint8_t>(severity), buffer);
} }
} // namespace j6 } // namespace j6

View File

@@ -18,6 +18,8 @@ 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
push 0 ; Add null frame push 0 ; Add null frame
push 0 push 0
mov rbp, rsp mov rbp, rsp

View File

@@ -9,16 +9,11 @@ extern cb __init_array_end;
namespace { namespace {
void void
run_ctor_list(cb *array, cb *end) run_ctor_list(cb *p, cb *end)
{ {
if (!array || !end) while (p && end && p < end) {
return; if (p) (*p)();
++p;
size_t i = 0;
while (true) {
const cb &ctor = array[i++];
if (&ctor == end) return;
if (ctor) ctor();
} }
} }
@@ -31,8 +26,8 @@ run_global_ctors()
} // namespace } // namespace
extern "C" extern "C" void
void __init_libc() __init_libc()
{ {
run_global_ctors(); run_global_ctors();
} }

View File

@@ -4,4 +4,5 @@ extern "C" {
// we have a real libdl // we have a real libdl
int dladdr(const void *, void *) { return 0; } int dladdr(const void *, void *) { return 0; }
int dl_iterate_phdr(void *, void *) { return 0; } int dl_iterate_phdr(void *, void *) { return 0; }
int dlsym(void *, const char *) { return 0; }
} }

View File

@@ -66,10 +66,10 @@ void* pvalloc(size_t);
_Noreturn void abort( void ); _Noreturn void abort( void );
int atexit( void (*func)(void) ); int atexit( void (*func)(void) );
int at_quick_exit( void (*func)(void) ); int at_quick_exit( void (*func)(void) );
_Noreturn void exit( int status ); _Noreturn void exit( long status );
_Noreturn void _Exit( int status ); _Noreturn void _Exit( long status );
char *getenv( const char *name ); char *getenv( const char *name );
_Noreturn void quick_exit( int status ); _Noreturn void quick_exit( long status );
int system( const char *string ); int system( const char *string );
// Searching and sorting utilities // Searching and sorting utilities

View File

@@ -10,4 +10,43 @@
* 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 time.h is not yet implemented. #include <__j6libc/null.h>
#include <__j6libc/size_t.h>
#include <stdint.h>
#define CLOCKS_PER_SEC 1
#define TIME_UTC 0
typedef int64_t clock_t;
typedef int64_t time_t;
struct timespec
{
time_t tv_sec;
long tv_nsec;
};
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
clock_t clock(void);
double difftime(time_t t1, time_t t0);
time_t mktime(struct tm *timeptr);
time_t time(time_t *timer);
int timespec_get(struct timespec *ts, int base);
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timer);
struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
size_t strftime(char *s, size_t maxsize, const char *fmt, const struct tm *timeptr);

View File

@@ -17,10 +17,6 @@
#include <__j6libc/wchar_t.h> #include <__j6libc/wchar_t.h>
#include <stdarg.h> #include <stdarg.h>
typedef unsigned int wint_t;
#define WEOF ((wint_t)-1)
struct tm; struct tm;
typedef struct { typedef struct {

View File

@@ -10,4 +10,7 @@
* 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 wctype.h is not yet implemented. #include <__j6libc/wchar_t.h>
typedef int wctrans_t;
typedef int wctype_t;

View File

@@ -5420,6 +5420,8 @@ void* dlrealloc_in_place(void* oldmem, size_t bytes) {
return mem; return mem;
} }
void* aligned_alloc(size_t alignment, size_t size) { return dlmemalign(alignment, size); }
void* dlmemalign(size_t alignment, size_t bytes) { void* dlmemalign(size_t alignment, size_t bytes) {
if (alignment <= MALLOC_ALIGNMENT) { if (alignment <= MALLOC_ALIGNMENT) {
return dlmalloc(bytes); return dlmalloc(bytes);

View File

@@ -21,7 +21,7 @@ size_t quick_exit_count = 0;
atexit_item quick_exit_array[max_atexit]; atexit_item quick_exit_array[max_atexit];
[[noreturn]] inline void [[noreturn]] inline void
exit_with_callbacks(int status, atexit_item *cbs, size_t count) exit_with_callbacks(long status, atexit_item *cbs, size_t count)
{ {
for (size_t i = count - 1; i < count; --i) { for (size_t i = count - 1; i < count; --i) {
atexit_item &item = cbs[i]; atexit_item &item = cbs[i];
@@ -61,13 +61,13 @@ int at_quick_exit( void (*func)(void) )
} }
void void
exit(int status) exit(long status)
{ {
exit_with_callbacks(status, atexit_array, atexit_count); exit_with_callbacks(status, atexit_array, atexit_count);
} }
void void
quick_exit(int status) quick_exit(long status)
{ {
exit_with_callbacks(status, quick_exit_array, quick_exit_count); exit_with_callbacks(status, quick_exit_array, quick_exit_count);
} }
@@ -75,11 +75,11 @@ quick_exit(int status)
void void
abort() abort()
{ {
_Exit(INT32_MIN); _Exit(INT64_MIN);
} }
void void
_Exit( int status ) _Exit( long status )
{ {
j6_process_exit(status); j6_process_exit(status);
} }

View File

@@ -16,7 +16,9 @@ int strncmp(const char *s1, const char *s2, size_t n) {
char const * c1 = s1; char const * c1 = s1;
char const * c2 = s2; char const * c2 = s2;
while (n && *c1 && *c2 && *c1++ == *c2++) n--; while (n && *c1 && *c2 && *c1 == *c2) {
n--; c1++; c2++;
}
if (!n || *c1 == *c2) return 0; if (!n || *c1 == *c2) return 0;
if (!*c2 || *c1 > *c2) return 1; if (!*c2 || *c1 > *c2) return 1;

View File

@@ -14,5 +14,9 @@
typedef __WCHAR_TYPE__ wchar_t; typedef __WCHAR_TYPE__ wchar_t;
#endif #endif
typedef unsigned int wint_t;
#define WEOF ((wint_t)-1)
#define WCHAR_MAX __WCHAR_MAX__ #define WCHAR_MAX __WCHAR_MAX__
#define WCHAR_MIN ((-__WCHAR_MAX__) - 1) #define WCHAR_MIN ((-__WCHAR_MAX__) - 1)

View File

@@ -8,27 +8,37 @@
namespace util { namespace util {
bip_buffer::bip_buffer() : bip_buffer::bip_buffer(size_t size) :
m_start_a(0), m_start_a {0},
m_start_b(0), m_start_b {0},
m_size_a(0), m_size_a {0},
m_size_b(0), m_size_b {0},
m_size_r(0), m_size_r {0},
m_buffer_size(0), m_buffer_size {size - sizeof(bip_buffer)}
m_buffer(nullptr) {}
{}
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) : size_t
m_start_a(0), bip_buffer::write_available() const
m_start_b(0), {
m_size_a(0), scoped_lock lock {m_lock};
m_size_b(0),
m_size_r(0),
m_buffer_size(size),
m_buffer(buffer)
{}
size_t bip_buffer::reserve(size_t size, void **area) if (m_size_r) {
return 0;
}
if (m_size_b) {
// If B exists, we're appending there. Get space between
// the end of B and start of A.
return m_start_a - m_start_b - m_size_b;
} else {
// B doesn't exist, check the space both before and after A.
size_t remaining = m_buffer_size - m_start_a - m_size_a;
return (m_start_a > remaining) ? m_start_a : remaining;
}
}
size_t
bip_buffer::reserve(size_t size, uint8_t **area)
{ {
scoped_lock lock {m_lock}; scoped_lock lock {m_lock};
@@ -84,7 +94,7 @@ void bip_buffer::commit(size_t size)
m_start_r = m_size_r = 0; m_start_r = m_size_r = 0;
} }
size_t bip_buffer::get_block(void **area) const size_t bip_buffer::get_block(uint8_t const **area) const
{ {
scoped_lock lock {m_lock}; scoped_lock lock {m_lock};

View File

@@ -14,17 +14,14 @@ namespace util {
class API bip_buffer class API bip_buffer
{ {
public: public:
/// Default constructor. Creates a zero-size buffer.
bip_buffer();
/// Constructor. /// Constructor.
bip_buffer(uint8_t *buffer, size_t size); bip_buffer(size_t size);
/// Reserve an area of buffer for a write. /// Reserve an area of buffer for a write.
/// \arg size Requested size, in bytes /// \arg size Requested size, in bytes
/// \arg area [out] Pointer to returned area /// \arg area [out] Pointer to returned area
/// \returns Size of returned area, in bytes, or 0 on failure /// \returns Size of returned area, in bytes, or 0 on failure
size_t reserve(size_t size, void **area); size_t reserve(size_t size, uint8_t **area);
/// Commit a pending write started by reserve() /// Commit a pending write started by reserve()
/// \arg size Amount of data used, in bytes /// \arg size Amount of data used, in bytes
@@ -33,7 +30,7 @@ public:
/// Get a pointer to a block of data in the buffer. /// Get a pointer to a block of data in the buffer.
/// \arg area [out] Pointer to the retuned area /// \arg area [out] Pointer to the retuned area
/// \returns Size of the returned area, in bytes /// \returns Size of the returned area, in bytes
size_t get_block(void **area) const; size_t get_block(uint8_t const **area) const;
/// Mark a number of bytes as consumed, freeing buffer space /// Mark a number of bytes as consumed, freeing buffer space
/// \arg size Number of bytes to consume /// \arg size Number of bytes to consume
@@ -47,6 +44,13 @@ public:
/// \returns Number of bytes of buffer that are free /// \returns Number of bytes of buffer that are free
inline size_t free_space() const { return m_buffer_size - size(); } inline size_t free_space() const { return m_buffer_size - size(); }
/// Get total size of the buffer
/// \returns Number of bytes in the buffer
inline size_t buffer_size() const { return m_buffer_size; }
/// Get the current size available for a contiguous write
size_t write_available() const;
private: private:
size_t m_start_a; size_t m_start_a;
size_t m_start_b; size_t m_start_b;
@@ -56,9 +60,10 @@ private:
size_t m_size_r; size_t m_size_r;
mutable spinlock m_lock; mutable spinlock m_lock;
const size_t m_buffer_size; const size_t m_buffer_size;
uint8_t * const m_buffer; uint8_t m_buffer[0];
bip_buffer() = delete;
}; };
} // namespace util } // namespace util

View File

@@ -7,34 +7,148 @@
namespace util { namespace util {
/// A statically-sized templated bitset /// A statically-sized templated bitset
template <unsigned N> template <typename I>
class bitset class bitset
{
public:
using storage_type = I;
private:
template <typename T>
static constexpr storage_type bit_or(T b) { return 1ull << uint64_t(b); }
template <typename T, typename ...Args>
static constexpr storage_type bit_or(T b, Args... bs) { return (1ull << storage_type(b)) | bit_or(bs...); }
public:
constexpr bitset(storage_type v = 0) : m_bits {v} {}
constexpr bitset(const bitset<I> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
__attribute__ ((always_inline))
static constexpr bitset of(Args... args) { return {bit_or(args...)}; }
template <typename T>
__attribute__ ((always_inline))
static constexpr bitset from(T i) { return {storage_type(i)}; }
__attribute__ ((always_inline))
inline constexpr bitset & operator=(bitset b) { m_bits = b.m_bits; return *this; }
inline constexpr operator storage_type () const { return m_bits; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((always_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((always_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
__attribute__ ((always_inline))
inline storage_type range(unsigned start, unsigned count) {
return (m_bits >> start) & ~((1 << count) - 1);
}
__attribute__ ((always_inline))
inline bitset & set_range(unsigned start, unsigned count, storage_type val) {
const storage_type mask = ~((1 << count) - 1) << start;
m_bits = (m_bits & ~mask) | ((val << start) & mask);
return *this;
}
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool operator[](T i) const { return get(i); }
__attribute__ ((always_inline))
inline constexpr bitset operator|(bitset b) const { return {storage_type(m_bits | b.m_bits)}; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator+(T i) const { return {storage_type(m_bits | bit(i))}; }
__attribute__ ((always_inline))
inline constexpr bitset operator|=(bitset b) { *this = *this|b; return *this; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator+=(T i) { set(i); return *this; }
__attribute__ ((always_inline))
inline constexpr bitset operator&(const bitset &b) const { return {storage_type(m_bits & b.m_bits)}; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator&(T i) const { return {m_bits & bit(i)}; }
__attribute__ ((always_inline))
inline constexpr bitset & operator&=(const bitset &b) { m_bits &= b.m_bits; return *this; }
__attribute__ ((always_inline))
inline constexpr bool operator==(const bitset &b) const { return m_bits == b.m_bits; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool operator==(T i) const { return m_bits == storage_type(i); }
inline constexpr bool empty() const { return m_bits == 0; }
inline constexpr uint64_t value() const { return m_bits; }
private:
template <typename T>
inline constexpr storage_type bit(T i) const { return (storage_type(1) << int(i)); }
storage_type m_bits;
};
using bitset64 = bitset<uint64_t>;
using bitset32 = bitset<uint32_t>;
using bitset16 = bitset<uint16_t>;
using bitset8 = bitset<uint8_t>;
template <unsigned N>
class sized_bitset
{ {
static constexpr unsigned num_elems = (N + 63) / 64; static constexpr unsigned num_elems = (N + 63) / 64;
public: public:
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline bool get(T i) const { inline bool get(T i) const {
return bits(i) & bit(i); return bits(i) & bit(i);
} }
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline bitset & set(T i) { inline sized_bitset & set(T i) {
bits(i) |= bit(i); bits(i) |= bit(i);
return *this; return *this;
} }
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline bitset & clear(T i) { inline sized_bitset & clear(T i) {
bits(i) &= ~bit(i); bits(i) &= ~bit(i);
return *this; return *this;
} }
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline bool operator[](T i) const { return get(i); } inline bool operator[](T i) const { return get(i); }
inline bool empty() const { inline bool empty() const {
@@ -45,260 +159,18 @@ public:
private: private:
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline uint64_t bit(T i) const { return (1ull << (static_cast<uint64_t>(i) & 63)); } inline uint64_t bit(T i) const { return (1ull << (static_cast<uint64_t>(i) & 63)); }
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline uint64_t &bits(T i) { return m_bits[static_cast<uint64_t>(i) >> 6]; } inline uint64_t &bits(T i) { return m_bits[static_cast<uint64_t>(i) >> 6]; }
template <typename T> template <typename T>
__attribute__ ((force_inline)) __attribute__ ((always_inline))
inline uint64_t bits(T i) const { return m_bits[static_cast<uint64_t>(i) >> 6]; } inline uint64_t bits(T i) const { return m_bits[static_cast<uint64_t>(i) >> 6]; }
uint64_t m_bits[num_elems] = {0}; uint64_t m_bits[num_elems] = {0};
}; };
namespace {
}
/// A statically-sized templated bitset
template <>
class bitset<64>
{
template <typename T>
static constexpr uint64_t bit_or(T b) { return 1ull << uint64_t(b); }
template <typename T, typename ...Args>
static constexpr uint64_t bit_or(T b, Args... bs) { return (1ull << uint64_t(b)) | bit_or(bs...); }
public:
bitset(uint64_t v = 0) : m_bits {v} {}
bitset(const bitset<64> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr explicit bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
__attribute__ ((force_inline))
inline bitset & operator=(T v) { m_bits = static_cast<uint64_t>(v); return *this; }
inline constexpr operator const uint64_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool operator[](T i) const { return get(i); }
inline constexpr bool empty() const { return m_bits == 0; }
inline constexpr uint64_t value() const { return m_bits; }
private:
template <typename T>
inline constexpr uint64_t bit(T i) const { return (1ull << static_cast<uint64_t>(i)); }
uint64_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<32>
{
template <typename T>
static constexpr uint32_t bit_or(T b) { return 1u << uint32_t(b); }
template <typename T, typename ...Args>
static constexpr uint32_t bit_or(T b, Args... bs) { return (1u << uint32_t(b)) | bit_or(bs...); }
public:
bitset(uint32_t v = 0) : m_bits {v} {}
bitset(const bitset<32> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint32_t>(v); return *this; }
inline constexpr operator uint32_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint32_t value() const { return m_bits; }
private:
template <typename T>
inline uint32_t bit(T i) const { return (1u << static_cast<uint32_t>(i)); }
uint32_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<16>
{
template <typename T>
static constexpr uint16_t bit_or(T b) { return 1u << uint16_t(b); }
template <typename T, typename ...Args>
static constexpr uint16_t bit_or(T b, Args... bs) { return (1u << uint16_t(b)) | bit_or(bs...); }
public:
bitset(uint16_t v = 0) : m_bits {v} {}
bitset(const bitset<16> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint16_t>(v); return *this; }
inline constexpr operator uint16_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint16_t value() const { return m_bits; }
private:
template <typename T>
inline uint16_t bit(T i) const { return (1u << static_cast<uint16_t>(i)); }
uint16_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<8>
{
template <typename T>
static constexpr uint8_t bit_or(T b) { return 1u << uint8_t(b); }
template <typename T, typename ...Args>
static constexpr uint8_t bit_or(T b, Args... bs) { return (1u << uint8_t(b)) | bit_or(bs...); }
public:
bitset(uint8_t v = 0) : m_bits {v} {}
bitset(const bitset<8> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint8_t>(v); return *this; }
inline constexpr operator uint8_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint8_t value() const { return m_bits; }
private:
template <typename T>
inline uint8_t bit(T i) const { return (1u << static_cast<uint8_t>(i)); }
uint8_t m_bits;
};
using bitset64 = bitset<64>;
using bitset32 = bitset<32>;
using bitset16 = bitset<16>;
using bitset8 = bitset<8>;
} // namespace util } // namespace util

View File

@@ -1,99 +0,0 @@
#pragma once
#include <util/basic_types.h>
namespace util {
namespace bits {
template <typename E>
constexpr bool is_enum_bitfield(E) { return false; }
template <typename E>
struct enum_or_int {
static constexpr bool value =
is_enum_bitfield(typename types::non_const<E>::type{})
|| types::is_integral<E>::value;
};
template <typename E, typename F>
struct both_enum_or_int {
static constexpr bool value =
types::conjunction< enum_or_int<E>, enum_or_int<F> >::value;
};
template <typename E, typename R>
struct enable_if_bitfield {
using enum_t = typename types::non_const<E>::type;
using type = typename
types::enable_if< is_enum_bitfield(enum_t{}), R >::type;
};
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator |= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) |
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator &= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) &
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator ^= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) ^
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs) { return lhs &= rhs; }
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs) { return lhs |= rhs; }
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs) { return lhs ^= rhs; }
template <typename E>
constexpr typename enable_if_bitfield<E,E>::type
operator ~ (E rhs) { return static_cast<E>(~static_cast<typename types::integral<E>::type>(rhs)); }
template <typename E>
constexpr typename enable_if_bitfield<E,bool>::type
operator ! (E rhs) { return static_cast<typename types::integral<E>::type>(rhs) == 0; }
/// Override logical-and to mean 'rhs contains all bits in lhs'
template <typename E>
constexpr typename enable_if_bitfield<E,bool>::type
operator && (E rhs, E lhs) { return (rhs & lhs) == lhs; }
/// Generic 'has' for non-marked bitfields
template <typename E, typename F>
constexpr bool has(E set, F flags)
{
return
(static_cast<typename types::integral<E>::type>(set) &
static_cast<typename types::integral<F>::type>(flags)) ==
static_cast<typename types::integral<F>::type>(flags);
}
} // namespace bits
} // namespace util
#define is_bitfield(name) \
constexpr bool is_enum_bitfield(name) { return true; } \
using namespace ::util::bits;

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