mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[project] Generate syscalls from new interface DSL
This change adds a new interface DSL for specifying objects (with methods) and interfaces (that expose objects, and optionally have their own methods). Significant changes: - Add the new scripts/definitions Python module to parse the DSL - Add the new definitions directory containing DSL definition files - Use cog to generate syscall-related code in kernel and libj6 - Unify ordering of pointer + length pairs in interfaces
This commit is contained in:
42
assets/grammars/definitions.g
Normal file
42
assets/grammars/definitions.g
Normal file
@@ -0,0 +1,42 @@
|
||||
start: import_statement* (object|interface)+
|
||||
|
||||
import_statement: "import" PATH
|
||||
|
||||
object: description? "object" name options? super? "{" uid method* "}"
|
||||
|
||||
interface: description? "interface" name options? "{" uid interface_param* "}"
|
||||
|
||||
?interface_param: expose | function
|
||||
|
||||
expose: "expose" type
|
||||
|
||||
uid: "uid" UID
|
||||
|
||||
super: ":" name
|
||||
|
||||
function: description? "function" name options? ("{" param* "}")?
|
||||
|
||||
method: description? "method" name options? ("{" param* "}")?
|
||||
|
||||
param: "param" name type options? description?
|
||||
|
||||
?type: PRIMITIVE | object_name
|
||||
|
||||
object_name: "object" name
|
||||
|
||||
id: NUMBER
|
||||
name: IDENTIFIER
|
||||
options: "[" IDENTIFIER+ "]"
|
||||
description: COMMENT+
|
||||
|
||||
PRIMITIVE: INT_TYPE | "size" | "string" | "buffer" | "address"
|
||||
INT_TYPE: /u?int(8|16|32|64)?/
|
||||
NUMBER: /(0x)?[0-9a-fA-F]+/
|
||||
UID: /[0-9a-fA-F]{16}/
|
||||
COMMENT: /#.*/
|
||||
PATH: /"[^"]*"/
|
||||
|
||||
%import common.LETTER
|
||||
%import common.CNAME -> IDENTIFIER
|
||||
%import common.WS
|
||||
%ignore WS
|
||||
3
definitions/objects/channel.def
Normal file
3
definitions/objects/channel.def
Normal file
@@ -0,0 +1,3 @@
|
||||
object channel : kobject {
|
||||
uid 3ea38b96aa0e54c8
|
||||
}
|
||||
30
definitions/objects/endpoint.def
Normal file
30
definitions/objects/endpoint.def
Normal file
@@ -0,0 +1,30 @@
|
||||
# Channels are objects that enable synchronous IPC of
|
||||
# arbitrary-sized messages.
|
||||
|
||||
object endpoint : kobject {
|
||||
uid c5882f24a4c03b7e
|
||||
|
||||
method create [constructor]
|
||||
|
||||
# Send a message on a channel. Blocks until the message
|
||||
# is received.
|
||||
method send {
|
||||
param tag uint64
|
||||
param data buffer
|
||||
}
|
||||
|
||||
# Receieve a message on a channel. Blocks until a message
|
||||
# is available.
|
||||
method receive {
|
||||
param tag uint64 [out]
|
||||
param data buffer [out]
|
||||
}
|
||||
|
||||
# Send a message on a channel and then await a new message.
|
||||
# Equivalent to calling send and then recieve, as a single
|
||||
# operation.
|
||||
method sendrecv {
|
||||
param tag uint64 [inout]
|
||||
param data buffer [inout]
|
||||
}
|
||||
}
|
||||
25
definitions/objects/kobject.def
Normal file
25
definitions/objects/kobject.def
Normal file
@@ -0,0 +1,25 @@
|
||||
# The base type of all kernel-exposed objects
|
||||
object kobject [virtual] {
|
||||
uid 667f61fb2cd57bb4
|
||||
|
||||
# Get the internal kernel object id of an object
|
||||
method koid {
|
||||
param koid uint64 [out]
|
||||
}
|
||||
|
||||
# Block the current thread waiting for an object to assert
|
||||
# one of a set of signals
|
||||
method wait {
|
||||
param mask uint64 # Bitmap of which signals to wait for
|
||||
param signals uint64 [out] # Returns the state of the signals
|
||||
}
|
||||
|
||||
# Block the current thread waiting for an one of multiple
|
||||
# objects to assert one of a set of signals
|
||||
method wait_many [static] {
|
||||
param handles object kobject [list] # The objects to wait on
|
||||
param mask uint64 # Bitmap of which signals to wait for
|
||||
param handle object kobject [out] # Returns the object that signalled
|
||||
param signals uint64 [out] # Returns the state of the signals
|
||||
}
|
||||
}
|
||||
33
definitions/objects/mailbox.def
Normal file
33
definitions/objects/mailbox.def
Normal file
@@ -0,0 +1,33 @@
|
||||
# Mailboxes are objects that enable asynchronous IPC via event notification.
|
||||
# This is a second line of documentation
|
||||
|
||||
object mailbox {
|
||||
uid 99934ad04ece1e07
|
||||
|
||||
# Create an unbound mailbox
|
||||
method create [constructor]
|
||||
|
||||
method close [destructor]
|
||||
|
||||
method bind {
|
||||
param index uint
|
||||
param source object kobject
|
||||
param event uint
|
||||
}
|
||||
|
||||
method unbind {
|
||||
param index uint
|
||||
}
|
||||
|
||||
method notify {
|
||||
param index uint
|
||||
}
|
||||
|
||||
method wait {
|
||||
param bitmap uint64 [out]
|
||||
}
|
||||
|
||||
method ack {
|
||||
param bitmap uint64
|
||||
}
|
||||
}
|
||||
27
definitions/objects/process.def
Normal file
27
definitions/objects/process.def
Normal file
@@ -0,0 +1,27 @@
|
||||
import "objects/kobject.def"
|
||||
|
||||
# Processes are a collection of handles and a virtual memory
|
||||
# space inside which threads are run.
|
||||
|
||||
object process : kobject {
|
||||
uid 0c69ee0b7502ba31
|
||||
|
||||
# Create a new empty process
|
||||
method create [constructor]
|
||||
|
||||
# Stop all threads and exit the given process
|
||||
method kill [destructor]
|
||||
|
||||
# Stop all threads and exit the current process
|
||||
method exit [static] {
|
||||
param result int32 # The result to retrun to the parent process
|
||||
}
|
||||
|
||||
# Start the given process running. Note that the entrypoint
|
||||
# address must be specified in the address space of the new
|
||||
# process.
|
||||
method start {
|
||||
param entrypoint address # The address of the main thread entrypoint
|
||||
param handles object kobject [list] # A list of parent handles to send to the child process
|
||||
}
|
||||
}
|
||||
29
definitions/objects/system.def
Normal file
29
definitions/objects/system.def
Normal file
@@ -0,0 +1,29 @@
|
||||
import "objects/endpoint.def"
|
||||
import "objects/vma.def"
|
||||
|
||||
# The system object represents a handle to kernel functionality
|
||||
# needed by drivers and other priviledged services
|
||||
object system : kobject {
|
||||
uid fa72506a2cf71a30
|
||||
|
||||
# Get a log line from the kernel log
|
||||
method get_log {
|
||||
param buffer buffer [out] # Buffer for the log message data structure
|
||||
}
|
||||
|
||||
# Ask the kernel to send this process messages whenever
|
||||
# the given IRQ fires
|
||||
method bind_irq {
|
||||
param dest object endpoint # Endpoint that will receive messages
|
||||
param irq uint # IRQ number to bind
|
||||
}
|
||||
|
||||
# Create a VMA and map an area of physical memory into it,
|
||||
# also mapping that VMA into the current process
|
||||
method map_phys {
|
||||
param area object vma [out] # Receives a handle to the VMA created
|
||||
param phys address # The physical address of the area
|
||||
param size size # Size of the area, in pages
|
||||
param flags uint32 # Flags to apply to the created VMA
|
||||
}
|
||||
}
|
||||
18
definitions/objects/thread.def
Normal file
18
definitions/objects/thread.def
Normal file
@@ -0,0 +1,18 @@
|
||||
object thread : kobject {
|
||||
uid 11f23e593d5761bd
|
||||
|
||||
method create [constructor] {
|
||||
param entrypoint address
|
||||
}
|
||||
|
||||
method kill [destructor]
|
||||
|
||||
method exit [static] {
|
||||
param status int32
|
||||
}
|
||||
|
||||
method pause [static]
|
||||
method sleep [static] {
|
||||
param until uint64
|
||||
}
|
||||
}
|
||||
29
definitions/objects/vma.def
Normal file
29
definitions/objects/vma.def
Normal file
@@ -0,0 +1,29 @@
|
||||
import "objects/process.def"
|
||||
|
||||
object vma : kobject {
|
||||
uid d6a12b63b3ed3937
|
||||
|
||||
method create [constructor] {
|
||||
param size size
|
||||
param flags uint32
|
||||
}
|
||||
|
||||
method create_map [constructor] {
|
||||
param size size
|
||||
param address address
|
||||
param flags uint32
|
||||
}
|
||||
|
||||
method map {
|
||||
param process object process
|
||||
param address address
|
||||
}
|
||||
|
||||
method unmap {
|
||||
param process object process
|
||||
}
|
||||
|
||||
method resize {
|
||||
param size size [inout]
|
||||
}
|
||||
}
|
||||
27
definitions/syscalls.def
Normal file
27
definitions/syscalls.def
Normal file
@@ -0,0 +1,27 @@
|
||||
import "objects/system.def"
|
||||
import "objects/kobject.def"
|
||||
import "objects/process.def"
|
||||
import "objects/thread.def"
|
||||
import "objects/channel.def"
|
||||
import "objects/endpoint.def"
|
||||
import "objects/vma.def"
|
||||
|
||||
interface syscalls [syscall] {
|
||||
uid 01d9b6a948961097
|
||||
|
||||
expose object system
|
||||
expose object kobject
|
||||
expose object process
|
||||
expose object thread
|
||||
expose object channel
|
||||
expose object endpoint
|
||||
expose object vma
|
||||
|
||||
# Simple no-op syscall for testing
|
||||
function noop
|
||||
|
||||
# Write a message to the kernel log
|
||||
function log {
|
||||
param message string
|
||||
}
|
||||
}
|
||||
0
scripts/definitions/__init__.py
Normal file
0
scripts/definitions/__init__.py
Normal file
65
scripts/definitions/context.py
Normal file
65
scripts/definitions/context.py
Normal file
@@ -0,0 +1,65 @@
|
||||
class NotFound(Exception): pass
|
||||
|
||||
class Context:
|
||||
def __init__(self, path, verbose=False):
|
||||
if isinstance(path, str):
|
||||
self.__paths = [path]
|
||||
else:
|
||||
self.__paths = path
|
||||
|
||||
self.__closed = set()
|
||||
self.__verbose = verbose
|
||||
|
||||
self.__deps = {}
|
||||
|
||||
self.objects = dict()
|
||||
self.interfaces = dict()
|
||||
|
||||
verbose = property(lambda self: self.__verbose)
|
||||
|
||||
def find(self, filename):
|
||||
from os.path import exists, isabs, join
|
||||
|
||||
if exists(filename) or isabs(filename):
|
||||
return filename
|
||||
|
||||
for path in self.__paths:
|
||||
full = join(path, filename)
|
||||
if exists(full):
|
||||
return full
|
||||
|
||||
raise NotFound(filename)
|
||||
|
||||
def parse(self, filename):
|
||||
pending = set()
|
||||
pending.add(filename)
|
||||
|
||||
from .parser import Lark_StandAlone as Parser
|
||||
from .transformer import DefTransformer
|
||||
|
||||
objects = {}
|
||||
interfaces = {}
|
||||
|
||||
while pending:
|
||||
name = pending.pop()
|
||||
self.__closed.add(name)
|
||||
|
||||
path = self.find(name)
|
||||
|
||||
parser = Parser(transformer=DefTransformer(name))
|
||||
imps, objs, ints = parser.parse(open(path, "r").read())
|
||||
objects.update(objs)
|
||||
interfaces.update(ints)
|
||||
|
||||
self.__deps[name] = imps
|
||||
|
||||
pending.update(imps.difference(self.__closed))
|
||||
|
||||
from .types import ObjectRef
|
||||
ObjectRef.connect(objects)
|
||||
|
||||
self.objects.update(objects)
|
||||
self.interfaces.update(interfaces)
|
||||
|
||||
def deps(self):
|
||||
return {self.find(k): tuple(map(self.find, v)) for k, v in self.__deps.items()}
|
||||
2
scripts/definitions/errors.py
Normal file
2
scripts/definitions/errors.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class InvalidType(Exception): pass
|
||||
class UnknownTypeError(Exception): pass
|
||||
62
scripts/definitions/generate.py
Normal file
62
scripts/definitions/generate.py
Normal file
@@ -0,0 +1,62 @@
|
||||
def generate_template(template, outfile, **kwargs):
|
||||
from hashlib import md5
|
||||
from os import makedirs
|
||||
from os.path import dirname, exists
|
||||
|
||||
content = template.render(**kwargs)
|
||||
h = md5(content.encode('utf-8')).hexdigest()
|
||||
|
||||
if exists(outfile):
|
||||
existing = open(outfile, 'r').read().encode('utf-8')
|
||||
if md5(existing).hexdigest() == h:
|
||||
return False
|
||||
|
||||
makedirs(dirname(outfile), exist_ok=True)
|
||||
open(outfile, 'w').write(content)
|
||||
|
||||
return True
|
||||
|
||||
def generate(ctx, outdir, template_type):
|
||||
from os.path import basename, join, split, splitext
|
||||
from jinja2 import Environment, PackageLoader
|
||||
|
||||
for name, interface in ctx.interfaces.items():
|
||||
base = "_".join(sorted(interface.options))
|
||||
|
||||
path = join("templates", base, template_type)
|
||||
env = Environment(
|
||||
loader = PackageLoader('definitions', package_path=path),
|
||||
trim_blocks = True, lstrip_blocks = True)
|
||||
|
||||
env.filters
|
||||
|
||||
for template_name in env.list_templates():
|
||||
template = env.get_template(template_name)
|
||||
|
||||
basepath, filename = split(template_name)
|
||||
filename, ext = splitext(filename)
|
||||
|
||||
if filename == "_object_":
|
||||
for obj in ctx.objects.values():
|
||||
outfile = join(outdir, basepath, obj.name + ext)
|
||||
wrote = generate_template(
|
||||
template, outfile,
|
||||
filename=obj.name + ext,
|
||||
basepath=basepath,
|
||||
object=obj,
|
||||
interface=interface,
|
||||
objects=ctx.objects)
|
||||
|
||||
if wrote and ctx.verbose:
|
||||
print(f"Writing {outfile}")
|
||||
|
||||
else:
|
||||
outfile = join(outdir, template_name)
|
||||
wrote = generate_template(
|
||||
template, outfile,
|
||||
filename=basename(template_name),
|
||||
interface=interface,
|
||||
objects=ctx.objects)
|
||||
|
||||
if wrote and ctx.verbose:
|
||||
print(f"Writing {outfile}")
|
||||
2809
scripts/definitions/parser.py
Normal file
2809
scripts/definitions/parser.py
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,23 @@
|
||||
%macro SYSCALL 2
|
||||
global __syscall_%1
|
||||
__syscall_%1:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; args should already be in rdi, etc, but rcx will
|
||||
; get stomped, so stash it in r10, which isn't a
|
||||
; callee-saved register, but also isn't used in the
|
||||
; function call ABI.
|
||||
mov r10, rcx
|
||||
|
||||
mov rax, %2
|
||||
syscall
|
||||
; result is now already in rax, so just return
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
%endmacro
|
||||
|
||||
{% for id, scope, method in interface.methods %}
|
||||
SYSCALL {% if scope %}{{ scope.name }}_{% endif %}{{ method.name }} {{ id }}
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
/// \file {{filename}}
|
||||
{% if object.super %}
|
||||
|
||||
#include <j6/{{ object.super.name }}.hh>
|
||||
{% endif %}
|
||||
|
||||
namespace j6 {
|
||||
|
||||
{% if object.desc %}
|
||||
{{ object.desc|indent('/// ', first=True) }}
|
||||
{% endif %}
|
||||
class {{ object.name }}
|
||||
{% if object.super %} : public {{ object.super.name }}
|
||||
{% endif %}
|
||||
{
|
||||
public:
|
||||
{% macro argument(type, name, first, options=False) -%}
|
||||
{%- for ctype, suffix in type.c_names(options) -%}
|
||||
{%- if not first or not loop.first %}, {% endif %}{{ ctype }} {{ name }}{{ suffix }}
|
||||
{%- endfor -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% for method in object.methods %}
|
||||
{% if method.desc %} /// {{ method.desc|indent(' /// ') }}{% endif %}
|
||||
{% for param in method.params %}
|
||||
{% if param.desc %} /// \arg {{ "%-10s"|format(param.name) }} {{ param.desc }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{%+ if method.static %}static {% endif %}j6_status_t {{ method.name }} (
|
||||
{%- for param in method.params %}{{ argument(param.type, param.name, loop.first, options=param.options) }}{% endfor -%});
|
||||
|
||||
{% endfor %}
|
||||
~{{ object.name }}();
|
||||
|
||||
private:
|
||||
{{ object.name }}(j6_handle_t handle) : m_handle {handle} {}
|
||||
j6_handle_t m_handle;
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <j6/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
{% macro argument(type, name, first, options=False) -%}
|
||||
{%- for ctype, suffix in type.c_names(options) -%}
|
||||
{%- if not first or not loop.first %}, {% endif %}{{ ctype }} {{ name }}{{ suffix }}
|
||||
{%- endfor -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% for id, scope, method in interface.methods %}
|
||||
j6_status_t __syscall_{% if scope %}{{ scope.name }}_{% endif %}{{ method.name }} (
|
||||
{%- if not method.static -%}{{ argument(scope.reftype, "self", True) }}{% endif -%}
|
||||
{%- set first = method.static -%}
|
||||
{%- for param in method.params %}{{ argument(param.type, param.name, first, options=param.options) }}{% set first = False %}{% endfor -%}
|
||||
);
|
||||
{% endfor %}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
142
scripts/definitions/transformer.py
Normal file
142
scripts/definitions/transformer.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from .parser import Transformer, v_args
|
||||
|
||||
def get_opts(args):
|
||||
from .types import Description, Options, Type, UID
|
||||
|
||||
kinds = {
|
||||
Description: "desc",
|
||||
Options: "opts",
|
||||
UID: "uid",
|
||||
Type: "typename",
|
||||
}
|
||||
|
||||
result = dict()
|
||||
outargs = []
|
||||
for a in args:
|
||||
for kind, name in kinds.items():
|
||||
if isinstance(a, kind):
|
||||
result[name] = a
|
||||
break
|
||||
else:
|
||||
outargs.append(a)
|
||||
|
||||
return result, outargs
|
||||
|
||||
class DefTransformer(Transformer):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def start(self, args):
|
||||
from .types import Import, Interface, Object
|
||||
|
||||
imports = set()
|
||||
objects = dict()
|
||||
interfaces = dict()
|
||||
|
||||
for o in args:
|
||||
if isinstance(o, Object):
|
||||
objects[o.name] = o
|
||||
|
||||
elif isinstance(o, Interface):
|
||||
interfaces[o.name] = o
|
||||
|
||||
elif isinstance(o, Import):
|
||||
imports.add(o)
|
||||
|
||||
return imports, objects, interfaces
|
||||
|
||||
@v_args(inline=True)
|
||||
def import_statement(self, path):
|
||||
from .types import Import
|
||||
return Import(path)
|
||||
|
||||
def object(self, args):
|
||||
from .types import Object
|
||||
specials, args = get_opts(args)
|
||||
name, args = args[0], args[1:]
|
||||
return Object(name, children=args, **specials)
|
||||
|
||||
def interface(self, args):
|
||||
from .types import Interface
|
||||
specials, args = get_opts(args)
|
||||
name, args = args[0], args[1:]
|
||||
return Interface(name, children=args, **specials)
|
||||
|
||||
def method(self, args):
|
||||
from .types import Method
|
||||
specials, args = get_opts(args)
|
||||
name, args = args[0], args[1:]
|
||||
return Method(name, children=args, **specials)
|
||||
|
||||
def function(self, args):
|
||||
from .types import Function
|
||||
specials, args = get_opts(args)
|
||||
name, args = args[0], args[1:]
|
||||
return Function(name, children=args, **specials)
|
||||
|
||||
def param(self, args):
|
||||
from .types import Param
|
||||
specials, args = get_opts(args)
|
||||
name = args[0]
|
||||
return Param(name, **specials)
|
||||
|
||||
@v_args(inline=True)
|
||||
def expose(self, s):
|
||||
from .types import Expose
|
||||
return Expose(s)
|
||||
|
||||
@v_args(inline=True)
|
||||
def uid(self, s):
|
||||
return s
|
||||
|
||||
@v_args(inline=True)
|
||||
def name(self, s):
|
||||
return s
|
||||
|
||||
@v_args(inline=True)
|
||||
def type(self, s):
|
||||
return s
|
||||
|
||||
@v_args(inline=True)
|
||||
def super(self, s):
|
||||
from .types import ObjectRef
|
||||
return ObjectRef(s, self.filename)
|
||||
|
||||
def options(self, args):
|
||||
from .types import Options
|
||||
return Options([str(s) for s in args])
|
||||
|
||||
def description(self, s):
|
||||
from .types import Description
|
||||
return Description("\n".join(s))
|
||||
|
||||
@v_args(inline=True)
|
||||
def object_name(self, n):
|
||||
from .types import ObjectRef
|
||||
return ObjectRef(n, self.filename)
|
||||
|
||||
def PRIMITIVE(self, s):
|
||||
from .types import get_primitive
|
||||
return get_primitive(s)
|
||||
|
||||
def UID(self, s):
|
||||
from .types import UID
|
||||
return UID(int(s, base=16))
|
||||
|
||||
def INT_TYPE(self, s):
|
||||
return s
|
||||
|
||||
def NUMBER(self, s):
|
||||
if s.startswith("0x"):
|
||||
return int(s,16)
|
||||
return int(s)
|
||||
|
||||
def COMMENT(self, s):
|
||||
return s[2:].strip()
|
||||
|
||||
def IDENTIFIER(self, s):
|
||||
return str(s)
|
||||
|
||||
def PATH(self, s):
|
||||
return str(s[1:-1])
|
||||
|
||||
23
scripts/definitions/types/__init__.py
Normal file
23
scripts/definitions/types/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
def _indent(x):
|
||||
from textwrap import indent
|
||||
return indent(str(x), ' ')
|
||||
|
||||
class Description(str): pass
|
||||
class Import(str): pass
|
||||
|
||||
class Options(set):
|
||||
def __str__(self):
|
||||
if not self: return ""
|
||||
return "[{}]".format(" ".join(self))
|
||||
|
||||
class UID(int):
|
||||
def __str__(self):
|
||||
return f"{self:016x}"
|
||||
|
||||
from .object import Object
|
||||
from .interface import Interface, Expose
|
||||
from .function import Function, Method, Param
|
||||
|
||||
from .type import Type
|
||||
from .primitive import get_primitive
|
||||
from .objref import ObjectRef
|
||||
49
scripts/definitions/types/function.py
Normal file
49
scripts/definitions/types/function.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
def _hasopt(opt):
|
||||
def test(self):
|
||||
return opt in self.options
|
||||
return test
|
||||
|
||||
class Function:
|
||||
typename = "function"
|
||||
|
||||
def __init__(self, name, opts=Options(), desc="", children=tuple()):
|
||||
self.name = name
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
self.params = [c for c in children if isinstance(c, Param)]
|
||||
self.id = -1
|
||||
|
||||
def __str__(self):
|
||||
parts = ["{} {}".format(self.typename, self.name)]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.params))
|
||||
return "\n".join(parts)
|
||||
|
||||
static = property(lambda x: True)
|
||||
constructor = property(lambda x: False)
|
||||
|
||||
|
||||
class Method(Function):
|
||||
typename = "method"
|
||||
|
||||
static = property(_hasopt("static"))
|
||||
constructor = property(_hasopt("constructor"))
|
||||
|
||||
|
||||
class Param:
|
||||
def __init__(self, name, typename, opts=Options(), desc=""):
|
||||
self.name = name
|
||||
self.type = typename
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
|
||||
def __str__(self):
|
||||
return "param {} {} {} {}".format(
|
||||
self.name, repr(self.type), self.options, self.desc or "")
|
||||
|
||||
43
scripts/definitions/types/interface.py
Normal file
43
scripts/definitions/types/interface.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
class Expose(object):
|
||||
def __init__(self, type):
|
||||
self.type = type
|
||||
|
||||
def __repr__(self):
|
||||
return f'expose {repr(self.type)}'
|
||||
|
||||
class Interface:
|
||||
def __init__(self, name, uid, opts=Options(), desc="", children=tuple()):
|
||||
from .function import Function
|
||||
|
||||
self.name = name
|
||||
self.uid = uid
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
|
||||
self.functions = [c for c in children if isinstance(c, Function)]
|
||||
self.exposes = [e.type for e in children if isinstance(e, Expose)]
|
||||
|
||||
def __str__(self):
|
||||
parts = [f"interface {self.name}: {self.uid}"]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.exposes))
|
||||
parts.extend(map(_indent, self.functions))
|
||||
return "\n".join(parts)
|
||||
|
||||
def __methods(self):
|
||||
mm = [(i, None, self.functions[i]) for i in range(len(self.functions))]
|
||||
|
||||
base = len(mm)
|
||||
for o in [e.object for e in self.exposes]:
|
||||
mm.extend([(base + i, o, o.methods[i]) for i in range(len(o.methods))])
|
||||
base += len(o.methods)
|
||||
|
||||
return mm
|
||||
|
||||
methods = property(__methods)
|
||||
25
scripts/definitions/types/object.py
Normal file
25
scripts/definitions/types/object.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from . import _indent
|
||||
from . import Options
|
||||
|
||||
class Object:
|
||||
def __init__(self, name, uid, typename=None, opts=Options(), desc="", children=tuple()):
|
||||
self.name = name
|
||||
self.uid = uid
|
||||
self.options = opts
|
||||
self.desc = desc
|
||||
self.super = typename
|
||||
self.methods = children
|
||||
|
||||
from . import ObjectRef
|
||||
self.__ref = ObjectRef(name)
|
||||
|
||||
def __str__(self):
|
||||
parts = [f"object {self.name}: {self.uid}"]
|
||||
if self.desc:
|
||||
parts.append(_indent(self.desc))
|
||||
if self.options:
|
||||
parts.append(f" Options: {self.options}")
|
||||
parts.extend(map(_indent, self.methods))
|
||||
return "\n".join(parts)
|
||||
|
||||
reftype = property(lambda self: self.__ref)
|
||||
44
scripts/definitions/types/objref.py
Normal file
44
scripts/definitions/types/objref.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from .type import Type
|
||||
|
||||
class ObjectRef(Type):
|
||||
all_refs = {}
|
||||
|
||||
def __init__(self, name, filename=None):
|
||||
super().__init__(name)
|
||||
self.__c_type = "j6_handle_t"
|
||||
self.__object = None
|
||||
ObjectRef.all_refs[self] = filename
|
||||
|
||||
def __repr__(self):
|
||||
return f'ObjectRef({self.name})'
|
||||
|
||||
object = property(lambda self: self.__object)
|
||||
|
||||
def c_names(self, options):
|
||||
one = self.__c_type
|
||||
out = bool({"out", "inout"}.intersection(options))
|
||||
|
||||
if "list" in options:
|
||||
two = "size_t"
|
||||
if out:
|
||||
one = f"const {one} *"
|
||||
two += " *"
|
||||
else:
|
||||
one = f"{one} *"
|
||||
return ((one, ""), (two, "_count"))
|
||||
|
||||
else:
|
||||
if out:
|
||||
one += " *"
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
@classmethod
|
||||
def connect(cls, objects):
|
||||
for ref, filename in cls.all_refs.items():
|
||||
ref.__object = objects.get(ref.name)
|
||||
if ref.__object is None:
|
||||
from ..errors import UnknownTypeError
|
||||
raise UnknownTypeError(ref.name, filename)
|
||||
70
scripts/definitions/types/primitive.py
Normal file
70
scripts/definitions/types/primitive.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from .type import Type
|
||||
|
||||
class Primitive(Type):
|
||||
def __init__(self, name, c_type):
|
||||
super().__init__(name)
|
||||
self.c_type = c_type
|
||||
|
||||
def __repr__(self):
|
||||
return f'Primitive({self.name})'
|
||||
|
||||
def c_names(self, options=None):
|
||||
one = self.c_type
|
||||
if options and "out" in options or "inout" in options:
|
||||
one += " *"
|
||||
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
class PrimitiveRef(Primitive):
|
||||
def __init__(self, name, c_type, counted=False):
|
||||
super().__init__(name, c_type)
|
||||
self.__counted = counted
|
||||
|
||||
def c_names(self, options=None):
|
||||
one = f"{self.c_type} *"
|
||||
two = "size_t"
|
||||
|
||||
if options and "out" in options or "inout" in options:
|
||||
two += " *"
|
||||
else:
|
||||
one = "const " + one
|
||||
|
||||
if self.__counted:
|
||||
return ((one, ""), (two, "_len"))
|
||||
else:
|
||||
return ((one, ""),)
|
||||
|
||||
def cxx_names(self, options):
|
||||
return self.c_names(options)
|
||||
|
||||
_primitives = {
|
||||
"string": PrimitiveRef("string", "char"),
|
||||
"buffer": PrimitiveRef("buffer", "void", counted=True),
|
||||
|
||||
"int": Primitive("int", "int"),
|
||||
"uint": Primitive("uint", "unsigned"),
|
||||
"size": Primitive("size", "size_t"),
|
||||
"address": Primitive("address", "uintptr_t"),
|
||||
|
||||
"int8": Primitive("int8", "int8_t"),
|
||||
"uint8": Primitive("uint8", "uint8_t"),
|
||||
|
||||
"int16": Primitive("int16", "int16_t"),
|
||||
"uint16": Primitive("uint16", "uint16_t"),
|
||||
|
||||
"int32": Primitive("int32", "int32_t"),
|
||||
"uint32": Primitive("uint32", "uint32_t"),
|
||||
|
||||
"int64": Primitive("int64", "int64_t"),
|
||||
"uint64": Primitive("uint64", "uint64_t"),
|
||||
}
|
||||
|
||||
def get_primitive(name):
|
||||
p = _primitives.get(name)
|
||||
if not p:
|
||||
from ..errors import InvalidType
|
||||
raise InvalidType(name)
|
||||
return p
|
||||
12
scripts/definitions/types/type.py
Normal file
12
scripts/definitions/types/type.py
Normal file
@@ -0,0 +1,12 @@
|
||||
class Type:
|
||||
def __init__(self, name):
|
||||
self.__name = name
|
||||
|
||||
name = property(lambda self: self.__name)
|
||||
|
||||
def c_names(self, options):
|
||||
raise NotImplemented("Call to base Type.c_names")
|
||||
|
||||
def cxx_names(self, options):
|
||||
raise NotImplemented("Call to base Type.c_names")
|
||||
|
||||
11
scripts/idgen
Executable file
11
scripts/idgen
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
def hashid(s):
|
||||
from hashlib import shake_128 as sh
|
||||
return sh(s.encode('utf-8')).hexdigest(8)
|
||||
|
||||
import sys
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
id = hashid(arg)
|
||||
print(f"{arg}: {id}")
|
||||
@@ -14,7 +14,6 @@ kernel = module("kernel",
|
||||
"clock.cpp",
|
||||
"console.cpp",
|
||||
"cpprt.cpp",
|
||||
"cpu.cpp",
|
||||
"debug.cpp",
|
||||
"debug.s",
|
||||
"device_manager.cpp",
|
||||
@@ -27,7 +26,6 @@ kernel = module("kernel",
|
||||
"interrupts.s",
|
||||
"io.cpp",
|
||||
"log.cpp",
|
||||
"main.cpp",
|
||||
"memory_bootstrap.cpp",
|
||||
"msr.cpp",
|
||||
"objects/channel.cpp",
|
||||
@@ -42,8 +40,6 @@ kernel = module("kernel",
|
||||
"pci.cpp",
|
||||
"scheduler.cpp",
|
||||
"serial.cpp",
|
||||
"syscall.cpp",
|
||||
"syscall.s",
|
||||
"syscalls/channel.cpp",
|
||||
"syscalls/endpoint.cpp",
|
||||
"syscalls/object.cpp",
|
||||
@@ -56,4 +52,15 @@ kernel = module("kernel",
|
||||
"vm_space.cpp",
|
||||
])
|
||||
|
||||
from glob import glob
|
||||
definitions = glob('definitions/**/*.def', recursive=True)
|
||||
sysinc = kernel.add_input("syscalls.inc.cog", deps=definitions)
|
||||
kernel.add_input("syscall.s", deps=[sysinc])
|
||||
|
||||
sysh = kernel.add_input("syscall.h.cog", deps=definitions)
|
||||
sysc = kernel.add_input("syscall.cpp.cog", deps=definitions + [sysh])
|
||||
|
||||
kernel.add_input("main.cpp", deps=[sysh])
|
||||
kernel.add_input("cpu.cpp", deps=[sysh])
|
||||
|
||||
kernel.variables['ldflags'] = ["${ldflags}", "-T", "${source_root}/src/kernel/kernel.ld"]
|
||||
|
||||
@@ -28,10 +28,10 @@ endpoint::close()
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint::send(j6_tag_t tag, size_t len, void *data)
|
||||
endpoint::send(j6_tag_t tag, const void *data, size_t data_len)
|
||||
{
|
||||
thread_data sender = { &thread::current(), data };
|
||||
sender.len = len;
|
||||
sender.len = data_len;
|
||||
sender.tag = tag;
|
||||
|
||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||
@@ -55,11 +55,11 @@ endpoint::send(j6_tag_t tag, size_t len, void *data)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
|
||||
endpoint::receive(j6_tag_t *tag, void *data, size_t *data_len)
|
||||
{
|
||||
thread_data receiver = { &thread::current(), data };
|
||||
receiver.tag_p = tag;
|
||||
receiver.len_p = len;
|
||||
receiver.len_p = data_len;
|
||||
|
||||
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
||||
assert_signal(j6_signal_endpoint_can_send);
|
||||
@@ -120,7 +120,7 @@ endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_
|
||||
if (sender.len) {
|
||||
vm_space &source = sender.th->parent().space();
|
||||
vm_space &dest = receiver.th->parent().space();
|
||||
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||
vm_space::copy(source, dest, sender.data, receiver.buffer, sender.len);
|
||||
}
|
||||
|
||||
*receiver.len_p = sender.len;
|
||||
|
||||
@@ -27,10 +27,10 @@ public:
|
||||
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||
/// are currently trying to receive, block the current thread.
|
||||
/// \arg tag The application-specified message tag
|
||||
/// \arg len The size in bytes of the message
|
||||
/// \arg data The message data
|
||||
/// \arg len The size in bytes of the message
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
||||
j6_status_t send(j6_tag_t tag, const void *data, size_t data_len);
|
||||
|
||||
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||
/// are currently trying to send, block the current thread.
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
||||
/// \arg data Buffer for copying message data into
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
||||
j6_status_t receive(j6_tag_t *tag, void *data, size_t *data_len);
|
||||
|
||||
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
||||
/// \arg irq The IRQ that caused this signal
|
||||
@@ -48,7 +48,10 @@ private:
|
||||
struct thread_data
|
||||
{
|
||||
thread *th;
|
||||
void *data;
|
||||
union {
|
||||
const void *data;
|
||||
void *buffer;
|
||||
};
|
||||
union {
|
||||
j6_tag_t *tag_p;
|
||||
j6_tag_t tag;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "kutil/memory.h"
|
||||
|
||||
#include "console.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "syscall.h"
|
||||
|
||||
extern "C" {
|
||||
void syscall_invalid(uint64_t call);
|
||||
}
|
||||
|
||||
uintptr_t syscall_registry[256] __attribute__((section(".syscall_registry")));
|
||||
const char * syscall_names[256] __attribute__((section(".syscall_registry")));
|
||||
static constexpr size_t num_syscalls = sizeof(syscall_registry) / sizeof(syscall_registry[0]);
|
||||
|
||||
void
|
||||
syscall_invalid(uint64_t call)
|
||||
{
|
||||
console *cons = console::get();
|
||||
cons->set_color(9);
|
||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||
|
||||
cons->printf(" Known syscalls:\n");
|
||||
cons->printf(" invalid %016lx\n", syscall_invalid);
|
||||
|
||||
for (unsigned i = 0; i < num_syscalls; ++i) {
|
||||
const char *name = syscall_names[i];
|
||||
uintptr_t handler = syscall_registry[i];
|
||||
if (name)
|
||||
cons->printf(" %02x %10s %016lx\n", i, name, handler);
|
||||
}
|
||||
|
||||
cons->set_color();
|
||||
_halt();
|
||||
}
|
||||
|
||||
void
|
||||
syscall_initialize()
|
||||
{
|
||||
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
||||
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
|
||||
|
||||
#define SYSCALL(id, name, result, ...) \
|
||||
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
|
||||
syscall_names[id] = #name; \
|
||||
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
|
||||
#include "j6/tables/syscalls.inc"
|
||||
#undef SYSCALL
|
||||
}
|
||||
|
||||
56
src/kernel/syscall.cpp.cog
Normal file
56
src/kernel/syscall.cpp.cog
Normal file
@@ -0,0 +1,56 @@
|
||||
// vim: ft=cpp
|
||||
#include <stddef.h>
|
||||
|
||||
#include "kutil/memory.h"
|
||||
|
||||
#include "console.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "syscall.h"
|
||||
|
||||
extern "C" {
|
||||
void syscall_invalid(uint64_t call);
|
||||
}
|
||||
|
||||
/*[[[cog code generation
|
||||
from definitions.context import Context
|
||||
|
||||
ctx = Context(definitions_path)
|
||||
ctx.parse("syscalls.def")
|
||||
syscalls = ctx.interfaces['syscalls']
|
||||
|
||||
cog.outl(f"constexpr size_t num_syscalls = {len(syscalls.methods)};")
|
||||
]]]*/
|
||||
/// [[[end]]]
|
||||
uintptr_t syscall_registry[num_syscalls] __attribute__((section(".syscall_registry")));
|
||||
|
||||
void
|
||||
syscall_invalid(uint64_t call)
|
||||
{
|
||||
console *cons = console::get();
|
||||
cons->set_color(9);
|
||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||
|
||||
cons->set_color();
|
||||
_halt();
|
||||
}
|
||||
|
||||
void
|
||||
syscall_initialize()
|
||||
{
|
||||
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
||||
|
||||
/*[[[cog code generation
|
||||
for id, scope, method in syscalls.methods:
|
||||
if scope:
|
||||
name = f"{scope.name}_{method.name}"
|
||||
else:
|
||||
name = method.name
|
||||
|
||||
cog.outl(f"syscall_registry[{id}] = reinterpret_cast<uintptr_t>(syscalls::{name});")
|
||||
cog.outl(f"""log::debug(logs::syscall, "Enabling syscall {id:x} as {name}");""")
|
||||
cog.outl("")
|
||||
]]]*/
|
||||
//[[[end]]]
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "j6/types.h"
|
||||
|
||||
struct cpu_state;
|
||||
|
||||
enum class syscall : uint64_t
|
||||
{
|
||||
#define SYSCALL(id, name, ...) name = id,
|
||||
#include "j6/tables/syscalls.inc"
|
||||
#undef SYSCALL
|
||||
};
|
||||
|
||||
void syscall_initialize();
|
||||
extern "C" void syscall_enable();
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
#define SYSCALL(id, name, ...) j6_status_t name (__VA_ARGS__);
|
||||
#include "j6/tables/syscalls.inc"
|
||||
#undef SYSCALL
|
||||
}
|
||||
60
src/kernel/syscall.h.cog
Normal file
60
src/kernel/syscall.h.cog
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
// vim: ft=cpp
|
||||
|
||||
#include <stdint.h>
|
||||
#include "j6/types.h"
|
||||
|
||||
struct cpu_state;
|
||||
|
||||
/*[[[cog code generation
|
||||
from definitions.context import Context
|
||||
|
||||
ctx = Context(definitions_path)
|
||||
ctx.parse("syscalls.def")
|
||||
syscalls = ctx.interfaces["syscalls"]
|
||||
|
||||
]]]*/
|
||||
/// [[[end]]]
|
||||
|
||||
enum class syscall : uint64_t
|
||||
{
|
||||
/*[[[cog code generation
|
||||
|
||||
for id, scope, method in syscalls.methods:
|
||||
if scope:
|
||||
name = f"{scope.name}_{method.name}"
|
||||
else:
|
||||
name = method.name
|
||||
cog.outl(f"{name:20} = {id},")
|
||||
|
||||
]]]*/
|
||||
//[[[end]]]
|
||||
};
|
||||
|
||||
void syscall_initialize();
|
||||
extern "C" void syscall_enable();
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
/*[[[cog code generation
|
||||
|
||||
for id, scope, method in syscalls.methods:
|
||||
if scope:
|
||||
name = f"{scope.name}_{method.name}"
|
||||
else:
|
||||
name = method.name
|
||||
|
||||
args = []
|
||||
if method.constructor:
|
||||
args.append("j6_handle_t *handle")
|
||||
elif not method.static:
|
||||
args.append("j6_handle_t handle")
|
||||
|
||||
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"""j6_status_t {name} ({", ".join(args)});""")
|
||||
]]]*/
|
||||
//[[[end]]]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
%include "tasking.inc"
|
||||
%include "syscalls.inc"
|
||||
|
||||
; SYSCALL/SYSRET control MSRs
|
||||
MSR_STAR equ 0xc0000081
|
||||
@@ -51,7 +52,9 @@ syscall_handler_prelude:
|
||||
|
||||
inc qword [rel __counter_syscall_enter]
|
||||
|
||||
and rax, 0xff ; Only 256 possible syscall values
|
||||
cmp rax, NUM_SYSCALLS
|
||||
jge .bad_syscall
|
||||
|
||||
lea r11, [rel syscall_registry]
|
||||
mov r11, [r11 + rax * 8]
|
||||
cmp r11, 0
|
||||
|
||||
11
src/kernel/syscalls.inc.cog
Normal file
11
src/kernel/syscalls.inc.cog
Normal file
@@ -0,0 +1,11 @@
|
||||
; vim: ft=asm
|
||||
; [[[cog code generation
|
||||
; from definitions.context import Context
|
||||
;
|
||||
; ctx = Context(definitions_path)
|
||||
; ctx.parse("syscalls.def")
|
||||
; syscalls = ctx.interfaces['syscalls']
|
||||
; cog.outl(f"NUM_SYSCALLS equ {len(syscalls.methods)}")
|
||||
; ]]]
|
||||
; [[[end]]]
|
||||
|
||||
@@ -15,7 +15,7 @@ endpoint_create(j6_handle_t *handle)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
||||
endpoint_send(j6_handle_t handle, uint64_t tag, const void * data, size_t data_len)
|
||||
{
|
||||
if (tag & j6_tag_system_flag)
|
||||
return j6_err_invalid_arg;
|
||||
@@ -23,28 +23,28 @@ endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
return e->send(tag, len, data);
|
||||
return e->send(tag, data, data_len);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_receive(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
endpoint_receive(j6_handle_t handle, uint64_t * tag, void * data, size_t * data_len)
|
||||
{
|
||||
if (!tag || !len || (*len && !data))
|
||||
if (!tag || !data_len || !data)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
size_t out_len = *data_len;
|
||||
j6_status_t s = e->receive(&out_tag, data, &out_len);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
*data_len = out_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
endpoint_sendrecv(j6_handle_t handle, uint64_t * tag, void * data, size_t * data_len)
|
||||
{
|
||||
if (!tag || (*tag & j6_tag_system_flag))
|
||||
return j6_err_invalid_arg;
|
||||
@@ -52,15 +52,15 @@ endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
j6_status_t status = e->send(*tag, *len, data);
|
||||
j6_status_t status = e->send(*tag, data, *data_len);
|
||||
if (status != j6_status_ok)
|
||||
return status;
|
||||
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
size_t out_len = *data_len;
|
||||
j6_status_t s = e->receive(&out_tag, data, &out_len);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
*data_len = out_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace syscalls {
|
||||
|
||||
j6_status_t
|
||||
object_koid(j6_handle_t handle, j6_koid_t *koid)
|
||||
kobject_koid(j6_handle_t handle, j6_koid_t *koid)
|
||||
{
|
||||
if (koid == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
@@ -23,7 +23,7 @@ object_koid(j6_handle_t handle, j6_koid_t *koid)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
|
||||
kobject_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
|
||||
{
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
@@ -47,11 +47,11 @@ object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_handle_t *handle, j6_signal_t *sigs)
|
||||
kobject_wait_many(j6_handle_t * handles, size_t handles_count, uint64_t mask, j6_handle_t * handle, uint64_t * signals)
|
||||
{
|
||||
kutil::vector<kobject*> objects {count};
|
||||
kutil::vector<kobject*> objects {uint32_t(handles_count)};
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
for (unsigned i = 0; i < handles_count; ++i) {
|
||||
j6_handle_t h = handles[i];
|
||||
if (h == j6_handle_invalid)
|
||||
continue;
|
||||
@@ -62,7 +62,7 @@ object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_hand
|
||||
|
||||
j6_signal_t current = obj->signals();
|
||||
if ((current & mask) != 0) {
|
||||
*sigs = current;
|
||||
*signals = current;
|
||||
*handle = h;
|
||||
return j6_status_ok;
|
||||
}
|
||||
@@ -81,9 +81,9 @@ object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_hand
|
||||
return result;
|
||||
|
||||
*handle = j6_handle_invalid;
|
||||
*sigs = th.get_wait_data();
|
||||
*signals = th.get_wait_data();
|
||||
j6_koid_t koid = th.get_wait_object();
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
for (unsigned i = 0; i < handles_count; ++i) {
|
||||
if (koid == objects[i]->koid())
|
||||
*handle = handles[i];
|
||||
else
|
||||
@@ -96,7 +96,7 @@ object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_hand
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_signal(j6_handle_t handle, j6_signal_t signals)
|
||||
kobject_signal(j6_handle_t handle, j6_signal_t signals)
|
||||
{
|
||||
if ((signals & j6_signal_user_mask) != signals)
|
||||
return j6_err_invalid_arg;
|
||||
@@ -110,7 +110,7 @@ object_signal(j6_handle_t handle, j6_signal_t signals)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_close(j6_handle_t handle)
|
||||
kobject_close(j6_handle_t handle)
|
||||
{
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
|
||||
@@ -16,14 +16,14 @@ process_create(j6_handle_t *handle)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_start(j6_handle_t handle, uintptr_t entrypoint, j6_handle_t *handles, size_t handle_count)
|
||||
process_start(j6_handle_t handle, uintptr_t entrypoint, j6_handle_t * handles, size_t handles_count)
|
||||
{
|
||||
process &p = process::current();
|
||||
process *c = get_handle<process>(handle);
|
||||
if (handle_count && !handles)
|
||||
if (handles_count && !handles)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
for (size_t i = 0; i < handle_count; ++i) {
|
||||
for (size_t i = 0; i < handles_count; ++i) {
|
||||
kobject *o = p.lookup_handle(handles[i]);
|
||||
if (o) c->add_handle(o);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ extern log::logger &g_logger;
|
||||
namespace syscalls {
|
||||
|
||||
j6_status_t
|
||||
system_log(const char *message)
|
||||
log(const char *message)
|
||||
{
|
||||
if (message == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
@@ -26,7 +26,7 @@ system_log(const char *message)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_noop()
|
||||
noop()
|
||||
{
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::syscall, "Thread %llx called noop syscall.", th.koid());
|
||||
@@ -61,18 +61,18 @@ system_bind_irq(j6_handle_t sys, j6_handle_t endp, unsigned irq)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_map_phys(j6_handle_t sys, j6_handle_t *vma_handle, uintptr_t phys_addr, size_t size, uint32_t flags)
|
||||
system_map_phys(j6_handle_t handle, j6_handle_t * area, uintptr_t phys, size_t size, uint32_t flags)
|
||||
{
|
||||
// TODO: check capabilities on sys handle
|
||||
if (!vma_handle) return j6_err_invalid_arg;
|
||||
if (!area) return j6_err_invalid_arg;
|
||||
|
||||
// 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))
|
||||
frame_allocator::get().used(phys_addr, memory::page_count(size));
|
||||
frame_allocator::get().used(phys, memory::page_count(size));
|
||||
|
||||
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask);
|
||||
construct_handle<vm_area_fixed>(vma_handle, phys_addr, size, vmf);
|
||||
construct_handle<vm_area_fixed>(area, phys, size, vmf);
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
#include "log.h"
|
||||
#include "objects/process.h"
|
||||
#include "objects/thread.h"
|
||||
#include "syscalls/helpers.h"
|
||||
|
||||
namespace syscalls {
|
||||
|
||||
j6_status_t
|
||||
thread_create(void *rip, j6_handle_t *handle)
|
||||
thread_create(j6_handle_t *handle, uintptr_t entrypoint)
|
||||
{
|
||||
thread &parent = thread::current();
|
||||
process &p = parent.parent();
|
||||
|
||||
thread *child = p.create_thread();
|
||||
child->add_thunk_user(reinterpret_cast<uintptr_t>(rip));
|
||||
child->add_thunk_user(entrypoint);
|
||||
*handle = child->self_handle();
|
||||
child->clear_state(thread::state::loading);
|
||||
child->set_state(thread::state::ready);
|
||||
@@ -36,6 +37,18 @@ thread_exit(int32_t status)
|
||||
return j6_err_unexpected;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread_kill(j6_handle_t handle)
|
||||
{
|
||||
thread *th = get_handle<thread>(handle);
|
||||
if (!th)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
log::debug(logs::task, "Killing thread %llx", th->koid());
|
||||
th->exit(-1);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread_pause()
|
||||
{
|
||||
|
||||
@@ -286,7 +286,7 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
||||
}
|
||||
|
||||
size_t
|
||||
vm_space::copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length)
|
||||
vm_space::copy(vm_space &source, vm_space &dest, const void *from, void *to, size_t length)
|
||||
{
|
||||
uintptr_t ifrom = reinterpret_cast<uintptr_t>(from);
|
||||
uintptr_t ito = reinterpret_cast<uintptr_t>(to);
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
/// \arg to Pointer to the destination in the dest address space
|
||||
/// \arg length Amount of data to copy, in bytes
|
||||
/// \returnd The number of bytes copied
|
||||
static size_t copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length);
|
||||
static size_t copy(vm_space &source, vm_space &dest, const void *from, void *to, size_t length);
|
||||
|
||||
private:
|
||||
friend class vm_area;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <j6/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SYSCALL(n, name, ...) j6_status_t j6_ ## name (__VA_ARGS__);
|
||||
#include "j6/tables/syscalls.inc"
|
||||
#undef SYSCALL
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
39
src/libraries/j6/include/j6/syscalls.h.cog
Normal file
39
src/libraries/j6/include/j6/syscalls.h.cog
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
// vim: ft=cpp
|
||||
|
||||
#include <j6/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*[[[cog code generation
|
||||
from definitions.context import Context
|
||||
|
||||
ctx = Context(definitions_path)
|
||||
ctx.parse("syscalls.def")
|
||||
syscalls = ctx.interfaces["syscalls"]
|
||||
|
||||
for id, scope, method in syscalls.methods:
|
||||
if scope:
|
||||
name = f"{scope.name}_{method.name}"
|
||||
else:
|
||||
name = method.name
|
||||
|
||||
args = []
|
||||
if method.constructor:
|
||||
args.append("j6_handle_t *handle")
|
||||
elif not method.static:
|
||||
args.append("j6_handle_t handle")
|
||||
|
||||
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"""j6_status_t j6_{name} ({", ".join(args)});""")
|
||||
]]]*/
|
||||
/// [[[end]]]
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,9 +1,14 @@
|
||||
# vim: ft=python
|
||||
|
||||
module("j6",
|
||||
j6 = module("j6",
|
||||
kind = "lib",
|
||||
includes = [ "include" ],
|
||||
sources = [
|
||||
"init.cpp",
|
||||
"syscalls.s",
|
||||
])
|
||||
|
||||
from glob import glob
|
||||
definitions = glob('definitions/**/*.def', recursive=True)
|
||||
j6.add_input("include/j6/syscalls.h.cog", deps=definitions)
|
||||
j6.add_input("syscalls.s.cog", deps=definitions)
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
%macro SYSCALL 2
|
||||
global j6_%1
|
||||
j6_%1:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; args should already be in rdi, etc, but rcx will
|
||||
; get stomped, so stash it in r10, which isn't a
|
||||
; callee-saved register, but also isn't used in the
|
||||
; function call ABI.
|
||||
mov r10, rcx
|
||||
|
||||
mov rax, %2
|
||||
syscall
|
||||
; result is now already in rax, so just return
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
%endmacro
|
||||
|
||||
%define SYSCALL(n, name) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c, d) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c, d, e) SYSCALL name, n
|
||||
|
||||
%include "j6/tables/syscalls.inc"
|
||||
37
src/libraries/j6/syscalls.s.cog
Normal file
37
src/libraries/j6/syscalls.s.cog
Normal file
@@ -0,0 +1,37 @@
|
||||
; vim: ft=asm
|
||||
|
||||
%macro define_syscall 2
|
||||
global j6_%1
|
||||
j6_%1:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; args should already be in rdi, etc, but rcx will
|
||||
; get stomped, so stash it in r10, which isn't a
|
||||
; callee-saved register, but also isn't used in the
|
||||
; function call ABI.
|
||||
mov r10, rcx
|
||||
|
||||
mov rax, %2
|
||||
syscall
|
||||
; result is now already in rax, so just return
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
%endmacro
|
||||
|
||||
; [[[cog code generation
|
||||
; from definitions.context import Context
|
||||
;
|
||||
; ctx = Context(definitions_path)
|
||||
; ctx.parse("syscalls.def")
|
||||
; syscalls = ctx.interfaces['syscalls']
|
||||
;
|
||||
; for id, scope, method in syscalls.methods:
|
||||
; if scope:
|
||||
; name = f"{scope.name}_{method.name}"
|
||||
; else:
|
||||
; name = method.name
|
||||
; cog.outl(f"define_syscall {name:20}, {id}")
|
||||
; ]]]
|
||||
; [[[end]]]
|
||||
@@ -33,7 +33,7 @@ struct entry
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
j6_system_log("fb driver starting");
|
||||
j6_log("fb driver starting");
|
||||
|
||||
size_t initc = 0;
|
||||
j6_init_value *initv = nullptr;
|
||||
@@ -48,7 +48,7 @@ main(int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (!fb || fb->addr == 0) {
|
||||
j6_system_log("fb driver didn't find a framebuffer, exiting");
|
||||
j6_log("fb driver didn't find a framebuffer, exiting");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ main(int argc, const char **argv)
|
||||
buffer_size = size;
|
||||
continue;
|
||||
} else if (s != j6_status_ok) {
|
||||
j6_system_log("fb driver got error from get_log, quitting");
|
||||
j6_log("fb driver got error from get_log, quitting");
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ main(int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
j6_system_log("fb driver done, exiting");
|
||||
j6_log("fb driver done, exiting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ j6_handle_t handle_system = 2; // boot protocol is that init gets the system as
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
j6_system_log("srv.init starting");
|
||||
j6_log("srv.init starting");
|
||||
|
||||
modules mods = modules::load_modules(_arg_modules_phys, handle_system, handle_self);
|
||||
|
||||
@@ -28,7 +28,7 @@ main(int argc, const char **argv)
|
||||
for (auto &mod : mods.of_type(module_type::program)) {
|
||||
auto &prog = static_cast<const module_program&>(mod);
|
||||
sprintf(message, " program module '%s' at %lx", prog.filename, prog.base_address);
|
||||
j6_system_log(message);
|
||||
j6_log(message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -70,7 +70,7 @@ modules::load_modules(uintptr_t address, j6_handle_t system, j6_handle_t self)
|
||||
|
||||
char message[100];
|
||||
sprintf(message, "srv.init found %d modules from page at 0x%lx", page->count, address);
|
||||
j6_system_log(message);
|
||||
j6_log(message);
|
||||
|
||||
if (!first)
|
||||
first = page->modules;
|
||||
|
||||
@@ -20,32 +20,32 @@ extern "C" {
|
||||
void
|
||||
thread_proc()
|
||||
{
|
||||
j6_system_log("sub thread starting");
|
||||
j6_log("sub thread starting");
|
||||
|
||||
char buffer[512];
|
||||
size_t len = sizeof(buffer);
|
||||
j6_tag_t tag = 0;
|
||||
j6_status_t result = j6_endpoint_receive(endp, &tag, &len, (void*)buffer);
|
||||
j6_status_t result = j6_endpoint_receive(endp, &tag, (void*)buffer, &len);
|
||||
if (result != j6_status_ok)
|
||||
j6_thread_exit(result);
|
||||
|
||||
j6_system_log("sub thread received message");
|
||||
j6_log("sub thread received message");
|
||||
|
||||
for (int i = 0; i < len; ++i)
|
||||
if (buffer[i] >= 'A' && buffer[i] <= 'Z')
|
||||
buffer[i] += 0x20;
|
||||
|
||||
tag++;
|
||||
result = j6_endpoint_send(endp, tag, len, (void*)buffer);
|
||||
result = j6_endpoint_send(endp, tag, (void*)buffer, len);
|
||||
if (result != j6_status_ok)
|
||||
j6_thread_exit(result);
|
||||
|
||||
j6_system_log("sub thread sent message");
|
||||
j6_log("sub thread sent message");
|
||||
|
||||
for (int i = 1; i < 5; ++i)
|
||||
j6_thread_sleep(i*10);
|
||||
|
||||
j6_system_log("sub thread exiting");
|
||||
j6_log("sub thread exiting");
|
||||
j6_thread_exit(0);
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ main(int argc, const char **argv)
|
||||
j6_handle_t children[2] = {j6_handle_invalid, j6_handle_invalid};
|
||||
j6_signal_t out = 0;
|
||||
|
||||
j6_system_log("main thread starting");
|
||||
j6_log("main thread starting");
|
||||
|
||||
for (int i = 0; i < argc; ++i)
|
||||
j6_system_log(argv[i]);
|
||||
j6_log(argv[i]);
|
||||
|
||||
void *base = malloc(0x1000);
|
||||
if (!base)
|
||||
@@ -68,57 +68,57 @@ main(int argc, const char **argv)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
vma_ptr[i*100] = uint64_t(i);
|
||||
|
||||
j6_system_log("main thread wrote to memory area");
|
||||
j6_log("main thread wrote to memory area");
|
||||
|
||||
j6_status_t result = j6_endpoint_create(&endp);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
j6_system_log("main thread created endpoint");
|
||||
j6_log("main thread created endpoint");
|
||||
|
||||
result = j6_thread_create(reinterpret_cast<void*>(&thread_proc), &children[1]);
|
||||
result = j6_thread_create(&children[1], reinterpret_cast<uintptr_t>(&thread_proc));
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
j6_system_log("main thread created sub thread");
|
||||
j6_log("main thread created sub thread");
|
||||
|
||||
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
|
||||
size_t size = sizeof(message);
|
||||
j6_tag_t tag = 16;
|
||||
result = j6_endpoint_sendrecv(endp, &tag, &size, (void*)message);
|
||||
result = j6_endpoint_sendrecv(endp, &tag, (void*)message, &size);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
if (tag != 17)
|
||||
j6_system_log("GOT WRONG TAG FROM SENDRECV");
|
||||
j6_log("GOT WRONG TAG FROM SENDRECV");
|
||||
|
||||
result = j6_system_bind_irq(__handle_sys, endp, 3);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
j6_system_log(message);
|
||||
j6_log(message);
|
||||
|
||||
j6_system_log("main thread creating a new process");
|
||||
j6_log("main thread creating a new process");
|
||||
result = j6_process_create(&children[0]);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
j6_system_log("main thread waiting on children");
|
||||
j6_log("main thread waiting on children");
|
||||
|
||||
j6_handle_t outhandle;
|
||||
result = j6_object_wait_many(children, 2, -1ull, &outhandle, &out);
|
||||
result = j6_kobject_wait_many(children, 2, -1ull, &outhandle, &out);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
if (outhandle == children[1]) {
|
||||
j6_system_log(" ok from child thread");
|
||||
j6_log(" ok from child thread");
|
||||
} else if (outhandle == children[0]) {
|
||||
j6_system_log(" ok from child process");
|
||||
j6_log(" ok from child process");
|
||||
} else {
|
||||
j6_system_log(" ... got unknown handle");
|
||||
j6_log(" ... got unknown handle");
|
||||
}
|
||||
|
||||
j6_system_log("main testing irqs");
|
||||
j6_log("main testing irqs");
|
||||
|
||||
|
||||
serial_port com2(COM2);
|
||||
@@ -137,16 +137,10 @@ main(int argc, const char **argv)
|
||||
return result;
|
||||
|
||||
if (j6_tag_is_irq(tag))
|
||||
j6_system_log("main thread got irq!");
|
||||
j6_log("main thread got irq!");
|
||||
}
|
||||
|
||||
j6_system_log("main thread closing endpoint");
|
||||
|
||||
result = j6_object_close(endp);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
j6_system_log("main thread done, exiting");
|
||||
j6_log("main thread done, exiting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user