mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
Compare commits
24 Commits
feature/dy
...
v0.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 70af5a31cd | |||
| 0ab2f59fe8 | |||
| ab84cdb790 | |||
| da9041823b | |||
| 2d7cfc6c59 | |||
| cee4fe67fc | |||
| 53f90f5a1d | |||
| bab2dd5c69 | |||
| 3b9efc11d0 | |||
| 182d3b0a6a | |||
|
|
ed38e989b1 | ||
|
|
1bc6f422c5 | ||
|
|
4909d15c20 | ||
|
|
e725795a17 | ||
|
|
d8a21608c3 | ||
|
|
396fc131e0 | ||
|
|
40130076b2 | ||
|
|
9f54927a82 | ||
|
|
f7ea46e49e | ||
|
|
bc46c9a7d5 | ||
|
|
a1e2c58afc | ||
|
|
4e73e933db | ||
|
|
9f8e75f680 | ||
|
|
e17119254b |
56
.github/workflows/sphinx_deploy.yml
vendored
Normal file
56
.github/workflows/sphinx_deploy.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -16,3 +16,4 @@ compile_commands.json
|
||||
buddy_allocs.txt
|
||||
frame_allocs.txt
|
||||
heap_allocs.txt
|
||||
/docs/_build
|
||||
|
||||
3
assets/build/config.release.yaml
Normal file
3
assets/build/config.release.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
ccflags: [
|
||||
"-O3",
|
||||
]
|
||||
@@ -501,6 +501,18 @@ class LinkedListPrinter:
|
||||
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():
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("jsix")
|
||||
pp.add_printer("cap table", '^cap_table$', CapTablePrinter)
|
||||
@@ -520,9 +532,10 @@ GetThreadsCommand()
|
||||
PrintProfilesCommand()
|
||||
DumpLogCommand()
|
||||
ShowCurrentProcessCommand()
|
||||
IsRunning()
|
||||
|
||||
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:
|
||||
gdb.execute("add-symbol-file build/panic.serial.elf")
|
||||
gdb.execute("target remote :1234")
|
||||
|
||||
14
assets/manifests/minimal.yaml
Normal file
14
assets/manifests/minimal.yaml
Normal 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
|
||||
14
assets/manifests/shell.yaml
Normal file
14
assets/manifests/shell.yaml
Normal 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
|
||||
@@ -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 {
|
||||
uid f441e03da5516b1a
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ object mailbox : object {
|
||||
method call [cap:send] {
|
||||
param tag uint64 [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_size size # total size of handles buffer
|
||||
}
|
||||
|
||||
# Respond to a message sent using call, and wait for another
|
||||
@@ -31,8 +32,9 @@ object mailbox : object {
|
||||
method respond [cap:receive] {
|
||||
param tag uint64 [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_size size # total size of handles buffer
|
||||
param reply_tag uint64 [inout]
|
||||
param flags uint64
|
||||
}
|
||||
|
||||
@@ -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] {
|
||||
uid 667f61fb2cd57bb4
|
||||
cname kobject
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "objects/object.def"
|
||||
|
||||
# Processes are a collection of handles and a virtual memory
|
||||
# space inside which threads are run.
|
||||
# A ``process`` object represents a process running on the system, and allows
|
||||
# control over the threads, handles, and virtual memory space of that process.
|
||||
|
||||
object process : object {
|
||||
uid 0c69ee0b7502ba31
|
||||
@@ -19,7 +19,7 @@ object process : object {
|
||||
|
||||
# Stop all threads and exit the current process
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# The system object represents a handle to kernel functionality
|
||||
# needed by drivers and other priviledged services
|
||||
# The singular ``system`` object represents a handle to kernel functionality
|
||||
# needed by drivers and other priviledged services.
|
||||
|
||||
object system : object {
|
||||
uid fa72506a2cf71a30
|
||||
|
||||
|
||||
@@ -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 {
|
||||
uid 11f23e593d5761bd
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
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 {
|
||||
uid d6a12b63b3ed3937
|
||||
cname vm_area
|
||||
|
||||
@@ -24,6 +24,8 @@ interface syscalls [syscall] {
|
||||
|
||||
# Write a message to the kernel log
|
||||
function log {
|
||||
param area uint8
|
||||
param severity uint8
|
||||
param message string
|
||||
}
|
||||
|
||||
@@ -42,6 +44,11 @@ interface syscalls [syscall] {
|
||||
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
|
||||
function futex_wait [static] {
|
||||
param address uint32* # Address of the futex value
|
||||
|
||||
22
docs/Makefile
Normal file
22
docs/Makefile
Normal 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
254
docs/_static/custom.css
vendored
Normal 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
50
docs/conf.py
Normal 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
79
docs/index.rst
Normal 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
12
docs/jsix_transparent.svg
Normal 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
7
docs/modd.conf
Normal 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
5
docs/requirements.txt
Normal 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
436
docs/syscall_interface.rst
Normal 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)
|
||||
|
||||
@@ -7,3 +7,5 @@ pure-cdb == 4
|
||||
pyzstd == 0.15
|
||||
pyelftools
|
||||
iced-x86
|
||||
sphinx
|
||||
renku-sphinx-theme
|
||||
|
||||
@@ -104,7 +104,7 @@ def add_dir(path, files, dirs, inode_data, dir_inodes, output, compress):
|
||||
compressed = uncompressed
|
||||
comp_size = uncomp_size
|
||||
|
||||
output.write(uncompressed)
|
||||
output.write(compressed)
|
||||
inode_data.append((inode_type_dir, offset, comp_size, uncomp_size))
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ read_string(util::buffer &data)
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <bootproto/bootconfig.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace uefi {
|
||||
@@ -14,7 +15,7 @@ namespace boot {
|
||||
using desc_flags = bootproto::desc_flags;
|
||||
|
||||
struct descriptor {
|
||||
desc_flags flags;
|
||||
util::bitset16 flags;
|
||||
wchar_t const *path;
|
||||
};
|
||||
|
||||
@@ -27,7 +28,7 @@ public:
|
||||
/// Constructor. Loads bootconfig from the given buffer.
|
||||
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 & init() const { return m_init; }
|
||||
inline const wchar_t * initrd() const { return m_initrd; }
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
inline const descriptors & panics() { return m_panics; }
|
||||
|
||||
private:
|
||||
uint16_t m_flags;
|
||||
util::bitset16 m_flags;
|
||||
descriptor m_kernel;
|
||||
descriptor m_init;
|
||||
descriptors m_panics;
|
||||
|
||||
@@ -110,7 +110,7 @@ parse_program(const wchar_t *name, util::const_buffer data, bootproto::program &
|
||||
section.phys_addr = elf.base() + seg.offset;
|
||||
section.virt_addr = seg.vaddr;
|
||||
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)
|
||||
section.phys_addr = allocate_bss(seg);
|
||||
@@ -128,8 +128,6 @@ load_program(
|
||||
paging::pager &pager,
|
||||
bool verify)
|
||||
{
|
||||
using util::bits::has;
|
||||
|
||||
status_line status(L"Loading program", name);
|
||||
|
||||
elf::file elf {data};
|
||||
@@ -155,8 +153,8 @@ load_program(
|
||||
|
||||
pager.map_pages(phys_addr, seg.vaddr,
|
||||
memory::bytes_to_pages(seg.mem_size),
|
||||
has(seg.flags, elf::segment_flags::write),
|
||||
has(seg.flags, elf::segment_flags::exec));
|
||||
seg.flags.get(elf::segment_flags::write),
|
||||
seg.flags.get(elf::segment_flags::exec));
|
||||
}
|
||||
|
||||
return elf.entrypoint();
|
||||
|
||||
@@ -82,9 +82,8 @@ load_resources(
|
||||
util::buffer kernel = loader::load_file(disk, bc.kernel().path);
|
||||
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;
|
||||
|
||||
bool has_panic = false;
|
||||
@@ -96,7 +95,7 @@ load_resources(
|
||||
// Find the screen-specific panic handler first to
|
||||
// give it priority
|
||||
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);
|
||||
has_panic = true;
|
||||
break;
|
||||
@@ -106,7 +105,7 @@ load_resources(
|
||||
|
||||
if (!has_panic) {
|
||||
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);
|
||||
has_panic = true;
|
||||
break;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/misc.h> // for byteswap32
|
||||
|
||||
namespace acpi {
|
||||
@@ -62,32 +62,31 @@ struct gas
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class fadt_flags : uint32_t
|
||||
enum class fadt_flags
|
||||
{
|
||||
wbinvd = 0x00000001,
|
||||
wbinvd_flush = 0x00000002,
|
||||
proc_c1 = 0x00000004,
|
||||
p_lvl2_up = 0x00000008,
|
||||
pwr_button = 0x00000010,
|
||||
slp_button = 0x00000020,
|
||||
fix_rtc = 0x00000040,
|
||||
rtc_s4 = 0x00000080,
|
||||
tmr_val_ext = 0x00000100,
|
||||
dck_cap = 0x00000200,
|
||||
reset_reg_sup = 0x00000400,
|
||||
sealed_case = 0x00000800,
|
||||
headless = 0x00001000,
|
||||
cpu_sw_slp = 0x00002000,
|
||||
pci_exp_wak = 0x00004000,
|
||||
use_plat_clock = 0x00008000,
|
||||
s4_rtc_sts_val = 0x00010000,
|
||||
remote_pwr_cap = 0x00020000,
|
||||
apic_cluster = 0x00040000,
|
||||
apic_physical = 0x00080000,
|
||||
hw_reduced_acpi = 0x00100000,
|
||||
low_pwr_s0_idle = 0x00200000
|
||||
wbinvd,
|
||||
wbinvd_flush,
|
||||
proc_c1,
|
||||
p_lvl2_up,
|
||||
pwr_button,
|
||||
slp_button,
|
||||
fix_rtc,
|
||||
rtc_s4,
|
||||
tmr_val_ext,
|
||||
dck_cap,
|
||||
reset_reg_sup,
|
||||
sealed_case,
|
||||
headless,
|
||||
cpu_sw_slp,
|
||||
pci_exp_wak,
|
||||
use_plat_clock,
|
||||
s4_rtc_sts_val,
|
||||
remote_pwr_cap,
|
||||
apic_cluster,
|
||||
apic_physical,
|
||||
hw_reduced_acpi,
|
||||
low_pwr_s0_idle
|
||||
};
|
||||
is_bitfield(fadt_flags);
|
||||
|
||||
struct fadt
|
||||
{
|
||||
@@ -134,7 +133,7 @@ struct fadt
|
||||
|
||||
uint16_t iapc_boot_arch;
|
||||
uint8_t reserved1;
|
||||
fadt_flags flags;
|
||||
util::bitset32 flags;
|
||||
|
||||
gas reset_reg;
|
||||
uint8_t reset_value;
|
||||
|
||||
@@ -71,21 +71,19 @@ lapic::get_id()
|
||||
}
|
||||
|
||||
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
|
||||
ipi_wait();
|
||||
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode);
|
||||
uint32_t command = util::bitset32::from(vector) | mode;
|
||||
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
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
|
||||
ipi_wait();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// Classes to control both local and I/O APICs.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
#include "interrupts.h"
|
||||
|
||||
@@ -33,33 +33,30 @@ public:
|
||||
/// Get the local APIC's ID
|
||||
uint8_t get_id();
|
||||
|
||||
enum class ipi : uint32_t
|
||||
enum class ipi_flags
|
||||
{
|
||||
// Delivery modes
|
||||
fixed = 0x0000,
|
||||
smi = 0x0200,
|
||||
nmi = 0x0400,
|
||||
init = 0x0500,
|
||||
startup = 0x0600,
|
||||
|
||||
// Flags
|
||||
deassert = 0x0000,
|
||||
assert = 0x4000,
|
||||
edge = 0x0000, ///< edge-triggered
|
||||
level = 0x8000, ///< level-triggered
|
||||
logical = 11,
|
||||
pending = 12,
|
||||
assert = 14,
|
||||
level = 15,
|
||||
};
|
||||
|
||||
// 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.
|
||||
/// \arg mode The sending mode
|
||||
/// \arg vector The interrupt vector
|
||||
/// \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
|
||||
/// \arg mode The sending mode
|
||||
/// \arg self If true, include this CPU in the broadcast
|
||||
/// \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
|
||||
/// before sending another IPI with send_ipi().
|
||||
@@ -145,6 +142,3 @@ private:
|
||||
uint8_t m_id;
|
||||
uint8_t m_version;
|
||||
};
|
||||
|
||||
is_bitfield(lapic::ipi);
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ cpu_early_init(cpu_data *cpu)
|
||||
set_xcr0(xcr0_val);
|
||||
|
||||
// Set initial floating point state
|
||||
const util::bitset32 mxcsr_val {
|
||||
const util::bitset32 mxcsr_val = util::bitset32::of(
|
||||
mxcsr::DAZ,
|
||||
mxcsr::IM,
|
||||
mxcsr::DM,
|
||||
@@ -126,8 +126,7 @@ cpu_early_init(cpu_data *cpu)
|
||||
mxcsr::OM,
|
||||
mxcsr::UM,
|
||||
mxcsr::PM,
|
||||
mxcsr::FTZ,
|
||||
};
|
||||
mxcsr::FTZ);
|
||||
asm ( "ldmxcsr %0" :: "m"(mxcsr_val) );
|
||||
|
||||
// Install the GS base pointint to the cpu_data
|
||||
|
||||
@@ -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;
|
||||
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) :
|
||||
m_tss(tss)
|
||||
@@ -32,13 +35,13 @@ GDT::GDT(TSS *tss) :
|
||||
m_ptr.base = &m_entries[0];
|
||||
|
||||
// Kernel CS/SS - always 64bit
|
||||
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
||||
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
||||
set(kern_cs_index, 0, 0xfffff, true, rwx);
|
||||
set(kern_ss_index, 0, 0xfffff, true, rw);
|
||||
|
||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
||||
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
||||
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
||||
set(user_cs32_index, 0, 0xfffff, false, ring3 | rwx);
|
||||
set(user_ss_index, 0, 0xfffff, true, ring3 | rw);
|
||||
set(user_cs64_index, 0, 0xfffff, true, ring3 | rwx);
|
||||
|
||||
set_tss(tss);
|
||||
}
|
||||
@@ -61,7 +64,7 @@ GDT::install() const
|
||||
}
|
||||
|
||||
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].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_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
|
||||
@@ -79,7 +84,7 @@ struct tss_descriptor
|
||||
uint16_t limit_low;
|
||||
uint16_t base_00;
|
||||
uint8_t base_16;
|
||||
GDT::type type;
|
||||
util::bitset8 type;
|
||||
uint8_t size;
|
||||
uint8_t base_24;
|
||||
uint32_t base_32;
|
||||
@@ -102,11 +107,9 @@ GDT::set_tss(TSS *tss)
|
||||
tssd.base_32 = (base >> 32) & 0xffffffff;
|
||||
tssd.reserved = 0;
|
||||
|
||||
tssd.type =
|
||||
type::accessed |
|
||||
type::execute |
|
||||
type::ring3 |
|
||||
type::present;
|
||||
static constexpr util::bitset8 tss_mark =
|
||||
util::bitset8::of(type::accessed, type::execute, type::present);
|
||||
tssd.type = ring3 | tss_mark;
|
||||
|
||||
memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// Definitions relating to a CPU's GDT table
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
class TSS;
|
||||
|
||||
@@ -25,21 +25,20 @@ public:
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
|
||||
enum class type : uint8_t
|
||||
enum class type
|
||||
{
|
||||
accessed = 0x01,
|
||||
read_write = 0x02,
|
||||
conforming = 0x04,
|
||||
execute = 0x08,
|
||||
system = 0x10,
|
||||
ring1 = 0x20,
|
||||
ring2 = 0x40,
|
||||
ring3 = 0x60,
|
||||
present = 0x80
|
||||
accessed,
|
||||
read_write,
|
||||
conforming,
|
||||
execute,
|
||||
system,
|
||||
ring1,
|
||||
ring2,
|
||||
present
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
struct descriptor
|
||||
@@ -47,7 +46,7 @@ private:
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
type type;
|
||||
util::bitset8 type;
|
||||
uint8_t size;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed, align(8)));
|
||||
@@ -63,5 +62,3 @@ private:
|
||||
|
||||
ptr m_ptr;
|
||||
};
|
||||
|
||||
is_bitfield(GDT::type);
|
||||
|
||||
@@ -123,16 +123,18 @@ isr_handler(cpu_state *regs)
|
||||
uintptr_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
bool user = cr2 < mem::kernel_offset;
|
||||
vm_space::fault_type ft =
|
||||
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||
// The zero page is always invalid
|
||||
if (cr2 > mem::frame_size) {
|
||||
bool user = cr2 < mem::kernel_offset;
|
||||
util::bitset8 ft = regs->errorcode;
|
||||
|
||||
vm_space &space = user
|
||||
? obj::process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
vm_space &space = user
|
||||
? obj::process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
}
|
||||
|
||||
util::format({message, sizeof(message)},
|
||||
"Page fault: %016lx%s%s%s%s%s", cr2,
|
||||
|
||||
@@ -1,49 +1,142 @@
|
||||
#include <j6/memutils.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
#include "kassert.h"
|
||||
#include "ipc_message.h"
|
||||
#include "j6/types.h"
|
||||
|
||||
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(
|
||||
uint64_t in_tag,
|
||||
const util::buffer &in_data,
|
||||
const util::counted<j6_handle_t> &in_handles) :
|
||||
tag {in_tag}, data {nullptr, in_data.count}, handles {nullptr, in_handles.count}
|
||||
out_of_band {0}
|
||||
{
|
||||
if (data.count) {
|
||||
data.pointer = new uint8_t [data.count];
|
||||
memcpy(data.pointer, in_data.pointer, data.count);
|
||||
}
|
||||
|
||||
if (handles.count) {
|
||||
handles.pointer = new j6_handle_t [handles.count];
|
||||
memcpy(handles.pointer, in_handles.pointer, handles.count * sizeof(j6_handle_t));
|
||||
}
|
||||
set(in_tag, in_data, in_handles);
|
||||
}
|
||||
|
||||
|
||||
message::message(message &&other) {
|
||||
*this = util::move(other);
|
||||
}
|
||||
|
||||
message::message(message &&other) { *this = util::move(other); }
|
||||
|
||||
message::~message()
|
||||
{
|
||||
delete [] reinterpret_cast<uint8_t*>(data.pointer);
|
||||
delete [] handles.pointer;
|
||||
clear_oob();
|
||||
}
|
||||
|
||||
|
||||
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::operator=(message &&other)
|
||||
{
|
||||
clear_oob();
|
||||
|
||||
tag = other.tag;
|
||||
other.tag = 0;
|
||||
|
||||
data = other.data;
|
||||
other.data = {nullptr, 0};
|
||||
data_size = other.data_size;
|
||||
other.data_size = 0;
|
||||
|
||||
handles = other.handles;
|
||||
other.handles = {nullptr, 0};
|
||||
handle_count = other.handle_count;
|
||||
other.handle_count = 0;
|
||||
|
||||
out_of_band = other.out_of_band;
|
||||
other.out_of_band = 0;
|
||||
|
||||
memcpy(content, other.content, sizeof(content));
|
||||
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
|
||||
|
||||
@@ -5,14 +5,29 @@
|
||||
#include <stdint.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/pointers.h>
|
||||
|
||||
namespace ipc {
|
||||
|
||||
static constexpr size_t message_size = 64;
|
||||
|
||||
struct message
|
||||
{
|
||||
uint64_t tag;
|
||||
util::buffer data;
|
||||
util::counted<j6_handle_t> handles;
|
||||
uint16_t data_size;
|
||||
|
||||
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(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 & 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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/memutils.h>
|
||||
@@ -54,7 +53,7 @@ kernel_main(bootproto::args *args)
|
||||
bsp_late_init();
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
for (const auto § : program.sections) {
|
||||
vm_flags flags =
|
||||
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
util::bitset32 flags = util::bitset32::of(vm_flags::exact);
|
||||
if (sect.type.get(section_flags::execute))
|
||||
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);
|
||||
space.add(sect.virt_addr, vma, obj::vm_flags::exact);
|
||||
space.add(sect.virt_addr, vma, flags);
|
||||
}
|
||||
|
||||
uint64_t iopl = (3ull << 12);
|
||||
|
||||
@@ -124,7 +124,8 @@ LOG_LEVEL_FUNCTION(info);
|
||||
LOG_LEVEL_FUNCTION(warn);
|
||||
LOG_LEVEL_FUNCTION(error);
|
||||
|
||||
void fatal(logs area, const char *fmt, ...)
|
||||
void
|
||||
fatal(logs area, const char *fmt, ...)
|
||||
{
|
||||
logger *l = logger::s_log;
|
||||
if (!l) return;
|
||||
@@ -137,4 +138,19 @@ void fatal(logs area, const char *fmt, ...)
|
||||
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
|
||||
|
||||
@@ -77,6 +77,7 @@ private:
|
||||
friend void warn (logs area, const char *fmt, ...);
|
||||
friend void error (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);
|
||||
|
||||
@@ -113,6 +114,8 @@ void warn (logs area, const char *fmt, ...);
|
||||
void error (logs area, const char *fmt, ...);
|
||||
void fatal (logs area, const char *fmt, ...);
|
||||
|
||||
void log (logs area, level severity, const char *fmt, ...);
|
||||
|
||||
} // namespace log
|
||||
|
||||
extern log::logger &g_logger;
|
||||
|
||||
@@ -20,7 +20,8 @@ extern "C" {
|
||||
|
||||
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,
|
||||
// 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::kernel_buffer_pages,
|
||||
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); }
|
||||
@@ -97,30 +98,30 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
|
||||
// Create the heap space and heap allocator
|
||||
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_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::heapmap_offset, heap_map, vm_flags::exact);
|
||||
vm.add(mem::heap_offset, heap, vm_flag_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};
|
||||
|
||||
// Set up the log area and logger
|
||||
size_t log_buffer_size = log::log_pages * arch::frame_size;
|
||||
obj::vm_area *logs = new (&g_kernel_log_area)
|
||||
obj::vm_area_ring(log_buffer_size, vm_flags::write);
|
||||
vm.add(mem::logs_offset, logs, vm_flags::exact);
|
||||
obj::vm_area_ring(log_buffer_size, vm_flag_write);
|
||||
vm.add(mem::logs_offset, logs, vm_flag_exact);
|
||||
|
||||
new (&g_logger) log::logger(
|
||||
util::buffer::from(mem::logs_offset, log_buffer_size));
|
||||
|
||||
// Set up the capability tables
|
||||
obj::vm_area *caps = new (&g_cap_table_area)
|
||||
obj::vm_area_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};
|
||||
|
||||
@@ -128,8 +129,8 @@ memory_initialize_pre_ctors(bootproto::args &kargs)
|
||||
mem::stacks_offset,
|
||||
mem::kernel_stack_pages,
|
||||
mem::stacks_size,
|
||||
vm_flags::write};
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flags::exact);
|
||||
vm_flag_write};
|
||||
vm.add(mem::stacks_offset, &g_kernel_stacks, vm_flag_exact);
|
||||
|
||||
// Clean out any remaning bootloader page table entries
|
||||
for (unsigned i = 0; i < arch::kernel_root_index; ++i)
|
||||
@@ -140,7 +141,7 @@ void
|
||||
memory_initialize_post_ctors(bootproto::args &kargs)
|
||||
{
|
||||
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(
|
||||
get_physical_page(kargs.page_tables.pointer),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <util/counted.h>
|
||||
#include <j6/memutils.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "objects/mailbox.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
@@ -26,6 +27,8 @@ mailbox::close()
|
||||
bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL);
|
||||
if (was_closed) return;
|
||||
|
||||
log::spam(logs::ipc, "mbx[%2x] closing...", obj_id());
|
||||
|
||||
m_callers.clear(j6_status_closed);
|
||||
m_responders.clear(j6_status_closed);
|
||||
|
||||
@@ -44,14 +47,20 @@ mailbox::call()
|
||||
m_callers.add_thread(¤t);
|
||||
|
||||
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);
|
||||
} else {
|
||||
log::spam(logs::ipc, "thread[%2x]:: mbx[%2x] call() found no responder yet.",
|
||||
current.obj_id(), obj_id());
|
||||
}
|
||||
|
||||
return current.block();
|
||||
}
|
||||
|
||||
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())
|
||||
return j6_status_closed;
|
||||
@@ -67,6 +76,9 @@ mailbox::receive(ipc::message &data, reply_tag_t &reply_tag, bool block)
|
||||
if (!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(¤t);
|
||||
j6_status_t s = current.block();
|
||||
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 });
|
||||
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();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
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())
|
||||
return j6_status_closed;
|
||||
@@ -97,6 +112,10 @@ mailbox::reply(reply_tag_t reply_tag, ipc::message &&data)
|
||||
m_reply_map.erase(reply_tag);
|
||||
lock.release();
|
||||
|
||||
thread ¤t = 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->wake(j6_status_ok);
|
||||
return j6_status_ok;
|
||||
|
||||
@@ -22,7 +22,6 @@ class mailbox :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
|
||||
using reply_tag_t = uint64_t;
|
||||
|
||||
/// Capabilities on a newly constructed mailbox handle
|
||||
@@ -48,18 +47,18 @@ public:
|
||||
j6_status_t call();
|
||||
|
||||
/// 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 block True if this call should block when no messages are available.
|
||||
/// \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
|
||||
/// wake the calling read upon destruction.
|
||||
/// \arg reply_tag The reply tag in the original message
|
||||
/// \arg data Message data to pass on to the caller
|
||||
/// \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:
|
||||
wait_queue m_callers;
|
||||
|
||||
@@ -46,7 +46,7 @@ process::create_kernel_process(page_table *pml4)
|
||||
}
|
||||
|
||||
void
|
||||
process::exit(int32_t code)
|
||||
process::exit(int64_t code)
|
||||
{
|
||||
if (m_state == state::exited)
|
||||
return;
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
|
||||
/// Terminate this process.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
void exit(int64_t code);
|
||||
|
||||
/// Get the process' virtual memory space
|
||||
vm_space & space() { return m_space; }
|
||||
@@ -95,7 +95,7 @@ private:
|
||||
// This constructor is called by create_kernel_process
|
||||
process(page_table *kpml4);
|
||||
|
||||
int32_t m_return_code;
|
||||
int64_t m_return_code;
|
||||
|
||||
vm_space m_space;
|
||||
|
||||
|
||||
@@ -111,9 +111,6 @@ thread::wake_only()
|
||||
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
|
||||
thread::exit()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/// Definition of thread kobject types
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/linked_list.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
@@ -122,8 +121,8 @@ public:
|
||||
/// \returns The clock time at which to wake. 0 for no timeout.
|
||||
inline uint64_t wake_timeout() const { return m_wake_timeout; }
|
||||
|
||||
void set_message_data(ipc::message &&md);
|
||||
ipc::message && get_message_data();
|
||||
inline void set_message_data(ipc::message_ptr md) { m_message = util::move(md); }
|
||||
inline ipc::message_ptr get_message_data() { return util::move(m_message); }
|
||||
|
||||
inline bool has_state(state s) const {
|
||||
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_timeout;
|
||||
|
||||
ipc::message m_message;
|
||||
ipc::message_ptr m_message;
|
||||
|
||||
wait_queue m_join_queue;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace obj {
|
||||
|
||||
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_flags {flags},
|
||||
m_spaces {m_vector_static, 0, static_size},
|
||||
@@ -62,7 +62,7 @@ vm_area::can_resize(size_t size)
|
||||
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},
|
||||
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()
|
||||
{
|
||||
if (m_flags && vm_flags::mmio)
|
||||
if (m_flags.get(vm_flags::mmio))
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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}
|
||||
{
|
||||
}
|
||||
@@ -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},
|
||||
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_stacks {start, m_pages*mem::frame_size},
|
||||
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);
|
||||
}
|
||||
|
||||
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},
|
||||
m_bufsize {size}
|
||||
{
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/vector.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
|
||||
#include "block_allocator.h"
|
||||
#include "objects/kobject.h"
|
||||
@@ -17,16 +17,15 @@ class vm_space;
|
||||
|
||||
namespace obj {
|
||||
|
||||
enum class vm_flags : uint32_t
|
||||
enum class vm_flags
|
||||
{
|
||||
#define VM_FLAG(name, v) name = v,
|
||||
#include <j6/tables/vm_flags.inc>
|
||||
#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
|
||||
class vm_area :
|
||||
@@ -40,7 +39,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the 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();
|
||||
|
||||
@@ -48,7 +47,7 @@ public:
|
||||
inline size_t size() const { return m_size; }
|
||||
|
||||
/// 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
|
||||
/// \arg space The space to add this area to
|
||||
@@ -83,7 +82,7 @@ protected:
|
||||
bool can_resize(size_t size);
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
util::bitset32 m_flags;
|
||||
util::vector<vm_space*> m_spaces;
|
||||
|
||||
// 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 size Size of the physical 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 size_t resize(size_t size) override;
|
||||
@@ -122,7 +121,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the 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 bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
@@ -144,7 +143,7 @@ public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the 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 bool add_to(vm_space *space) override;
|
||||
@@ -166,7 +165,7 @@ public:
|
||||
uintptr_t start,
|
||||
size_t sec_pages,
|
||||
size_t size,
|
||||
vm_flags flags);
|
||||
util::bitset32 flags);
|
||||
|
||||
virtual ~vm_area_guarded();
|
||||
|
||||
@@ -194,7 +193,7 @@ public:
|
||||
/// \arg size Virtual size of the ring buffer. Note that
|
||||
/// the VMA size will be double this value.
|
||||
/// \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 bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
@@ -14,10 +14,7 @@ free_page_header * page_table::s_page_cache = nullptr;
|
||||
util::spinlock page_table::s_lock;
|
||||
constexpr size_t page_table::entry_sizes[4];
|
||||
|
||||
|
||||
constexpr page_table::flag table_flags =
|
||||
page_table::flag::present |
|
||||
page_table::flag::write;
|
||||
inline constexpr util::bitset64 table_flags = page_flags::present | page_flags::write;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
uint64_t &parent = entry(l - 1);
|
||||
flag flags = table_flags;
|
||||
util::bitset64 flags = table_flags;
|
||||
if (m_index[0] < arch::kernel_root_index)
|
||||
flags |= flag::user;
|
||||
flags.set(flag::user);
|
||||
|
||||
m_table[unsigned(l)] = table;
|
||||
parent = (phys & ~0xfffull) | flags;
|
||||
|
||||
@@ -4,11 +4,33 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/memory.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
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 page_table
|
||||
{
|
||||
@@ -16,27 +38,19 @@ struct page_table
|
||||
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
||||
|
||||
/// Page entry flags
|
||||
enum class flag : uint64_t
|
||||
enum class flag
|
||||
{
|
||||
none = 0x0000,
|
||||
present = 0x0001, /// Entry is present in the table
|
||||
write = 0x0002, /// Section may be written
|
||||
user = 0x0004, /// User-accessible
|
||||
pat0 = 0x0008, /// PAT selector bit 0
|
||||
pat1 = 0x0010, /// PAT selector bit 1
|
||||
accessed = 0x0020, /// Entry has been accessed
|
||||
dirty = 0x0040, /// Page has been written to
|
||||
page = 0x0080, /// Entry is a large page
|
||||
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
||||
global = 0x0100, /// Entry is not PCID-specific
|
||||
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,
|
||||
present = 0, /// Entry is present in the table
|
||||
write = 1, /// Section may be written
|
||||
user = 2, /// User-accessible
|
||||
pat0 = 3, /// PAT selector bit 0
|
||||
pat1 = 4, /// PAT selector bit 1
|
||||
accessed = 5, /// Entry has been accessed
|
||||
dirty = 6, /// Page has been written to
|
||||
page = 7, /// Entry is a large page
|
||||
pat2 = 7, /// PAT selector bit 2 on PT entries
|
||||
global = 8, /// Entry is not PCID-specific
|
||||
pat2_lg = 12, /// PAT selector bit 2 on large/huge pages
|
||||
};
|
||||
|
||||
/// 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; }
|
||||
|
||||
is_bitfield(page_table::flag);
|
||||
|
||||
@@ -339,5 +339,5 @@ scheduler::maybe_schedule(TCB *t)
|
||||
return;
|
||||
|
||||
current_cpu().apic->send_ipi(
|
||||
lapic::ipi::fixed, isr::ipiSchedule, cpu->id);
|
||||
lapic::ipi_fixed, isr::ipiSchedule, cpu->id);
|
||||
}
|
||||
|
||||
@@ -57,8 +57,11 @@ start(cpu_data &bsp, void *kpml4)
|
||||
// Copy the startup code somwhere the real mode trampoline can run
|
||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||
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(
|
||||
reinterpret_cast<void*>(addr),
|
||||
reinterpret_cast<void*>(&ap_startup),
|
||||
@@ -67,7 +70,7 @@ start(cpu_data &bsp, void *kpml4)
|
||||
size_t free_stack_count = 0;
|
||||
|
||||
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));
|
||||
|
||||
for (uint8_t id : ids) {
|
||||
@@ -90,7 +93,7 @@ start(cpu_data &bsp, void *kpml4)
|
||||
size_t current_count = ap_startup_count;
|
||||
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);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
|
||||
@@ -32,4 +32,13 @@ handle_clone(j6_handle_t orig, j6_handle_t *clone, uint32_t mask)
|
||||
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
|
||||
|
||||
@@ -35,16 +35,17 @@ mailbox_call(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count)
|
||||
size_t *handles_count,
|
||||
size_t handles_size)
|
||||
{
|
||||
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};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message = new ipc::message {*tag, data, handles};
|
||||
cur.set_message_data(util::move(message));
|
||||
|
||||
j6_status_t s = self->call();
|
||||
@@ -52,19 +53,20 @@ mailbox_call(
|
||||
return s;
|
||||
|
||||
message = cur.get_message_data();
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
if (message->handle_count) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
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_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
@@ -75,18 +77,20 @@ mailbox_respond(
|
||||
uint64_t *tag,
|
||||
void *in_data,
|
||||
size_t *data_len,
|
||||
size_t data_in_len,
|
||||
size_t data_size,
|
||||
j6_handle_t *in_handles,
|
||||
size_t *handles_count,
|
||||
size_t handles_size,
|
||||
uint64_t *reply_tag,
|
||||
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};
|
||||
|
||||
ipc::message message(*tag, data, handles);
|
||||
ipc::message_ptr message;
|
||||
|
||||
if (*reply_tag) {
|
||||
message = new ipc::message {*tag, data, handles};
|
||||
j6_status_t s = self->reply(*reply_tag, util::move(message));
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -97,18 +101,20 @@ mailbox_respond(
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (message.handles) {
|
||||
for (unsigned i = 0; i < message.handles.count; ++i)
|
||||
process::current().add_handle(message.handles[i]);
|
||||
util::counted<j6_handle_t> msg_handles = message->handles();
|
||||
util::buffer msg_data = message->data();
|
||||
|
||||
if (msg_handles) {
|
||||
for (unsigned i = 0; i < msg_handles.count; ++i)
|
||||
process::current().add_handle(msg_handles[i]);
|
||||
}
|
||||
|
||||
*tag = message.tag;
|
||||
*data_len = *data_len > message.data.count ? message.data.count : *data_len;
|
||||
memcpy(in_data, message.data.pointer, *data_len);
|
||||
*tag = message->tag;
|
||||
*data_len = data_size > msg_data.count ? msg_data.count : data_size;
|
||||
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_min;
|
||||
memcpy(in_handles, message.handles.pointer, handles_min * sizeof(j6_handle_t));
|
||||
*handles_count = handles_size > msg_handles.count ? msg_handles.count : handles_size;
|
||||
memcpy(in_handles, msg_handles.pointer, *handles_count * sizeof(j6_handle_t));
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ process_kill(process *self)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_exit(int32_t status)
|
||||
process_exit(int64_t status)
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
@@ -21,10 +21,11 @@ namespace syscalls {
|
||||
using system = class ::system;
|
||||
|
||||
j6_status_t
|
||||
log(const char *message)
|
||||
log(uint8_t area, uint8_t severity, const char *message)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 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));
|
||||
|
||||
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask);
|
||||
construct_handle<vm_area_fixed>(area, phys, size, vmf);
|
||||
|
||||
construct_handle<vm_area_fixed>(area, phys, size, f);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
vma_create(j6_handle_t *self, size_t size, uint32_t flags)
|
||||
{
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
if (util::bits::has(f, vm_flags::ring))
|
||||
util::bitset32 f = flags & vm_user_mask;
|
||||
if (f.get(vm_flags::ring))
|
||||
construct_handle<vm_area_ring>(self, size, f);
|
||||
else
|
||||
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)
|
||||
{
|
||||
vm_area *a = nullptr;
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
if (util::bits::has(f, vm_flags::ring))
|
||||
util::bitset32 f = flags & vm_user_mask;
|
||||
if (f.get(vm_flags::ring))
|
||||
a = construct_handle<vm_area_ring>(self, size, f);
|
||||
else
|
||||
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)
|
||||
{
|
||||
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);
|
||||
return *base ? j6_status_ok : j6_err_collision;
|
||||
}
|
||||
|
||||
@@ -55,9 +55,9 @@ vm_space::vm_space() :
|
||||
obj::vm_area *sysc = new obj::vm_area_fixed(
|
||||
g_sysconf_phys,
|
||||
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()
|
||||
@@ -81,21 +81,20 @@ vm_space::kernel_space()
|
||||
}
|
||||
|
||||
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)
|
||||
base = min_auto_address;
|
||||
|
||||
uintptr_t end = base + area->size();
|
||||
|
||||
//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) {
|
||||
const vm_space::area &a = m_areas[i];
|
||||
uintptr_t aend = a.base + a.area->size();
|
||||
if (base >= aend)
|
||||
continue;
|
||||
|
||||
uintptr_t end = base + area->size();
|
||||
if (end <= a.base)
|
||||
break;
|
||||
else if (exact)
|
||||
@@ -192,7 +191,7 @@ vm_space::copy_from(const vm_space &source, const obj::vm_area &vma)
|
||||
|
||||
while (count--) {
|
||||
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
|
||||
}
|
||||
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;
|
||||
|
||||
uintptr_t virt = base + offset;
|
||||
page_table::flag flags =
|
||||
page_table::flag::present |
|
||||
(m_kernel ? page_table::flag::none : page_table::flag::user) |
|
||||
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) |
|
||||
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none);
|
||||
util::bitset64 flags =
|
||||
page_flags::present |
|
||||
(m_kernel ? page_flags::none : page_flags::user) |
|
||||
(vma.flags().get(vm_flags::write) ? page_flags::write : page_flags::none) |
|
||||
(vma.flags().get(vm_flags::write_combine) ? page_flags::wc : page_flags::none);
|
||||
|
||||
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);
|
||||
entry = (phys + i * frame_size) | flags;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -247,11 +246,11 @@ vm_space::clear(const obj::vm_area &vma, uintptr_t offset, size_t count, bool fr
|
||||
while (count--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
util::bitset64 flags = e;
|
||||
|
||||
if (e & page_table::flag::present) {
|
||||
uint64_t orig = e;
|
||||
if (flags & page_flags::present) {
|
||||
e = 0;
|
||||
if (orig & page_table::flag::accessed) {
|
||||
if (flags & page_flags::accessed) {
|
||||
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
|
||||
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--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
util::bitset64 flags = e;
|
||||
|
||||
if (e & page_table::flag::present) {
|
||||
uint64_t orig = e;
|
||||
if (flags & page_flags::present) {
|
||||
e = locked_page_tag;
|
||||
if (orig & page_table::flag::accessed) {
|
||||
if (flags & page_flags::accessed) {
|
||||
auto *addr = reinterpret_cast<const uint8_t *>(it.vaddress());
|
||||
asm ( "invlpg %0" :: "m"(*addr) : "memory" );
|
||||
}
|
||||
@@ -338,10 +337,10 @@ vm_space::initialize_tcb(TCB &tcb)
|
||||
}
|
||||
|
||||
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
|
||||
if (fault && fault_type::present)
|
||||
if (fault.get(fault_type::present))
|
||||
return false;
|
||||
|
||||
uintptr_t page = (addr & ~0xfffull);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <j6/flags.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
#include <util/spinlock.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
/// \arg area The area to add
|
||||
/// \arg flags Flags for the operation (exact, clobber, etc)
|
||||
/// \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
|
||||
/// \arg area The area to remove
|
||||
@@ -88,15 +88,6 @@ public:
|
||||
/// Set this space as the current active space
|
||||
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.
|
||||
/// \arg virt The virtual address at which to allocate
|
||||
/// \arg count The number of pages to allocate
|
||||
@@ -104,11 +95,13 @@ public:
|
||||
/// \returns The number of pages actually allocated
|
||||
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.
|
||||
/// \arg addr Address which caused the fault
|
||||
/// \arg ft Flags from the interrupt about the kind of fault
|
||||
/// \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.
|
||||
void initialize_tcb(TCB &tcb);
|
||||
@@ -155,6 +148,4 @@ private:
|
||||
util::vector<area> m_areas;
|
||||
|
||||
util::spinlock m_lock;
|
||||
};
|
||||
|
||||
is_bitfield(vm_space::fault_type);
|
||||
};
|
||||
@@ -3,15 +3,9 @@
|
||||
/// Data structures for reading jsix_boot.dat
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
|
||||
namespace bootproto {
|
||||
|
||||
enum class desc_flags : uint16_t {
|
||||
graphical = 0x0001,
|
||||
panic = 0x0002,
|
||||
symbols = 0x0004,
|
||||
};
|
||||
is_bitfield(desc_flags);
|
||||
enum class desc_flags { graphical, panic, symbols };
|
||||
|
||||
} // namespace bootproto
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/bitset.h>
|
||||
#include <util/counted.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
|
||||
namespace bootproto {
|
||||
|
||||
@@ -18,19 +18,13 @@ constexpr uint64_t header_magic = 0x4c454e52454b366aull; // 'j6KERNEL'
|
||||
constexpr uint16_t header_version = 2;
|
||||
constexpr uint16_t min_header_version = 2;
|
||||
|
||||
enum class section_flags : uint32_t {
|
||||
none = 0,
|
||||
execute = 1,
|
||||
write = 2,
|
||||
read = 4,
|
||||
};
|
||||
is_bitfield(section_flags);
|
||||
enum class section_flags { none, execute, write, read };
|
||||
|
||||
struct program_section {
|
||||
uintptr_t phys_addr;
|
||||
uintptr_t virt_addr;
|
||||
uint32_t size;
|
||||
section_flags type;
|
||||
util::bitset32 type;
|
||||
};
|
||||
|
||||
struct program {
|
||||
@@ -112,18 +106,13 @@ struct frame_block
|
||||
uint64_t *bitmap;
|
||||
};
|
||||
|
||||
enum class boot_flags : uint16_t {
|
||||
none = 0x0000,
|
||||
debug = 0x0001,
|
||||
test = 0x0002,
|
||||
};
|
||||
is_bitfield(boot_flags);
|
||||
enum class boot_flags { none, debug, test };
|
||||
|
||||
struct args
|
||||
{
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
boot_flags flags;
|
||||
util::bitset16 flags;
|
||||
|
||||
void *pml4;
|
||||
util::counted<void> page_tables;
|
||||
|
||||
@@ -18,7 +18,7 @@ enum class feature {
|
||||
max
|
||||
};
|
||||
|
||||
using features = util::bitset<(unsigned)feature::max>;
|
||||
using features = util::sized_bitset<(unsigned)feature::max>;
|
||||
|
||||
class cpu_id
|
||||
{
|
||||
|
||||
11
src/libraries/edit/edit.module
Normal file
11
src/libraries/edit/edit.module
Normal file
@@ -0,0 +1,11 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("edit",
|
||||
kind = "lib",
|
||||
deps = [ "j6", "libc" ],
|
||||
sources = [
|
||||
"line.cpp",
|
||||
],
|
||||
public_headers = [
|
||||
"edit/line.h",
|
||||
])
|
||||
34
src/libraries/edit/include/edit/line.h
Normal file
34
src/libraries/edit/include/edit/line.h
Normal 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
111
src/libraries/edit/line.cpp
Normal 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
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
namespace elf {
|
||||
|
||||
@@ -54,19 +54,12 @@ struct file_header
|
||||
|
||||
|
||||
enum class segment_type : uint32_t { null, load, dynamic, interpreter, note };
|
||||
enum class segment_flags : uint32_t
|
||||
{
|
||||
none = 0x00,
|
||||
exec = 0x01,
|
||||
write = 0x02,
|
||||
read = 0x04,
|
||||
};
|
||||
is_bitfield(segment_flags);
|
||||
enum class segment_flags { exec, write, read };
|
||||
|
||||
struct segment_header
|
||||
{
|
||||
segment_type type;
|
||||
segment_flags flags;
|
||||
util::bitset32 flags;
|
||||
uint64_t offset;
|
||||
|
||||
uint64_t vaddr;
|
||||
@@ -80,18 +73,13 @@ struct segment_header
|
||||
|
||||
|
||||
enum class section_type : uint32_t { null, progbits };
|
||||
enum class section_flags : uint64_t
|
||||
{
|
||||
write = 0x01,
|
||||
alloc = 0x02,
|
||||
exec = 0x04,
|
||||
};
|
||||
enum class section_flags { write, alloc, exec };
|
||||
|
||||
struct section_header
|
||||
{
|
||||
uint32_t name_offset;
|
||||
section_type type;
|
||||
section_flags flags;
|
||||
util::bitset64 flags;
|
||||
uint64_t addr;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
@@ -102,5 +90,3 @@ struct section_header
|
||||
} __attribute__ ((packed));
|
||||
|
||||
} // namespace elf
|
||||
|
||||
is_bitfield(elf::section_flags);
|
||||
|
||||
@@ -12,136 +12,180 @@
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/spinlock.h>
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/new.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
static uintptr_t channel_addr = 0x6000'0000;
|
||||
static util::spinlock addr_spinlock;
|
||||
|
||||
struct channel::header
|
||||
struct channel_memory_area
|
||||
{
|
||||
size_t size;
|
||||
size_t read_index;
|
||||
size_t write_index;
|
||||
|
||||
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; }
|
||||
j6::mutex mutex;
|
||||
j6::condition waiting;
|
||||
util::bip_buffer buf;
|
||||
};
|
||||
|
||||
channel *
|
||||
channel::create(size_t size)
|
||||
|
||||
static bool
|
||||
check_channel_size(size_t s)
|
||||
{
|
||||
j6_status_t result;
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
|
||||
if (size < arch::frame_size || (size & (size - 1)) != 0) {
|
||||
syslog("Bad channel size: %lx", size);
|
||||
return nullptr;
|
||||
if (s < arch::frame_size || (s & (s - 1)) != 0) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s);
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->size = size;
|
||||
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
||||
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
||||
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::open(j6_handle_t vma)
|
||||
channel::open(const channel_def &def)
|
||||
{
|
||||
j6_status_t result;
|
||||
|
||||
util::scoped_lock lock {addr_spinlock};
|
||||
uintptr_t addr = channel_addr;
|
||||
|
||||
result = j6_vma_map(vma, 0, &addr, 0);
|
||||
uintptr_t tx_addr = 0;
|
||||
result = j6_vma_map(def.tx, 0, &tx_addr, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
channel_addr += h->size;
|
||||
lock.release();
|
||||
uintptr_t rx_addr = 0;
|
||||
result = j6_vma_map(def.rx, 0, &rx_addr, 0);
|
||||
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) :
|
||||
m_vma {vma},
|
||||
m_size {h->size},
|
||||
m_header {h}
|
||||
channel::channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
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;
|
||||
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (m_header->write_avail() < len) {
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
while (m_tx.buf.write_available() < size) {
|
||||
if (!block) return 0;
|
||||
lock.release();
|
||||
m_header->write_waiting.wait();
|
||||
m_tx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
memcpy(m_header->write_at(), buffer, len);
|
||||
m_header->commit(len);
|
||||
m_header->read_waiting.wake();
|
||||
|
||||
return j6_status_ok;
|
||||
return m_tx.buf.reserve(size, area);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::receive(void *buffer, size_t *size, bool block)
|
||||
void
|
||||
channel::commit(size_t size)
|
||||
{
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (!m_header->read_avail()) {
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
m_tx.buf.commit(size);
|
||||
if (!size) return;
|
||||
|
||||
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();
|
||||
m_header->read_waiting.wait();
|
||||
m_rx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
size_t avail = m_header->read_avail();
|
||||
size_t read = *size > avail ? avail : *size;
|
||||
return m_rx.buf.get_block(area);
|
||||
}
|
||||
|
||||
memcpy(buffer, m_header->read_at(), read);
|
||||
m_header->consume(read);
|
||||
m_header->write_waiting.wake();
|
||||
void
|
||||
channel::consume(size_t size)
|
||||
{
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
m_rx.buf.consume(size);
|
||||
if (!size) return;
|
||||
|
||||
*size = read;
|
||||
return j6_status_ok;
|
||||
m_rx.waiting.wake();
|
||||
j6::syslog(logs::ipc, log_level::spam,
|
||||
"Read %d bytes from channel on {%x}", size, m_def.rx);
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace j6 {
|
||||
void
|
||||
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);
|
||||
j6_status_t s = j6_futex_wait(&m_state, v, 0);
|
||||
while (s == j6_status_futex_changed) {
|
||||
@@ -20,7 +20,7 @@ condition::wait()
|
||||
if (v == 0) break;
|
||||
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
|
||||
|
||||
@@ -10,41 +10,66 @@
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
|
||||
namespace util {
|
||||
class bip_buffer;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
/// Create a new channel of the given size.
|
||||
static channel * create(size_t size);
|
||||
/// Create a new channel.
|
||||
/// \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
|
||||
static channel * open(j6_handle_t vma);
|
||||
/// Open an existing channel for which we have VMA handles
|
||||
static channel * open(const channel_def &def);
|
||||
|
||||
/// Send data into the channel.
|
||||
/// \arg buffer The buffer from which to read data
|
||||
/// \arg len The number of bytes to read from `buffer`
|
||||
/// \arg block If true, block this thread if there aren't `len` bytes of space available
|
||||
j6_status_t send(const void *buffer, size_t len, bool block = true);
|
||||
/// Reserve an area of the output buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \arg block If true, block this thread until there are size bytes free
|
||||
/// \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.
|
||||
/// \arg buffer The buffer to receive channel data
|
||||
/// \arg size [in] The size of `buffer` [out] the amount read into `buffer`
|
||||
/// \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);
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
void commit(size_t size);
|
||||
|
||||
/// Get the VMA handle for sharing with other processes
|
||||
j6_handle_t handle() const { return m_vma; }
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \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:
|
||||
struct header;
|
||||
channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
channel_memory_area &rx);
|
||||
|
||||
channel(j6_handle_t vma, header *h);
|
||||
|
||||
j6_handle_t m_vma;
|
||||
|
||||
size_t m_size;
|
||||
header *m_header;
|
||||
channel_def m_def;
|
||||
channel_memory_area &m_tx;
|
||||
channel_memory_area &m_rx;
|
||||
};
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
/// Enums used as flags for syscalls
|
||||
|
||||
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>
|
||||
#undef VM_FLAG
|
||||
j6_vm_flag_MAX
|
||||
|
||||
@@ -33,6 +33,13 @@ struct j6_arg_header
|
||||
{
|
||||
uint32_t size;
|
||||
uint16_t type;
|
||||
uint16_t reserved;
|
||||
j6_arg_header *next;
|
||||
};
|
||||
|
||||
struct j6_arg_none
|
||||
{
|
||||
add_header(none);
|
||||
};
|
||||
|
||||
struct j6_arg_loader
|
||||
@@ -43,7 +50,6 @@ struct j6_arg_loader
|
||||
uintptr_t *got;
|
||||
uintptr_t entrypoint;
|
||||
uintptr_t start_addr;
|
||||
uintptr_t other;
|
||||
};
|
||||
|
||||
struct j6_arg_driver
|
||||
@@ -68,14 +74,17 @@ struct j6_arg_handles
|
||||
|
||||
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
|
||||
j6_handle_t API j6_find_first_handle(j6_object_type obj_type);
|
||||
|
||||
/// Find the first handle tagged with the given proto in the process init args
|
||||
j6_handle_t API j6_find_init_handle(uint64_t proto);
|
||||
|
||||
/// Get the init args
|
||||
const j6_init_args * j6_get_init_args();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <j6/protocols/service_locator.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace j6::proto::sl {
|
||||
|
||||
@@ -13,16 +14,16 @@ public:
|
||||
|
||||
/// Register a handle as a service with the locator.
|
||||
/// \arg proto_id The protocol this handle supports
|
||||
/// \arg handle The mailbox or channel handle
|
||||
j6_status_t register_service(uint64_t proto_id, j6_handle_t handle);
|
||||
/// \arg handles The mailbox or channel handles
|
||||
j6_status_t register_service(uint64_t proto_id, util::counted<j6_handle_t> handles);
|
||||
|
||||
/// Look up a handle with the locator service.
|
||||
/// \arg proto_id The protocol to look for
|
||||
/// \arg handle [out] The mailbox or channel handle
|
||||
j6_status_t lookup_service(uint64_t proto_id, j6_handle_t &handle);
|
||||
/// \arg handles [out] The mailbox or channel handles
|
||||
j6_status_t lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles);
|
||||
|
||||
private:
|
||||
j6_handle_t m_service;
|
||||
};
|
||||
|
||||
} // namespace j6::proto::sl
|
||||
} // namespace j6::proto::sl
|
||||
|
||||
55
src/libraries/j6/include/j6/ring_buffer.hh
Normal file
55
src/libraries/j6/include/j6/ring_buffer.hh
Normal 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
|
||||
|
||||
@@ -2,15 +2,26 @@
|
||||
/// \file j6/syslog.hh
|
||||
/// Utility function for writing messages to the kernel log
|
||||
|
||||
#include <util/api.h>
|
||||
|
||||
// The kernel depends on libj6 for some shared code,
|
||||
// but should not include the user-specific code.
|
||||
#ifndef __j6kernel
|
||||
#include <stdint.h>
|
||||
#include <util/api.h>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
LOG(apic, info)
|
||||
LOG(boot, info)
|
||||
LOG(device, spam)
|
||||
LOG(device, info)
|
||||
LOG(irq, info)
|
||||
LOG(memory, info)
|
||||
LOG(objs, info)
|
||||
@@ -9,3 +9,7 @@ LOG(sched, info)
|
||||
LOG(syscall,info)
|
||||
LOG(task, verbose)
|
||||
LOG(timer, info)
|
||||
LOG(ipc, spam)
|
||||
LOG(app, spam)
|
||||
LOG(proto, spam)
|
||||
LOG(srv, info)
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
VM_FLAG( none, 0x00000000 )
|
||||
VM_FLAG( write, 0 )
|
||||
VM_FLAG( exec, 1 )
|
||||
|
||||
VM_FLAG( write, 0x00000001 )
|
||||
VM_FLAG( exec, 0x00000002 )
|
||||
VM_FLAG( contiguous, 4 )
|
||||
VM_FLAG( large_pages, 5 )
|
||||
VM_FLAG( huge_pages, 6 )
|
||||
|
||||
VM_FLAG( contiguous, 0x00000010 )
|
||||
VM_FLAG( large_pages, 0x00000020 )
|
||||
VM_FLAG( huge_pages, 0x00000040 )
|
||||
VM_FLAG( write_combine, 8 )
|
||||
|
||||
VM_FLAG( write_combine, 0x00000100 )
|
||||
VM_FLAG( mmio, 12 )
|
||||
|
||||
VM_FLAG( mmio, 0x00001000 )
|
||||
|
||||
VM_FLAG( exact, 0x00010000 )
|
||||
VM_FLAG( ring, 0x00020000 )
|
||||
VM_FLAG( exact, 16 )
|
||||
VM_FLAG( ring, 17 )
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace {
|
||||
constexpr size_t static_arr_count = 32;
|
||||
j6_handle_descriptor handle_array[static_arr_count];
|
||||
j6_init_args init_args;
|
||||
j6_init_args init_args = { 0, 0, 0 };
|
||||
} // namespace
|
||||
|
||||
j6_handle_t
|
||||
@@ -36,6 +36,26 @@ j6_find_first_handle(j6_object_type obj_type)
|
||||
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
|
||||
j6_get_init_args()
|
||||
{
|
||||
@@ -43,10 +63,11 @@ j6_get_init_args()
|
||||
}
|
||||
|
||||
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.args[1] = arg1;
|
||||
init_args.argv[0] = argv0;
|
||||
init_args.argv[1] = argv1;
|
||||
init_args.args = args;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ j6 = module("j6",
|
||||
"condition.cpp",
|
||||
"init.cpp",
|
||||
"memutils.cpp",
|
||||
"memutils.s",
|
||||
"mutex.cpp",
|
||||
"protocol_ids.cpp",
|
||||
"protocols/service_locator.cpp",
|
||||
"protocols/vfs.cpp",
|
||||
"ring_buffer.cpp",
|
||||
"syscalls.s.cog",
|
||||
"sysconf.cpp.cog",
|
||||
"syslog.cpp",
|
||||
@@ -28,6 +30,7 @@ j6 = module("j6",
|
||||
"j6/protocols.h",
|
||||
"j6/protocols/service_locator.h",
|
||||
"j6/protocols/service_locator.hh",
|
||||
"j6/ring_buffer.hh",
|
||||
"j6/syscalls.h.cog",
|
||||
"j6/sysconf.h.cog",
|
||||
"j6/syslog.hh",
|
||||
|
||||
@@ -7,14 +7,6 @@
|
||||
using namespace j6;
|
||||
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) {
|
||||
if (s1 == s2) return;
|
||||
|
||||
|
||||
7
src/libraries/j6/memutils.s
Normal file
7
src/libraries/j6/memutils.s
Normal file
@@ -0,0 +1,7 @@
|
||||
global memcpy: function (memcpy.end - memcpy)
|
||||
memcpy:
|
||||
mov rax, rdi
|
||||
mov rcx, rdx
|
||||
rep movsb
|
||||
ret
|
||||
memcpy.end:
|
||||
@@ -13,16 +13,15 @@ client::client(j6_handle_t slp_mb) :
|
||||
}
|
||||
|
||||
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;
|
||||
size_t handle_count = 1;
|
||||
size_t data = proto_id;
|
||||
size_t data_size = sizeof(proto_id);
|
||||
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
&data, &data_size, data_size,
|
||||
&handle, &handle_count);
|
||||
handles.pointer, &handles.count, handles.count);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -34,31 +33,35 @@ client::register_service(uint64_t proto_id, j6_handle_t handle)
|
||||
}
|
||||
|
||||
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;
|
||||
size_t handle_count = 1;
|
||||
size_t data = 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,
|
||||
&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;
|
||||
}
|
||||
|
||||
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 j6_err_unexpected;
|
||||
}
|
||||
|
||||
|
||||
} // namespace j6::proto::sl
|
||||
#endif // __j6kernel
|
||||
#endif // __j6kernel
|
||||
|
||||
@@ -21,7 +21,7 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
uint64_t tag = j6_proto_vfs_load;
|
||||
size_t handle_count = 1;
|
||||
size_t handle_count = 0;
|
||||
vma = j6_handle_invalid;
|
||||
|
||||
// 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,
|
||||
data, &data_len, path_len,
|
||||
&vma, &handle_count);
|
||||
&vma, &handle_count, 1);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (tag == j6_proto_vfs_file) {
|
||||
if (tag == j6_proto_vfs_file && handle_count == 1) {
|
||||
size = 0;
|
||||
|
||||
// Get the size into `size`
|
||||
|
||||
38
src/libraries/j6/ring_buffer.cpp
Normal file
38
src/libraries/j6/ring_buffer.cpp
Normal 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
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace j6 {
|
||||
|
||||
void
|
||||
syslog(const char *fmt, ...)
|
||||
syslog(logs area, log_level severity, const char *fmt, ...)
|
||||
{
|
||||
char buffer[200];
|
||||
|
||||
@@ -19,7 +19,7 @@ syslog(const char *fmt, ...)
|
||||
va_end(va);
|
||||
|
||||
buffer[n] = 0;
|
||||
j6_log(buffer);
|
||||
j6_log(static_cast<uint8_t>(area), static_cast<uint8_t>(severity), buffer);
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -18,6 +18,8 @@ global _libc_crt0_start:function (_libc_crt0_start.end - _libc_crt0_start)
|
||||
|
||||
_start:
|
||||
_libc_crt0_start:
|
||||
mov rdx, [rsp] ; grab args pointer
|
||||
|
||||
push 0 ; Add null frame
|
||||
push 0
|
||||
mov rbp, rsp
|
||||
|
||||
@@ -9,16 +9,11 @@ extern cb __init_array_end;
|
||||
namespace {
|
||||
|
||||
void
|
||||
run_ctor_list(cb *array, cb *end)
|
||||
run_ctor_list(cb *p, cb *end)
|
||||
{
|
||||
if (!array || !end)
|
||||
return;
|
||||
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const cb &ctor = array[i++];
|
||||
if (&ctor == end) return;
|
||||
if (ctor) ctor();
|
||||
while (p && end && p < end) {
|
||||
if (p) (*p)();
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +26,8 @@ run_global_ctors()
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C"
|
||||
void __init_libc()
|
||||
extern "C" void
|
||||
__init_libc()
|
||||
{
|
||||
run_global_ctors();
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ extern "C" {
|
||||
// we have a real libdl
|
||||
int dladdr(const void *, void *) { return 0; }
|
||||
int dl_iterate_phdr(void *, void *) { return 0; }
|
||||
int dlsym(void *, const char *) { return 0; }
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ void* pvalloc(size_t);
|
||||
_Noreturn void abort( void );
|
||||
int atexit( void (*func)(void) );
|
||||
int at_quick_exit( void (*func)(void) );
|
||||
_Noreturn void exit( int status );
|
||||
_Noreturn void _Exit( int status );
|
||||
_Noreturn void exit( long status );
|
||||
_Noreturn void _Exit( long status );
|
||||
char *getenv( const char *name );
|
||||
_Noreturn void quick_exit( int status );
|
||||
_Noreturn void quick_exit( long status );
|
||||
int system( const char *string );
|
||||
|
||||
// Searching and sorting utilities
|
||||
|
||||
@@ -10,4 +10,43 @@
|
||||
* 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);
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
#include <__j6libc/wchar_t.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef unsigned int wint_t;
|
||||
|
||||
#define WEOF ((wint_t)-1)
|
||||
|
||||
struct tm;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -10,4 +10,7 @@
|
||||
* 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;
|
||||
|
||||
@@ -5420,6 +5420,8 @@ void* dlrealloc_in_place(void* oldmem, size_t bytes) {
|
||||
return mem;
|
||||
}
|
||||
|
||||
void* aligned_alloc(size_t alignment, size_t size) { return dlmemalign(alignment, size); }
|
||||
|
||||
void* dlmemalign(size_t alignment, size_t bytes) {
|
||||
if (alignment <= MALLOC_ALIGNMENT) {
|
||||
return dlmalloc(bytes);
|
||||
|
||||
@@ -21,7 +21,7 @@ size_t quick_exit_count = 0;
|
||||
atexit_item quick_exit_array[max_atexit];
|
||||
|
||||
[[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) {
|
||||
atexit_item &item = cbs[i];
|
||||
@@ -61,13 +61,13 @@ int at_quick_exit( void (*func)(void) )
|
||||
}
|
||||
|
||||
void
|
||||
exit(int status)
|
||||
exit(long status)
|
||||
{
|
||||
exit_with_callbacks(status, atexit_array, atexit_count);
|
||||
}
|
||||
|
||||
void
|
||||
quick_exit(int status)
|
||||
quick_exit(long status)
|
||||
{
|
||||
exit_with_callbacks(status, quick_exit_array, quick_exit_count);
|
||||
}
|
||||
@@ -75,11 +75,11 @@ quick_exit(int status)
|
||||
void
|
||||
abort()
|
||||
{
|
||||
_Exit(INT32_MIN);
|
||||
_Exit(INT64_MIN);
|
||||
}
|
||||
|
||||
void
|
||||
_Exit( int status )
|
||||
_Exit( long status )
|
||||
{
|
||||
j6_process_exit(status);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ int strncmp(const char *s1, const char *s2, size_t n) {
|
||||
char const * c1 = s1;
|
||||
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 (!*c2 || *c1 > *c2) return 1;
|
||||
|
||||
@@ -14,5 +14,9 @@
|
||||
typedef __WCHAR_TYPE__ wchar_t;
|
||||
#endif
|
||||
|
||||
typedef unsigned int wint_t;
|
||||
|
||||
#define WEOF ((wint_t)-1)
|
||||
|
||||
#define WCHAR_MAX __WCHAR_MAX__
|
||||
#define WCHAR_MIN ((-__WCHAR_MAX__) - 1)
|
||||
|
||||
@@ -8,27 +8,37 @@
|
||||
|
||||
namespace util {
|
||||
|
||||
bip_buffer::bip_buffer() :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(0),
|
||||
m_buffer(nullptr)
|
||||
{}
|
||||
bip_buffer::bip_buffer(size_t size) :
|
||||
m_start_a {0},
|
||||
m_start_b {0},
|
||||
m_size_a {0},
|
||||
m_size_b {0},
|
||||
m_size_r {0},
|
||||
m_buffer_size {size - sizeof(bip_buffer)}
|
||||
{}
|
||||
|
||||
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(size),
|
||||
m_buffer(buffer)
|
||||
{}
|
||||
size_t
|
||||
bip_buffer::write_available() const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
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};
|
||||
|
||||
@@ -84,7 +94,7 @@ void bip_buffer::commit(size_t size)
|
||||
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};
|
||||
|
||||
|
||||
@@ -14,17 +14,14 @@ namespace util {
|
||||
class API bip_buffer
|
||||
{
|
||||
public:
|
||||
/// Default constructor. Creates a zero-size buffer.
|
||||
bip_buffer();
|
||||
|
||||
/// Constructor.
|
||||
bip_buffer(uint8_t *buffer, size_t size);
|
||||
bip_buffer(size_t size);
|
||||
|
||||
/// Reserve an area of buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \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()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
@@ -33,7 +30,7 @@ public:
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \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
|
||||
/// \arg size Number of bytes to consume
|
||||
@@ -47,6 +44,13 @@ public:
|
||||
/// \returns Number of bytes of buffer that are free
|
||||
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:
|
||||
size_t m_start_a;
|
||||
size_t m_start_b;
|
||||
@@ -56,9 +60,10 @@ private:
|
||||
size_t m_size_r;
|
||||
|
||||
mutable spinlock m_lock;
|
||||
|
||||
const size_t m_buffer_size;
|
||||
uint8_t * const m_buffer;
|
||||
uint8_t m_buffer[0];
|
||||
|
||||
bip_buffer() = delete;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
@@ -7,34 +7,148 @@
|
||||
namespace util {
|
||||
|
||||
/// A statically-sized templated bitset
|
||||
template <unsigned N>
|
||||
template <typename I>
|
||||
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;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
__attribute__ ((always_inline))
|
||||
inline bool get(T i) const {
|
||||
return bits(i) & bit(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & set(T i) {
|
||||
__attribute__ ((always_inline))
|
||||
inline sized_bitset & set(T i) {
|
||||
bits(i) |= bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & clear(T i) {
|
||||
__attribute__ ((always_inline))
|
||||
inline sized_bitset & clear(T i) {
|
||||
bits(i) &= ~bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
__attribute__ ((always_inline))
|
||||
inline bool operator[](T i) const { return get(i); }
|
||||
|
||||
inline bool empty() const {
|
||||
@@ -45,260 +159,18 @@ public:
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
__attribute__ ((always_inline))
|
||||
inline uint64_t bit(T i) const { return (1ull << (static_cast<uint64_t>(i) & 63)); }
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
__attribute__ ((always_inline))
|
||||
inline uint64_t &bits(T i) { return m_bits[static_cast<uint64_t>(i) >> 6]; }
|
||||
|
||||
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]; }
|
||||
|
||||
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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user