aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoragge3 <sterspark@gmail.com>2024-10-21 21:42:13 +0000
committerBrooks Davis <brooks@FreeBSD.org>2024-10-30 21:04:30 +0000
commit9ded074e875c29cb92d5f643801990d7bb23cca4 (patch)
tree95e739d9a9c1d1c1bc4a6925f48749adc03c99dc
parentd6bc956e143b194c221a9e697cd45a8a62028e86 (diff)
downloadsrc-9ded074e875c29cb92d5f643801990d7bb23cca4.tar.gz
src-9ded074e875c29cb92d5f643801990d7bb23cca4.zip
Refactor makesyscalls.lua into a library
* main.lua replicates the functionality of makesyscalls.lua * Individual files are generated by their associated module * Modules can be called as standalone scripts to generate a specific file * Data and procedures are performed by objects instead of procedual code * Bitmasks are replaced by declarative types * Temporary files are no longer produced, writing is stored in memory * Comments provide explanation to functions and semantics Google Summer of Code 2024 Final Work Product Co-authored-by: Warner Losh <imp@freebsd.org> Co-authored-by: Kyle Evans <kevans@freebsd.org> Co-authored-by: Brooks Davis <brooks@freebsd.org> Sponsored by: Google (GSoC 24) Pull Request: https://github.com/freebsd/freebsd-src/pull/1362 Signed-off-by: agge3 <sterspark@gmail.com>
-rw-r--r--sys/tools/syscalls/README.md49
-rw-r--r--sys/tools/syscalls/config.lua312
-rw-r--r--sys/tools/syscalls/core/freebsd-syscall.lua147
-rw-r--r--sys/tools/syscalls/core/scarg.lua163
-rw-r--r--sys/tools/syscalls/core/scret.lua45
-rw-r--r--sys/tools/syscalls/core/syscall.lua497
-rwxr-xr-xsys/tools/syscalls/main.lua64
-rwxr-xr-xsys/tools/syscalls/scripts/init_sysent.lua193
-rwxr-xr-xsys/tools/syscalls/scripts/libsys_h.lua111
-rwxr-xr-xsys/tools/syscalls/scripts/syscall_h.lua97
-rwxr-xr-xsys/tools/syscalls/scripts/syscall_mk.lua90
-rwxr-xr-xsys/tools/syscalls/scripts/syscalls.lua109
-rwxr-xr-xsys/tools/syscalls/scripts/syscalls_map.lua74
-rwxr-xr-xsys/tools/syscalls/scripts/sysproto_h.lua242
-rwxr-xr-xsys/tools/syscalls/scripts/systrace_args.lua268
-rw-r--r--sys/tools/syscalls/tools/generator.lua113
-rw-r--r--sys/tools/syscalls/tools/util.lua194
17 files changed, 2768 insertions, 0 deletions
diff --git a/sys/tools/syscalls/README.md b/sys/tools/syscalls/README.md
new file mode 100644
index 000000000000..7ae6519360ba
--- /dev/null
+++ b/sys/tools/syscalls/README.md
@@ -0,0 +1,49 @@
+# System call creation library
+Parses `syscalls.master` and packages information into objects with methods.
+Modules reproduce the previous file auto-generation of `makesyscalls.lua`.
+
+We generally assume that this script will be run by flua, however we've
+carefully crafted modules for it that mimic interfaces provided by modules
+available in ports. Currently, this script is compatible with lua from
+ports along with the compatible luafilesystem and lua-posix modules.
+
+## Usage
+`main.lua` generates all files.
+Files are associated with their respective modules, and modules can be run as
+standalone scripts to generate specific files.
+
+### Examples
+**All files:**
+`# /usr/libexec/flua /usr/src/sys/tools/syscalls/main.lua /usr/src/sys/kern/syscalls.master`
+<br>
+**syscalls.h:**
+`# /usr/libexec/flua /usr/src/sys/tools/syscalls/scripts/syscalls.h /usr/src/sys/kern/syscalls.master`
+
+## Organization
+* `root`
+ * `main.lua` - Main entry point that calls all scripts.
+ * `config.lua` - Contains the global configuration table and associated
+ configuration functions.
+
+ * `core` (Core Classes)
+ * `syscall.lua` - Packages each system call entry from `syscalls.master`
+ into a system call object.
+ * `scarg.lua` - Packages each argument for the system call into an argument
+ object.
+ * `scret.lua` - An object for the return value of the system call.
+ * `freebsd-syscall.lua` - Contains the master system call table after
+ processing.
+
+ * `scripts`
+ * `init_sysent.lua` - Generates `init_sysent.c`.
+ * `libsys_h.lua` - Generates `lib/libsys/_libsys.h`.
+ * `syscall_h.lua` - Generates `syscall.h`.
+ * `syscall_mk.lua` - Generates `syscall.mk`.
+ * `syscalls.lua` - Generates `syscalls.c`.
+ * `syscalls_map.lua` - Generates `lib/libsys/syscalls.map`.
+ * `sysproto_h.lua` - Generates `sysproto.h`.
+ * `systrace_args.lua` - Generates `systrace_args.c`.
+
+ * `tools`
+ * `util.lua` - Contains utility functions.
+ * `generator.lua` - Handles file generation for the library.
diff --git a/sys/tools/syscalls/config.lua b/sys/tools/syscalls/config.lua
new file mode 100644
index 000000000000..92098a709854
--- /dev/null
+++ b/sys/tools/syscalls/config.lua
@@ -0,0 +1,312 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2021-2024 SRI International
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+--
+-- Code to read in the config file that drives this. Since we inherit from the
+-- FreeBSD makesyscall.sh legacy, all config is done through a config file that
+-- sets a number of variables (as noted below); it used to be a .sh file that
+-- was sourced in. This dodges the need to write a command line parser.
+--
+
+local util = require("tools.util")
+
+--
+-- Global config map.
+-- Default configuration is native. Any of these may get replaced by an
+-- optionally specified configuration file.
+--
+local config = {
+ sysnames = "syscalls.c",
+ syshdr = "../sys/syscall.h",
+ sysmk = "/dev/null",
+ syssw = "init_sysent.c",
+ systrace = "systrace_args.c",
+ sysproto = "../sys/sysproto.h",
+ libsysmap = "/dev/null",
+ libsys_h = "/dev/null",
+ sysproto_h = "_SYS_SYSPROTO_H_",
+ syscallprefix = "SYS_",
+ switchname = "sysent",
+ namesname = "syscallnames",
+ abi_flags = {},
+ abi_func_prefix = "",
+ abi_type_suffix = "",
+ abi_long = "long",
+ abi_u_long = "u_long",
+ abi_semid_t = "semid_t",
+ abi_size_t = "size_t",
+ abi_ptr_array_t = "",
+ abi_headers = "",
+ abi_intptr_t = "intptr_t",
+ ptr_intptr_t_cast = "intptr_t",
+ obsol = {},
+ unimpl = {},
+ capabilities_conf = "capabilities.conf",
+ compat_set = "native",
+ mincompat = 0,
+ capenabled = {},
+ -- System calls that require ABI-specific handling.
+ syscall_abi_change = {},
+ -- System calls that appear to require handling, but don't.
+ syscall_no_abi_change = {},
+ -- Keep track of modifications if there are.
+ modifications = {},
+ -- Stores compat_sets from syscalls.conf; config.mergeCompat()
+ -- instantiates.
+ compat_options = {},
+}
+
+--
+-- For each entry, the ABI flag is the key. One may also optionally provide an
+-- expr, which are contained in an array associated with each key; expr gets
+-- applied to each argument type to indicate whether this argument is subject to
+-- ABI change given the configured flags.
+--
+config.known_abi_flags = {
+ long_size = {
+ "_Contains[a-z_]*_long_",
+ "^long [a-z0-9_]+$",
+ "long [*]",
+ "size_t [*]",
+ -- semid_t is not included because it is only used
+ -- as an argument or written out individually and
+ -- said writes are handled by the ksem framework.
+ -- Technically a sign-extension issue exists for
+ -- arguments, but because semid_t is actually a file
+ -- descriptor negative 32-bit values are invalid
+ -- regardless of sign-extension.
+ },
+ time_t_size = {
+ "_Contains[a-z_]*_timet_",
+ },
+ pointer_args = {
+ -- no expr
+ },
+ pointer_size = {
+ "_Contains[a-z_]*_ptr_",
+ "[*][*]",
+ },
+ pair_64bit = {
+ "^dev_t[ ]*$",
+ "^id_t[ ]*$",
+ "^off_t[ ]*$",
+ },
+}
+
+-- All compat option entries should have five entries:
+-- definition: The preprocessor macro that will be set for this.
+-- compatlevel: The level this compatibility should be included at. This
+-- generally represents the version of FreeBSD that it is compatible
+-- with, but ultimately it's just the level of mincompat in which it's
+-- included.
+-- flag: The name of the flag in syscalls.master.
+-- prefix: The prefix to use for _args and syscall prototype. This will be
+-- used as-is, without "_" or any other character appended.
+-- descr: The description of this compat option in init_sysent.c comments.
+-- The special "stdcompat" entry will cause the other five to be autogenerated.
+local compat_option_sets = {
+ native = {
+ {
+ definition = "COMPAT_43",
+ compatlevel = 3,
+ flag = "COMPAT",
+ prefix = "o",
+ descr = "old",
+ },
+ { stdcompat = "FREEBSD4" },
+ { stdcompat = "FREEBSD6" },
+ { stdcompat = "FREEBSD7" },
+ { stdcompat = "FREEBSD10" },
+ { stdcompat = "FREEBSD11" },
+ { stdcompat = "FREEBSD12" },
+ { stdcompat = "FREEBSD13" },
+ { stdcompat = "FREEBSD14" },
+ },
+}
+
+--
+-- config looks like a shell script; in fact, the previous makesyscalls.sh
+-- script actually sourced it in. It had a pretty common format, so we should
+-- be fine to make various assumptions.
+--
+-- This function processes config to be merged into our global config map with
+-- config.merge(). It aborts if there's malformed lines and returns NIL and a
+-- message if no file was provided.
+--
+function config.process(file)
+ local cfg = {}
+ local comment_line_expr = "^%s*#.*"
+ -- We capture any whitespace padding here so we can easily advance to
+ -- the end of the line as needed to check for any trailing bogus bits.
+ -- Alternatively, we could drop the whitespace and instead try to
+ -- use a pattern to strip out the meaty part of the line, but then we
+ -- would need to sanitize the line for potentially special characters.
+ local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)"
+
+ if not file then
+ return nil, "No file given"
+ end
+
+ local fh = assert(io.open(file))
+
+ for nextline in fh:lines() do
+ -- Strip any whole-line comments.
+ nextline = nextline:gsub(comment_line_expr, "")
+ -- Parse it into key, value pairs.
+ local key, value = nextline:match(line_expr)
+ if key ~= nil and value ~= nil then
+ local kvp = key .. "=" .. value
+ key = util.trim(key)
+ value = util.trim(value)
+ local delim = value:sub(1,1)
+ if delim == '"' then
+ local trailing_context
+
+ -- Strip off the key/value part.
+ trailing_context = nextline:sub(kvp:len() + 1)
+ -- Strip off any trailing comment.
+ trailing_context = trailing_context:gsub("#.*$",
+ "")
+ -- Strip off leading/trailing whitespace.
+ trailing_context = util.trim(trailing_context)
+ if trailing_context ~= "" then
+ print(trailing_context)
+ util.abort(1,
+ "Malformed line: " .. nextline)
+ end
+
+ value = util.trim(value, delim)
+ else
+ -- Strip off potential comments.
+ value = value:gsub("#.*$", "")
+ -- Strip off any padding whitespace.
+ value = util.trim(value)
+ if value:match("%s") then
+ util.abort(1,
+ "Malformed config line: " ..
+ nextline)
+ end
+ end
+ cfg[key] = value
+ elseif not nextline:match("^%s*$") then
+ -- Make sure format violations don't get overlooked
+ -- here, but ignore blank lines. Comments are already
+ -- stripped above.
+ util.abort(1, "Malformed config line: " .. nextline)
+ end
+ end
+
+ assert(fh:close())
+ return cfg
+end
+
+-- Merges processed configuration file into the global config map (see above),
+-- or returns NIL and a message if no file was provided.
+function config.merge(fh)
+ if not fh then
+ return nil, "No file given"
+ end
+
+ local res = assert(config.process(fh))
+
+ for k, v in pairs(res) do
+ if v ~= config[k] then
+ -- Handling of string lists:
+ if k:find("abi_flags") then
+ -- Match for pipe, that's how abi_flags
+ -- is formatted.
+ config[k] = util.setFromString(v, "[^|]+")
+ elseif k:find("capenabled") or
+ k:find("syscall_abi_change") or
+ k:find("syscall_no_abi_change") or
+ k:find("obsol") or
+ k:find("unimpl") then
+ -- Match for space, that's how these
+ -- are formatted.
+ config[k] = util.setFromString(v, "[^ ]+")
+ else
+ config[k] = v
+ end
+ -- Construct config modified table as config
+ -- is processed.
+ config.modifications[k] = true
+ else
+ -- config wasn't modified.
+ config.modifications[k] = false
+ end
+ end
+end
+
+-- Returns TRUE if there are ABI changes from native for the provided ABI flag.
+function config.abiChanges(name)
+ if config.known_abi_flags[name] == nil then
+ util.abort(1, "abi_changes: unknown flag: " .. name)
+ end
+ return config.abi_flags[name] ~= nil
+end
+
+-- Instantiates config.compat_options.
+function config.mergeCompat()
+ if config.compat_set ~= "" then
+ if not compat_option_sets[config.compat_set] then
+ util.abort(1, "Undefined compat set: " ..
+ config.compat_set)
+ end
+
+ config.compat_options = compat_option_sets[config.compat_set]
+ end
+end
+
+-- Parses the provided capabilities.conf. Returns a string (comma separated
+-- list) as its formatted in capabilities.conf, or NIL and a message if no file
+-- was provided.
+local function grabCapenabled(file, open_fail_ok)
+ local capentries = {}
+ local commentExpr = "#.*"
+
+ if file == nil then
+ return nil, "No file given"
+ end
+
+ local fh, msg, errno = io.open(file)
+ if fh == nil then
+ if not open_fail_ok then
+ util.abort(errno, msg)
+ end
+ return nil, msg
+ end
+
+ for nextline in fh:lines() do
+ -- Strip any comments.
+ nextline = nextline:gsub(commentExpr, "")
+ if nextline ~= "" then
+ capentries[nextline] = true
+ end
+ end
+
+ assert(fh:close())
+ return capentries
+end
+
+-- Merge capability (Capsicum) configuration into the global config.
+function config.mergeCapability()
+ -- We ignore errors here if we're relying on the default configuration.
+ if not config.modifications.capenabled then
+ config.capenabled = grabCapenabled(config.capabilities_conf,
+ config.modifications.capabilities_conf == nil)
+ elseif config.capenabled ~= "" then
+ -- We have a comma separated list from the format of
+ -- capabilities.conf, split it into a set with boolean values
+ -- for each key.
+ config.capenabled = util.setFromString(config.capenabled,
+ "[^,]+")
+ end
+end
+
+return config
diff --git a/sys/tools/syscalls/core/freebsd-syscall.lua b/sys/tools/syscalls/core/freebsd-syscall.lua
new file mode 100644
index 000000000000..193b1e43563c
--- /dev/null
+++ b/sys/tools/syscalls/core/freebsd-syscall.lua
@@ -0,0 +1,147 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local syscall = require("core.syscall")
+local util = require("tools.util")
+
+local FreeBSDSyscall = {}
+
+FreeBSDSyscall.__index = FreeBSDSyscall
+
+-- For each compat option in the provided config table, process them and insert
+-- them into known_flags for class syscall.
+function FreeBSDSyscall:processCompat()
+ for _, v in pairs(self.config.compat_options) do
+ if v.stdcompat ~= nil then
+ local stdcompat = v.stdcompat
+ v.definition = "COMPAT_" .. stdcompat:upper()
+ v.compatlevel = tonumber(stdcompat:match("([0-9]+)$"))
+ v.flag = stdcompat:gsub("FREEBSD", "COMPAT")
+ v.prefix = stdcompat:lower() .. "_"
+ v.descr = stdcompat:lower()
+ end
+
+ -- Add compat option to syscall.known_flags.
+ table.insert(syscall.known_flags, v.flag)
+ end
+end
+
+function FreeBSDSyscall:parseSysfile()
+ local file = self.sysfile
+ local config = self.config
+ local commentExpr = "^%s*;.*"
+
+ if file == nil then
+ return nil, "No file given"
+ end
+
+ self.syscalls = {}
+
+ local fh, msg = io.open(file)
+ if fh == nil then
+ return nil, msg
+ end
+
+ local incs = ""
+ local defs = ""
+ local s
+ for line in fh:lines() do
+ line = line:gsub(commentExpr, "") -- Strip any comments.
+ -- NOTE: Can't use pure pattern matching here because of
+ -- the 's' test and this is shorter than a generic pattern
+ -- matching pattern.
+ if line == nil or line == "" then
+ goto skip -- Blank line, skip this line.
+ elseif s ~= nil then
+ -- If we have a partial system call object s,
+ -- then feed it one more line.
+ if s:add(line) then
+ -- Append to system call list.
+ for t in s:iter() do
+ if t:validate(t.num - 1) then
+ table.insert(self.syscalls, t)
+ else
+ util.abort(1,
+ "Skipped system call " ..
+ "at number " .. t.num)
+ end
+ end
+ s = nil
+ end
+ elseif line:match("^#%s*include") then
+ incs = incs .. line .. "\n"
+ elseif line:match("%%ABI_HEADERS%%") then
+ local h = self.config.abi_headers
+ if h ~= nil and h ~= "" then
+ incs = incs .. h .. "\n"
+ end
+ elseif line:match("^#%s*define") then
+ defs = defs .. line.. "\n"
+ elseif line:match("^#") then
+ util.abort(1, "Unsupported cpp op " .. line)
+ else
+ s = syscall:new()
+ if s:add(line) then
+ -- Append to system call list.
+ for t in s:iter() do
+ if t:validate(t.num - 1) then
+ table.insert(self.syscalls, t)
+ else
+ util.abort(1,
+ "Skipped system call " ..
+ "at number " .. t.num)
+ end
+ end
+ s = nil
+ end
+ end
+ ::skip::
+ end
+
+ -- Special handling for linux nosys.
+ if config.syscallprefix:find("LINUX") ~= nil then
+ s = nil
+ end
+
+ if s ~= nil then
+ util.abort(1, "Dangling system call at the end")
+ end
+
+ assert(fh:close())
+ self.includes = incs
+ self.defines = defs
+end
+
+function FreeBSDSyscall:findStructs()
+ self.structs = {}
+
+ for _, s in pairs(self.syscalls) do
+ if s:native() and not s.type.NODEF then
+ for _, v in ipairs(s.args) do
+ local name = util.structName(v.type)
+ if name ~= nil then
+ self.structs[name] = name
+ end
+ end
+ end
+ end
+end
+
+function FreeBSDSyscall:new(obj)
+ obj = obj or {}
+ setmetatable(obj, self)
+ self.__index = self
+
+ obj:processCompat()
+ obj:parseSysfile()
+ obj:findStructs()
+
+ return obj
+end
+
+return FreeBSDSyscall
diff --git a/sys/tools/syscalls/core/scarg.lua b/sys/tools/syscalls/core/scarg.lua
new file mode 100644
index 000000000000..7ffbf15b3a80
--- /dev/null
+++ b/sys/tools/syscalls/core/scarg.lua
@@ -0,0 +1,163 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2021-2024 SRI International
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local config = require("config")
+local util = require("tools.util")
+
+local scarg = {}
+
+scarg.__index = scarg
+
+-- Check this argument against config for ABI changes from native. Return TRUE
+-- if there are.
+local function checkAbiChanges(arg)
+ for k, v in pairs(config.known_abi_flags) do
+ if config.abiChanges(k) and v ~= nil then
+ for _, e in pairs(v) do
+ if arg:find(e) then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+-- Strips the Microsoft(R) SAL annotations from this argument.
+local function stripArgAnnotations(arg)
+ arg = arg:gsub("_Contains_[^ ]*[_)] ?", "")
+ arg = arg:gsub("_In[^ ]*[_)] ?", "")
+ arg = arg:gsub("_Out[^ ]*[_)] ?", "")
+ return util.trim(arg)
+end
+
+-- Preprocessing of this argument.
+function scarg:init(line)
+ -- Trim whitespace and trailing comma. We don't want them here;
+ -- these can mess with our processing of this argument.
+ line = util.trim(line) -- This provides a clearer abort error.
+ self.scarg = util.trim(line, ',')
+
+ self.arg_abi_change = checkAbiChanges(self.scarg)
+ self.changes_abi = self.arg_abi_change
+ self.scarg = stripArgAnnotations(self.scarg)
+
+ self.name = self.scarg:match("([^* ]+)$")
+ -- Our pattern might produce a Lua pattern sequence; that's a malformed
+ -- declaration.
+ local status, type = pcall(function()
+ return util.trim(self.scarg:gsub(self.name .. "$", ""), nil)
+ end)
+ if not status then
+ util.abort(1, "Malformed argument line: " .. line)
+ end
+ self.type = type
+end
+
+-- Processes this argument.
+-- Flags if there's ABI changes from native, converts this argument to the
+-- target ABI, and handles 64-bit argument pairing.
+-- Returns TRUE if this argument is processed and ready to add.
+-- Returns FALSE if it shouldn't be added (the argument type is void).
+function scarg:process()
+ if self.type ~= "" and self.name ~= "void" then
+ -- util.is64bitType() needs a bare type so check it after
+ -- argname is removed.
+ self.changes_abi = self.changes_abi or
+ (config.abiChanges("pair_64bit") and
+ util.is64bitType(self.type))
+
+ self.type = self.type:gsub("intptr_t", config.abi_intptr_t)
+ self.type = self.type:gsub("semid_t", config.abi_semid_t)
+
+ if util.isPtrType(self.type) then
+ self.type = self.type:gsub("size_t", config.abi_size_t)
+ self.type = self.type:gsub("^long", config.abi_long)
+ self.type = self.type:gsub("^u_long", config.abi_u_long)
+ self.type = self.type:gsub("^const u_long", "const " ..
+ config.abi_u_long)
+ elseif self.type:find("^long$") then
+ self.type = config.abi_long
+ end
+
+ if util.isPtrArrayType(self.type) and
+ config.abi_ptr_array_t ~= "" then
+ -- `* const *` -> `**`
+ self.type = self.type:gsub("[*][ ]*const[ ]*[*]", "**")
+ -- e.g., `struct aiocb **` -> `uint32_t *`
+ self.type = self.type:gsub("[^*]*[*]",
+ config.abi_ptr_array_t .. " ", 1)
+ end
+
+ if self.arg_abi_change then
+ self.type = self.type:gsub("(struct [^ ]*)", "%1" ..
+ config.abi_type_suffix)
+ self.type = self.type:gsub("(union [^ ]*)", "%1" ..
+ config.abi_type_suffix)
+ end
+ return true
+ end
+ return false
+end
+
+-- For pairing 64-bit arguments, pad if necessary.
+-- Returns TRUE if this argument was padded.
+local function pad(tbl)
+ if #tbl % 2 == 1 then
+ table.insert(tbl, {
+ type = "int",
+ name = "_pad",
+ })
+ return true
+ end
+ return false
+end
+
+-- To append to a system call's argument table. Appends to the end.
+function scarg:append(tbl)
+ if config.abiChanges("pair_64bit") and util.is64bitType(self.type) then
+ pad(tbl) -- Needs argument padding.
+ table.insert(tbl, {
+ type = "uint32_t",
+ name = self.name .. "1",
+ })
+ table.insert(tbl, {
+ type = "uint32_t",
+ name = self.name .. "2",
+ })
+ else
+ table.insert(tbl, {
+ type = self.type,
+ name = self.name,
+ })
+ end
+end
+
+-- Returns TRUE if this argument has ABI changes from native.
+-- EXAMPLE: 32-bit argument for freebsd32.
+function scarg:changesAbi()
+ return self.changes_abi
+end
+
+function scarg:new(obj, line)
+ obj = obj or { }
+ setmetatable(obj, self)
+ self.__index = self
+
+ -- ABI changes that we only want in this scope.
+ self.arg_abi_change = false
+ -- ABI changes that we want the system call object to see.
+ self.changes_abi = false
+
+ obj:init(line)
+
+ return obj
+end
+
+return scarg
diff --git a/sys/tools/syscalls/core/scret.lua b/sys/tools/syscalls/core/scret.lua
new file mode 100644
index 000000000000..25522b4c830e
--- /dev/null
+++ b/sys/tools/syscalls/core/scret.lua
@@ -0,0 +1,45 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local util = require("tools.util")
+
+local scret = {}
+
+scret.__index = scret
+
+-- Processes this return type.
+function scret:process()
+ local words = util.split(self.scret, "%S+")
+ self.scret = words[1]
+ -- Pointer incoming.
+ if words[2]:sub(1,1) == "*" then
+ self.scret = self.scret .. " "
+ end
+ while words[2]:sub(1,1) == "*" do
+ words[2] = words[2]:sub(2)
+ self.scret = self.scret .. "*"
+ end
+end
+
+-- To add this return type to the system call.
+function scret:add()
+ self:process()
+ return self.scret
+end
+
+function scret:new(obj, line)
+ obj = obj or { }
+ setmetatable(obj, self)
+ self.__index = self
+
+ self.scret = line
+
+ return obj
+end
+
+return scret
diff --git a/sys/tools/syscalls/core/syscall.lua b/sys/tools/syscalls/core/syscall.lua
new file mode 100644
index 000000000000..7e8c562dad8a
--- /dev/null
+++ b/sys/tools/syscalls/core/syscall.lua
@@ -0,0 +1,497 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local config = require("config")
+local scarg = require("core.scarg")
+local scret = require("core.scret")
+local util = require("tools.util")
+
+local syscall = {}
+
+syscall.__index = syscall
+
+syscall.known_flags = util.set {
+ "STD",
+ "OBSOL",
+ "RESERVED",
+ "UNIMPL",
+ "NODEF",
+ "NOARGS",
+ "NOPROTO",
+ "NOSTD",
+ "NOTSTATIC",
+ "CAPENABLED",
+ "SYSMUX",
+}
+
+-- Native is an arbitrarily large number to have a constant and not
+-- interfere with compat numbers.
+local native = 1000000
+
+-- Processes and assigns the appropriate thread flag for this system call.
+function syscall:processThr()
+ self.thr = "SY_THR_STATIC"
+ for k, _ in pairs(self.type) do
+ if k == "NOTSTATIC" then
+ self.thr = "SY_THR_ABSENT"
+ end
+ end
+end
+
+-- Processes and assigns the appropriate capability flag for this system call.
+-- "SYF_CAPENABLED" for capability enabled; "0" for NOT capability enabled.
+function syscall:processCap()
+ self.cap = "0"
+ local stripped = util.stripAbiPrefix(self.name, self.prefix)
+ if config.capenabled ~= nil and (config.capenabled[self.name] ~= nil or
+ config.capenabled[stripped] ~= nil) then
+ self.cap = "SYF_CAPENABLED"
+ else
+ for k, _ in pairs(self.type) do
+ if k == "CAPENABLED" then
+ self.cap = "SYF_CAPENABLED"
+ end
+ end
+ end
+end
+
+-- Check that this system call has a known type.
+local function checkType(type)
+ for k, _ in pairs(type) do
+ if not syscall.known_flags[k] and not
+ k:match("^COMPAT") then
+ util.abort(1, "Bad type: " .. k)
+ end
+ end
+end
+
+-- If there are ABI changes from native, process this system call to match the
+-- target ABI.
+function syscall:processChangesAbi()
+ -- First, confirm we want to uphold our changes_abi flag.
+ if config.syscall_no_abi_change[self.name] then
+ self.changes_abi = false
+ end
+ self.noproto = not util.isEmpty(config.abi_flags) and
+ not self.changes_abi
+ if config.abiChanges("pointer_args") then
+ for _, v in ipairs(self.args) do
+ if util.isPtrType(v.type, config.abi_intptr_t) then
+ if config.syscall_no_abi_change[self.name] then
+ print("WARNING: " .. self.name ..
+ " in syscall_no_abi_change, " ..
+ "but pointers args are present")
+ end
+ self.changes_abi = true
+ goto ptrfound
+ end
+ end
+ ::ptrfound::
+ end
+ if config.syscall_abi_change[self.name] then
+ self.changes_abi = true
+ end
+ if self.changes_abi then
+ self.noproto = false
+ end
+end
+
+-- Final processing of flags. Process any flags that haven't already been
+-- processed (e.g., dictionaries from syscalls.conf).
+function syscall:processFlags()
+ if config.obsol[self.name] or (self:compatLevel() > 0 and
+ self:compatLevel() < tonumber(config.mincompat)) then
+ self.args = nil
+ self.type.OBSOL = true
+ -- Don't apply any ABI handling, declared as obsolete.
+ self.changes_abi = false
+ end
+ if config.unimpl[self.name] then
+ self.type.UNIMPL = true
+ end
+ if self.noproto or self.type.SYSMUX then
+ self.type.NOPROTO = true
+ end
+ if self.type.NODEF then
+ self.audit = "AUE_NULL"
+ end
+end
+
+-- Returns TRUE if prefix and arg_prefix are assigned; FALSE if they're left
+-- unassigned. Relies on a valid changes_abi flag, so should be called AFTER
+-- processChangesAbi().
+function syscall:processPrefix()
+ -- If there are ABI changes from native, assign the correct prefixes.
+ if self.changes_abi then
+ self.arg_prefix = config.abi_func_prefix
+ self.prefix = config.abi_func_prefix
+ return true
+ end
+ return false
+end
+
+-- Validate that we're not skipping system calls by comparing this system call
+-- number to the previous system call number. Called higher up the call stack
+-- by class FreeBSDSyscall.
+function syscall:validate(prev)
+ return prev + 1 == self.num
+end
+
+-- Return the compat prefix for this system call.
+function syscall:compatPrefix()
+ local c = self:compatLevel()
+ if self.type.OBSOL then
+ return "obs_"
+ end
+ if self.type.RESERVED then
+ return "reserved #"
+ end
+ if self.type.UNIMPL then
+ return "unimp_"
+ end
+ if c == 3 then
+ return "o"
+ end
+ if c < native then
+ return "freebsd" .. tostring(c) .. "_"
+ end
+ return ""
+end
+
+-- Return the symbol name for this system call.
+function syscall:symbol()
+ return self:compatPrefix() .. self.name
+end
+
+--
+-- Return the compatibility level for this system call.
+-- 0 is obsolete.
+-- < 0 is this isn't really a system call we care about.
+-- 3 is 4.3BSD in theory, but anything before FreeBSD 4.
+-- >= 4 is FreeBSD version, this system call was replaced with a new
+-- version.
+--
+function syscall:compatLevel()
+ if self.type.UNIMPL or self.type.RESERVED then
+ return -1
+ elseif self.type.OBSOL then
+ return 0
+ elseif self.type.COMPAT then
+ return 3
+ end
+ for k, _ in pairs(self.type) do
+ local l = k:match("^COMPAT(%d+)")
+ if l ~= nil then
+ return tonumber(l)
+ end
+ end
+ return native
+end
+
+-- Adds the definition for this system call. Guarded by whether we already have
+-- a system call number or not.
+function syscall:addDef(line)
+ if self.num == nil then
+ local words = util.split(line, "%S+")
+ self.num = words[1]
+ self.audit = words[2]
+ self.type = util.setFromString(words[3], "[^|]+")
+ checkType(self.type)
+ self.name = words[4]
+ -- These next three are optional, and either all present
+ -- or all absent.
+ self.altname = words[5]
+ self.alttag = words[6]
+ self.rettype = words[7]
+ return true
+ end
+ return false
+end
+
+-- Adds the function declaration for this system call. If addDef() found an
+-- opening curly brace, then we're looking for a function declaration.
+function syscall:addFunc(line)
+ if self.name == "{" then
+ local words = util.split(line, "%S+")
+ -- Expect line is `type syscall(` or `type syscall(void);`.
+ if #words ~= 2 then
+ util.abort(1, "Malformed line " .. line)
+ end
+
+ local ret = scret:new({}, line)
+ self.ret = ret:add()
+ -- Don't clobber rettype set in the alt information.
+ if self.rettype == nil then
+ self.rettype = "int"
+ end
+
+ self.name = words[2]:match("([%w_]+)%(")
+ if words[2]:match("%);$") then
+ -- Now we're looking for ending curly brace.
+ self.expect_rbrace = true
+ end
+ return true
+ end
+ return false
+end
+
+-- Adds the argument(s) for this system call. Once addFunc() assigns a name
+-- for this system call, arguments are next in syscalls.master.
+function syscall:addArgs(line)
+ if not self.expect_rbrace then
+ if line:match("%);$") then
+ self.expect_rbrace = true
+ return true
+ end
+ local arg = scarg:new({}, line)
+ -- We don't want to add this argument if it doesn't process.
+ -- scarg:process() handles those conditions.
+ if arg:process() then
+ arg:append(self.args)
+ end
+ -- If this argument has ABI changes, set globally for this
+ -- system call.
+ self.changes_abi = self.changes_abi or arg:changesAbi()
+ return true
+ end
+ return false
+end
+
+-- Once we have a good syscall, add some final information to it.
+function syscall:finalize()
+ if self.name == nil then
+ self.name = ""
+ end
+
+ -- Preserve the original name as the alias.
+ self.alias = self.name
+
+ self:processChangesAbi() -- process changes to the ABI
+ self:processFlags() -- process any unprocessed flags
+
+ -- If there's changes to the ABI, these prefixes will be changed by
+ -- processPrefix(); otherwise, they'll remain empty.
+ self.prefix = ""
+ self.arg_prefix = ""
+ self:processPrefix()
+
+ self:processCap() -- capability flag
+ self:processThr() -- thread flag
+
+ -- Assign argument alias.
+ if self.alttag ~= nil then
+ self.arg_alias = self.alttag
+ elseif self.arg_alias == nil and self.name ~= nil then
+ -- argalias should be:
+ -- COMPAT_PREFIX + ABI Prefix + funcname
+ self.arg_alias = self:compatPrefix() .. self.arg_prefix ..
+ self.name .. "_args"
+ elseif self.arg_alias ~= nil then
+ self.arg_alias = self.arg_prefix .. self.arg_alias
+ end
+
+ -- An empty string would not want a prefix; the entry doesn't have
+ -- a name so we want to keep the empty string.
+ if self.name ~= nil and self.name ~= "" then
+ self.name = self.prefix .. self.name
+ end
+
+ self:processArgstrings()
+ self:processArgsize()
+end
+
+-- Assigns the correct args_size. Defaults to "0", except if there's arguments
+-- or NODEF flag.
+function syscall:processArgsize()
+ if self.type.SYSMUX then -- catch this first
+ self.args_size = "0"
+ elseif self.arg_alias ~= nil and
+ (#self.args ~= 0 or self.type.NODEF) then
+ self.args_size = "AS(" .. self.arg_alias .. ")"
+ else
+ self.args_size = "0"
+ end
+end
+
+-- Constructs argstr_* strings for generated declerations/wrappers.
+function syscall:processArgstrings()
+ local type = ""
+ local type_var = ""
+ local var = ""
+ local comma = ""
+
+ for _, v in ipairs(self.args) do
+ local argname, argtype = v.name, v.type
+ type = type .. comma .. argtype
+ type_var = type_var .. comma .. argtype .. " " .. argname
+ var = var .. comma .. argname
+ comma = ", "
+ end
+ if type == "" then
+ type = "void"
+ type_var = "void"
+ end
+
+ self.argstr_type = type
+ self.argstr_type_var = type_var
+ self.argstr_var = var
+end
+
+-- Interface to add this system call to the master system call table.
+-- The system call is built up one line at a time. The states describe the
+-- current parsing state.
+-- Returns TRUE when ready to add and FALSE while still parsing.
+function syscall:add(line)
+ if self:addDef(line) then
+ return self:isAdded(line)
+ end
+ if self:addFunc(line) then
+ return false -- Function added; keep going.
+ end
+ if self:addArgs(line) then
+ return false -- Arguments added; keep going.
+ end
+ return self:isAdded(line) -- Final validation, before adding.
+end
+
+-- Returns TRUE if this system call was succesfully added. There's two entry
+-- points to this function: (1) the entry in syscalls.master is one-line, or
+-- (2) the entry is a full system call. This function handles those cases and
+-- decides whether to exit early for (1) or validate a full system call for
+-- (2). This function also handles cases where we don't want to add, and
+-- instead want to abort.
+function syscall:isAdded(line)
+ -- This system call is a range - exit early.
+ if tonumber(self.num) == nil then
+ -- The only allowed types are RESERVED and UNIMPL.
+ if not (self.type.RESERVED or self.type.UNIMPL) then
+ util.abort(1, "Range only allowed with RESERVED " ..
+ "and UNIMPL: " .. line)
+ end
+ self:finalize()
+ return true
+ -- This system call is a loadable system call - exit early.
+ elseif self.altname ~= nil and self.alttag ~= nil and
+ self.rettype ~= nil then
+ self:finalize()
+ return true
+ -- This system call is only one line, and should only be one line
+ -- (we didn't make it to addFunc()) - exit early.
+ elseif self.name ~= "{" and self.ret == nil then
+ self:finalize()
+ return true
+ -- This is a full system call and we've passed multiple states to
+ -- get here - final exit.
+ elseif self.expect_rbrace then
+ if not line:match("}$") then
+ util.abort(1, "Expected '}' found '" .. line ..
+ "' instead.")
+ end
+ self:finalize()
+ return true
+ end
+ return false
+end
+
+-- Return TRUE if this system call is native.
+function syscall:native()
+ return self:compatLevel() == native
+end
+
+-- Make a shallow copy of `self` and replace the system call number with num
+-- (which should be a number).
+-- For system call ranges.
+function syscall:shallowCopy(num)
+ local obj = syscall:new()
+
+ -- shallow copy
+ for k, v in pairs(self) do
+ obj[k] = v
+ end
+ obj.num = num -- except override range
+ return obj
+end
+
+-- Make a deep copy of the parameter object. Save copied tables in `copies`,
+-- indexed by original table.
+-- CREDIT: http://lua-users.org/wiki/CopyTable
+-- For a full system call (the nested arguments table should be a deep copy).
+local function deepCopy(orig, copies)
+ copies = copies or {}
+ local orig_type = type(orig)
+ local copy
+ if orig_type == 'table' then
+ if copies[orig] then
+ copy = copies[orig]
+ else
+ copy = {}
+ copies[orig] = copy
+ for orig_key, orig_value in next, orig, nil do
+ copy[deepCopy(orig_key, copies)] =
+ deepCopy(orig_value, copies)
+ end
+ setmetatable(copy, deepCopy(getmetatable(orig), copies))
+ end
+ else -- number, string, boolean, etc
+ copy = orig
+ end
+ return copy
+end
+
+--
+-- In syscalls.master, system calls come in two types: (1) a fully defined
+-- system call with function declaration, with a distinct number for each system
+-- call; or (2) a one-line entry, sometimes with a distinct number and sometimes
+-- with a range of numbers. One-line entries can be obsolete, reserved, no
+-- definition, etc. Ranges are only allowed for reserved and unimplemented.
+--
+-- This function provides the iterator to traverse system calls by number. If
+-- the entry is a fully defined system call with a distinct number, the iterator
+-- creates a deep copy and captures any nested objects; if the entry is a range
+-- of numbers, the iterator creates shallow copies from the start of the range
+-- to the end of the range.
+--
+function syscall:iter()
+ local s = tonumber(self.num)
+ local e
+ if s == nil then
+ s, e = string.match(self.num, "(%d+)%-(%d+)")
+ s, e = tonumber(s), tonumber(e)
+ return function ()
+ if s <= e then
+ s = s + 1
+ return self:shallowCopy(s - 1)
+ end
+ end
+ else
+ e = s
+ self.num = s -- Replace string with number, like the clones.
+ return function ()
+ if s == e then
+ local deep_copy = deepCopy(self)
+ s = e + 1
+ return deep_copy
+ end
+ end
+ end
+end
+
+function syscall:new(obj)
+ obj = obj or { }
+ setmetatable(obj, self)
+ self.__index = self
+
+ self.expect_rbrace = false
+ self.changes_abi = false
+ self.args = {}
+ self.noproto = false
+
+ return obj
+end
+
+return syscall
diff --git a/sys/tools/syscalls/main.lua b/sys/tools/syscalls/main.lua
new file mode 100755
index 000000000000..8f791eec0943
--- /dev/null
+++ b/sys/tools/syscalls/main.lua
@@ -0,0 +1,64 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+-- Thanks to Kyle Evans for his makesyscall.lua in FreeBSD which served as
+-- inspiration for this, and as a source of code at times.
+--
+
+-- When we have a path, add it to the package.path (. is already in the list).
+if arg[0]:match("/") then
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/?.lua"
+end
+
+-- Common config file management.
+local config = require("config")
+-- FreeBSDSyscall generates a table of system calls from syscalls.master.
+local FreeBSDSyscall = require("core.freebsd-syscall")
+
+-- Modules for each file:
+local init_sysent = require("scripts.init_sysent")
+local libsys_h = require("scripts.libsys_h")
+local syscall_h = require("scripts.syscall_h")
+local syscall_mk = require("scripts.syscall_mk")
+local syscalls = require("scripts.syscalls")
+local syscalls_map = require("scripts.syscalls_map")
+local sysproto_h = require("scripts.sysproto_h")
+local systrace_args = require("scripts.systrace_args")
+
+-- Entry:
+if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+end
+
+local sysfile, configfile = arg[1], arg[2]
+
+config.merge(configfile)
+config.mergeCompat()
+config.mergeCapability()
+
+local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+-- Output files:
+init_sysent.file = config.syssw
+libsys_h.file = config.libsys_h
+syscall_h.file = config.syshdr
+syscall_mk.file = config.sysmk
+syscalls.file = config.sysnames
+syscalls_map.file = config.libsysmap
+sysproto_h.file = config.sysproto
+systrace_args.file = config.systrace
+
+init_sysent.generate(tbl, config, init_sysent.file)
+libsys_h.generate(tbl, config, libsys_h.file)
+syscall_h.generate(tbl, config, syscall_h.file)
+syscall_mk.generate(tbl, config, syscall_mk.file)
+syscalls.generate(tbl, config, syscalls.file)
+syscalls_map.generate(tbl, config, syscalls_map.file)
+sysproto_h.generate(tbl, config, sysproto_h.file)
+systrace_args.generate(tbl, config, systrace_args.file)
diff --git a/sys/tools/syscalls/scripts/init_sysent.lua b/sys/tools/syscalls/scripts/init_sysent.lua
new file mode 100755
index 000000000000..106c51be5f8a
--- /dev/null
+++ b/sys/tools/syscalls/scripts/init_sysent.lua
@@ -0,0 +1,193 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local init_sysent = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+init_sysent.file = "/dev/null"
+
+function init_sysent.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("System call switch table.")
+
+ gen:write(tbl.includes)
+
+ -- Newline before and after this line.
+ gen:write(
+ "\n#define AS(name) (sizeof(struct name) / sizeof(syscallarg_t))\n")
+
+ -- Write out all the compat directives from compat_options.
+ for _, v in pairs(config.compat_options) do
+ gen:write(string.format([[
+
+#ifdef %s
+#define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
+#else
+#define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
+#endif
+]], v.definition, v.flag:lower(), v.prefix, v.flag:lower()))
+ end
+ -- Add a newline only if there were compat_options.
+ if config.compat_options ~= nil then
+ gen:write("\n")
+ end
+
+ gen:write(string.format([[
+/* The casts are bogus but will do for now. */
+struct sysent %s[] = {
+]], config.switchname))
+
+ for _, v in pairs(s) do
+ local c = v:compatLevel()
+ -- Comment is the function name by default, but may change
+ -- based on the type of system call.
+ local comment = v.name
+
+ -- Handle non-compat:
+ if v:native() then
+ gen:write(string.format(
+ "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)",
+ v.args_size))
+ -- Handle SYSMUX flag:
+ if v.type.SYSMUX then
+ gen:write(string.format("nosys, " ..
+ ".sy_auevent = AUE_NULL, " ..
+ ".sy_flags = %s, " ..
+ ".sy_thrcnt = SY_THR_STATIC },",
+ v.cap))
+ -- Handle NOSTD flag:
+ elseif v.type.NOSTD then
+ gen:write(string.format("lkmressys, " ..
+ ".sy_auevent = AUE_NULL, " ..
+ ".sy_flags = %s, " ..
+ ".sy_thrcnt = SY_THR_ABSENT },",
+ v.cap))
+ -- Handle rest of non-compat:
+ else
+ if v.name == "nosys" or
+ v.name == "lkmnosys" or
+ v.name == "sysarch" or
+ v.name:find("^freebsd") or
+ v.name:find("^linux") then
+ gen:write(string.format("%s, " ..
+ ".sy_auevent = %s, " ..
+ ".sy_flags = %s, " ..
+ ".sy_thrcnt = %s },",
+ v:symbol(), v.audit, v.cap, v.thr))
+ else
+ gen:write(string.format("sys_%s, " ..
+ ".sy_auevent = %s, " ..
+ ".sy_flags = %s, " ..
+ ".sy_thrcnt = %s },",
+ v:symbol(), v.audit, v.cap, v.thr))
+ end
+ end
+
+ -- Handle compat (everything >= FREEBSD3):
+ elseif c >= 3 then
+ -- Lookup the info for this specific compat option.
+ local flag, descr
+ for _, opt in pairs(config.compat_options) do
+ if opt.compatlevel == c then
+ flag = opt.flag
+ flag = flag:lower()
+ descr = opt.descr
+ break
+ end
+ end
+
+ if v.type.NOSTD then
+ gen:write(string.format("\t{ " ..
+ ".sy_narg = %s, " ..
+ ".sy_call = (sy_call_t *)%s, " ..
+ ".sy_auevent = %s, " ..
+ ".sy_flags = 0, " ..
+ ".sy_thrcnt = SY_THR_ABSENT },",
+ "0", "lkmressys", "AUE_NULL"))
+ else
+ gen:write(string.format("\t{ %s(%s,%s), " ..
+ ".sy_auevent = %s, " ..
+ ".sy_flags = %s, " ..
+ ".sy_thrcnt = %s },",
+ flag, v.args_size, v.name, v.audit, v.cap, v.thr))
+ end
+ comment = descr .. " " .. v.name
+
+ -- Handle obsolete:
+ elseif v.type.OBSOL then
+ gen:write("\t{ " ..
+ ".sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
+ ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
+ ".sy_thrcnt = SY_THR_ABSENT },")
+ comment = "obsolete " .. v.name
+
+ -- Handle unimplemented:
+ elseif v.type.UNIMPL then
+ gen:write("\t{ " ..
+ ".sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
+ ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
+ ".sy_thrcnt = SY_THR_ABSENT },")
+ -- UNIMPL comment is not different in sysent.
+
+ -- Handle reserved:
+ elseif v.type.RESERVED then
+ gen:write("\t{ " ..
+ ".sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
+ ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
+ ".sy_thrcnt = SY_THR_ABSENT },")
+ comment = "reserved for local use"
+ end
+
+ gen:write(string.format("\t/* %d = %s */\n", v.num, comment))
+ end
+
+ -- End
+ gen:write("};\n")
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed syscall table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ init_sysent.file = config.syssw -- change file here
+ init_sysent.generate(tbl, config, init_sysent.file)
+end
+
+-- Return the module.
+return init_sysent
diff --git a/sys/tools/syscalls/scripts/libsys_h.lua b/sys/tools/syscalls/scripts/libsys_h.lua
new file mode 100755
index 000000000000..18ffc68fded6
--- /dev/null
+++ b/sys/tools/syscalls/scripts/libsys_h.lua
@@ -0,0 +1,111 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 SRI International
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local libsys_h = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+local util = require("tools.util")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+libsys_h.file = "/dev/null"
+
+function libsys_h.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ local print_decl = function (sc)
+ return sc:native() and not sc.type.NODEF and
+ not sc.type.SYSMUX and sc.name ~= "yield"
+ end
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("Public system call stubs provided by libsys.\n" ..
+ "\n" ..
+ "Do not use directly, include <libsys.h> instead.")
+
+ gen:write(string.format([[
+#ifndef __LIBSYS_H_
+#define __LIBSYS_H_
+
+#include <sys/_cpuset.h>
+#include <sys/_domainset.h>
+#include <sys/_ffcounter.h>
+#include <sys/_semaphore.h>
+#include <sys/_sigaltstack.h>
+#include <machine/ucontext.h> /* for mcontext_t */
+#include <sys/_ucontext.h>
+#include <sys/wait.h>
+
+]]))
+
+ for name, _ in util.pairsByKeys(tbl.structs) do
+ gen:write(string.format("struct %s;\n", name))
+ end
+ gen:write("union semun;\n")
+
+ gen:write("\n__BEGIN_DECLS\n")
+
+ for _, v in pairs(s) do
+ if print_decl(v) then
+ gen:write(string.format(
+ "typedef %s (__sys_%s_t)(%s);\n",
+ v.ret, v.name, v.argstr_type))
+ end
+ end
+
+ gen:write("\n")
+
+ for _, v in pairs(s) do
+ if print_decl(v) then
+ gen:write(string.format("%s __sys_%s(%s);\n",
+ v.ret, v.name, v.argstr_type_var))
+ end
+ end
+
+ gen:write("__END_DECLS\n")
+ -- End
+ gen:write("\n#endif /* __LIBSYS_H_ */\n")
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed syscall table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ libsys_h.file = config.libsys_h -- change file here
+ libsys_h.generate(tbl, config, libsys_h.file)
+end
+
+-- Return the module.
+return libsys_h
diff --git a/sys/tools/syscalls/scripts/syscall_h.lua b/sys/tools/syscalls/scripts/syscall_h.lua
new file mode 100755
index 000000000000..ce5ffcec4b94
--- /dev/null
+++ b/sys/tools/syscalls/scripts/syscall_h.lua
@@ -0,0 +1,97 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local syscall_h = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+syscall_h.file = "/dev/null"
+
+-- Libc has all the STD, NOSTD and SYSMUX system calls in it, as well as
+-- replaced system calls dating back to FreeBSD 7. We are lucky that the
+-- system call filename is just the base symbol name for it.
+function syscall_h.generate(tbl, config, fh)
+ -- Grab the master system calls table, and prepare bookkeeping for
+ -- the max system call number.
+ local s = tbl.syscalls
+ local max = 0
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("System call numbers.")
+
+ for _, v in pairs(s) do
+ local c = v:compatLevel()
+ if v.num > max then
+ max = v.num
+ end
+ if v.type.UNIMPL then
+ goto skip
+ elseif v.type.RESERVED then
+ goto skip
+ elseif v.type.NODEF then
+ goto skip
+ elseif v.type.STD or v.type.NOSTD or v.type.SYSMUX or
+ c >= 7 then
+ gen:write(string.format("#define\t%s%s%s\t%d\n",
+ config.syscallprefix, v:compatPrefix(), v.name,
+ v.num))
+ elseif c >= 0 then
+ local comment
+ if c == 0 then
+ comment = "obsolete"
+ elseif c == 3 then
+ comment = "old"
+ else
+ comment = "freebsd" .. c
+ end
+ gen:write(string.format("\t\t\t\t/* %d is %s %s */\n",
+ v.num, comment, v.name))
+ end
+ ::skip::
+ end
+ gen:write(string.format("#define\t%sMAXSYSCALL\t%d\n",
+ config.syscallprefix, max + 1))
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed system call table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ syscall_h.file = config.syshdr -- change file here
+ syscall_h.generate(tbl, config, syscall_h.file)
+end
+
+-- Return the module.
+return syscall_h
diff --git a/sys/tools/syscalls/scripts/syscall_mk.lua b/sys/tools/syscalls/scripts/syscall_mk.lua
new file mode 100755
index 000000000000..d4347a050cf1
--- /dev/null
+++ b/sys/tools/syscalls/scripts/syscall_mk.lua
@@ -0,0 +1,90 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local syscall_mk = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+syscall_mk.file = "/dev/null"
+
+-- Libc has all the STD, NOSTD and SYSMUX system calls in it, as well as
+-- replaced system calls dating back to FreeBSD 7. We are lucky that the
+-- system call filename is just the base symbol name for it.
+function syscall_mk.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+ -- Bookkeeping for keeping track of when we're at the last system
+ -- call (no backslash).
+ local size = #s
+ local idx = 0
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("FreeBSD system call object files.", "#")
+
+ gen:write("MIASM = \\\n") -- preamble
+ for _, v in pairs(s) do
+ local c = v:compatLevel()
+ idx = idx + 1
+ if v:native() and not v.type.NODEF then
+ if idx >= size then
+ -- At last system call, no backslash.
+ gen:write(string.format("\t%s.o\n", v:symbol()))
+ else
+ -- Normal behavior.
+ gen:write(string.format("\t%s.o \\\n", v:symbol()))
+ end
+ -- Handle compat (everything >= FREEBSD3):
+ elseif c >= 7 and not v.type.NODEF then
+ if idx >= size then
+ -- At last system call, no backslash.
+ gen:write(string.format("\t%s.o\n", v:symbol()))
+ else
+ -- Normal behavior.
+ gen:write(string.format("\t%s.o \\\n", v:symbol()))
+ end
+ end
+ end
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed syscall table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ syscall_mk.file = config.sysmk -- change file here
+ syscall_mk.generate(tbl, config, syscall_mk.file)
+end
+
+-- Return the module.
+return syscall_mk
diff --git a/sys/tools/syscalls/scripts/syscalls.lua b/sys/tools/syscalls/scripts/syscalls.lua
new file mode 100755
index 000000000000..b1a3d1020f88
--- /dev/null
+++ b/sys/tools/syscalls/scripts/syscalls.lua
@@ -0,0 +1,109 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local syscalls = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+syscalls.file = "/dev/null"
+
+function syscalls.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("System call names.")
+
+ gen:write(string.format("const char *%s[] = {\n", config.namesname))
+
+ for _, v in pairs(s) do
+ --print("num " .. v.num .. " name " .. v.name)
+ local c = v:compatLevel()
+ if v:native() then
+ gen:write(string.format([[
+ "%s", /* %d = %s */
+]],
+ v.name, v.num, v.name))
+ elseif c >= 3 then
+ -- Lookup the info for this specific compat option.
+ local flag, descr
+ for _, opt in pairs(config.compat_options) do
+ if opt.compatlevel == c then
+ flag = opt.flag
+ flag = flag:lower()
+ descr = opt.descr
+ break
+ end
+ end
+
+ gen:write(string.format([[
+ "%s.%s", /* %d = %s %s */
+]],
+ flag, v.name, v.num, descr, v.name))
+
+ elseif v.type.RESERVED then
+ gen:write(string.format([[
+ "#%d", /* %d = reserved for local use */
+]],
+ v.num, v.num))
+
+ elseif v.type.UNIMPL then
+ gen:write(string.format([[
+ "#%d", /* %d = %s */
+]],
+ v.num, v.num, v.alias))
+
+ elseif v.type.OBSOL then
+ gen:write(string.format([[
+ "obs_%s", /* %d = obsolete %s */
+]],
+ v.name, v.num, v.name))
+
+ end
+ end
+ -- End
+ gen:write("};\n")
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed syscall table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ syscalls.file = config.sysnames -- change file here
+ syscalls.generate(tbl, config, syscalls.file)
+end
+
+-- Return the module.
+return syscalls
diff --git a/sys/tools/syscalls/scripts/syscalls_map.lua b/sys/tools/syscalls/scripts/syscalls_map.lua
new file mode 100755
index 000000000000..4108d9f46fcf
--- /dev/null
+++ b/sys/tools/syscalls/scripts/syscalls_map.lua
@@ -0,0 +1,74 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 SRI International
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local syscalls_map = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+syscalls_map.file = "/dev/null"
+
+function syscalls_map.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+
+ -- Write the generated preamble.
+ gen:preamble("FreeBSD system call symbols.")
+
+ gen:write(string.format("FBSDprivate_1.0 {\n"))
+
+ for _, v in pairs(s) do
+ --print("num " .. v.num .. " name " .. v.name)
+ if v:native() and not v.type.NODEF and v.name ~= "yield" then
+ if v.name ~= "exit" and v.name ~= "vfork" then
+ gen:write(string.format("\t_%s;\n", v.name))
+ end
+ gen:write(string.format("\t__sys_%s;\n", v.name))
+ end
+ end
+ -- End
+ gen:write("};\n")
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed syscall table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ syscalls_map.file = config.libsysmap -- change file here
+ syscalls_map.generate(tbl, config, syscalls_map.file)
+end
+
+-- Return the module.
+return syscalls_map
diff --git a/sys/tools/syscalls/scripts/sysproto_h.lua b/sys/tools/syscalls/scripts/sysproto_h.lua
new file mode 100755
index 000000000000..d4fc30c31292
--- /dev/null
+++ b/sys/tools/syscalls/scripts/sysproto_h.lua
@@ -0,0 +1,242 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local sysproto_h = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+sysproto_h.file = "/dev/null"
+
+function sysproto_h.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+ gen.storage_levels = {} -- make sure storage is clear
+
+ -- Write the generated preamble.
+ gen:preamble("System call prototypes.")
+
+ -- Write out all the preprocessor directives.
+ gen:write(string.format([[
+#ifndef %s
+#define %s
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/cpuset.h>
+#include <sys/domainset.h>
+#include <sys/_ffcounter.h>
+#include <sys/_semaphore.h>
+#include <sys/ucontext.h>
+#include <sys/wait.h>
+
+#include <bsm/audit_kevents.h>
+
+struct proc;
+
+struct thread;
+
+#define PAD_(t) (sizeof(syscallarg_t) <= sizeof(t) ? \
+ 0 : sizeof(syscallarg_t) - sizeof(t))
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define PADL_(t) 0
+#define PADR_(t) PAD_(t)
+#else
+#define PADL_(t) PAD_(t)
+#define PADR_(t) 0
+#endif
+
+]], config.sysproto_h, config.sysproto_h))
+
+ -- 64-bit padding preprocessor directive.
+ gen:pad64(config.abiChanges("pair_64bit"))
+
+ --
+ -- Storing each compat entry requires storing multiple levels of file
+ -- generation; compat entries are given ranges of 10 instead to cope
+ -- with this. For example, 13 is indexed as 130; 131 is the second
+ -- storage level of 13.
+ --
+
+ -- Store all the compat #ifdef from compat_options at their zero index.
+ for _, v in pairs(config.compat_options) do
+ -- Tag an extra newline to the end, so it doesn't have to be
+ -- worried about later.
+ gen:store(string.format("\n#ifdef %s\n\n", v.definition),
+ v.compatlevel * 10)
+ end
+
+ for _, v in pairs(s) do
+ local c = v:compatLevel()
+
+ -- Audit defines are stored at an arbitrarily large number so
+ -- that they're always at the last storage level, and compat
+ -- entries can be indexed by their compat level (more
+ -- intuitive).
+ local audit_idx = 10000 -- this should do
+
+ -- Handle non-compat:
+ if v:native() then
+ -- All these negation conditions are because (in
+ -- general) these are cases where code for sysproto.h
+ -- is not generated.
+ if not v.type.NOARGS and not v.type.NOPROTO and
+ not v.type.NODEF then
+ if #v.args > 0 then
+ gen:write(string.format(
+ "struct %s {\n", v.arg_alias))
+ for _, arg in ipairs(v.args) do
+ if arg.type == "int" and
+ arg.name == "_pad" and
+ config.abiChanges(
+ "pair_64bit") then
+ gen:write("#ifdef PAD64_REQUIRED\n")
+ end
+
+ gen:write(string.format([[
+ char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];
+]],
+ arg.name, arg.type,
+ arg.type, arg.name,
+ arg.name, arg.type))
+
+ if arg.type == "int" and
+ arg.name == "_pad" and
+ config.abiChanges(
+ "pair_64bit") then
+ gen:write("#endif\n")
+ end
+ end
+ gen:write("};\n")
+ else
+ gen:write(string.format(
+ "struct %s {\n\tsyscallarg_t dummy;\n};\n",
+ v.arg_alias))
+ end
+ end
+ if not v.type.NOPROTO and not v.type.NODEF then
+ local sys_prefix = "sys_"
+ if v.name == "nosys" or v.name == "lkmnosys" or
+ v.name == "sysarch" or
+ v.name:find("^freebsd") or
+ v.name:find("^linux") then
+ sys_prefix = ""
+ end
+ gen:store(string.format(
+ "%s\t%s%s(struct thread *, struct %s *);\n",
+ v.rettype, sys_prefix, v.name, v.arg_alias),
+ 1)
+ gen:store(string.format(
+ "#define\t%sAUE_%s\t%s\n",
+ config.syscallprefix, v:symbol(), v.audit),
+ audit_idx)
+ end
+
+ -- Handle compat (everything >= FREEBSD3):
+ elseif c >= 3 then
+ local idx = c * 10
+ if not v.type.NOARGS and not v.type.NOPROTO and
+ not v.type.NODEF then
+ if #v.args > 0 then
+ gen:store(string.format(
+ "struct %s {\n", v.arg_alias), idx)
+ for _, arg in ipairs(v.args) do
+ gen:store(string.format([[
+ char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];
+]],
+ arg.name, arg.type,
+ arg.type, arg.name,
+ arg.name, arg.type), idx)
+ end
+ gen:store("};\n", idx)
+ else
+ -- Not stored, written on the first run.
+ gen:write(string.format([[
+struct %s {
+ syscallarg_t dummy;
+};
+]],
+ v.arg_alias))
+ end
+ end
+ if not v.type.NOPROTO and not v.type.NODEF then
+ gen:store(string.format([[
+%s %s%s(struct thread *, struct %s *);
+]],
+ v.rettype, v:compatPrefix(), v.name,
+ v.arg_alias), idx + 1)
+ gen:store(string.format([[
+#define %sAUE_%s%s %s
+]],
+ config.syscallprefix, v:compatPrefix(),
+ v.name, v.audit), audit_idx)
+ end
+ end
+ -- Do nothing for obsolete, unimplemented, and reserved.
+ end
+
+ -- Append #endif to the end of each compat option.
+ for _, v in pairs(config.compat_options) do
+ -- Based on how they're indexed, 9 is the last index.
+ local end_idx = (v.compatlevel * 10) + 9
+ -- Need an extra newline after #endif.
+ gen:store(string.format("\n#endif /* %s */\n\n", v.definition),
+ end_idx)
+ end
+
+ if gen.storage_levels ~= nil then
+ gen:writeStorage()
+ end
+
+ -- After storage has been unrolled, tag on the ending bits.
+ gen:write(string.format([[
+
+#undef PAD_
+#undef PADL_
+#undef PADR_
+
+#endif /* !%s */
+]], config.sysproto_h))
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed system call table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ sysproto_h.file = config.sysproto -- change file here
+ sysproto_h.generate(tbl, config, sysproto_h.file)
+end
+
+-- Return the module.
+return sysproto_h
diff --git a/sys/tools/syscalls/scripts/systrace_args.lua b/sys/tools/syscalls/scripts/systrace_args.lua
new file mode 100755
index 000000000000..abd5d284d46e
--- /dev/null
+++ b/sys/tools/syscalls/scripts/systrace_args.lua
@@ -0,0 +1,268 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+-- Setup to be a module, or ran as its own script.
+local systrace_args = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local util = require("tools.util")
+local generator = require("tools.generator")
+
+-- File has not been decided yet; config will decide file. Default defined as
+-- /dev/null.
+systrace_args.file = "/dev/null"
+
+function systrace_args.generate(tbl, config, fh)
+ -- Grab the master system calls table.
+ local s = tbl.syscalls
+
+ -- Bind the generator to the parameter file.
+ local gen = generator:new({}, fh)
+ gen.storage_levels = {} -- make sure storage is clear
+
+ -- 64-bit padding preprocessor directive.
+ gen:pad64(config.abiChanges("pair_64bit"))
+
+ -- Write the generated preamble.
+ gen:preamble(
+ "System call argument to DTrace register array conversion.\n" ..
+ "\n" ..
+ "This file is part of the DTrace syscall provider.")
+
+ gen:write(string.format([[
+static void
+systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
+{
+ int64_t *iarg = (int64_t *)uarg;
+ int a = 0;
+ switch (sysnum) {
+]]))
+
+ gen:store(string.format([[
+static void
+systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
+{
+ const char *p = NULL;
+ switch (sysnum) {
+]]), 1)
+
+ gen:store(string.format([[
+static void
+systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
+{
+ const char *p = NULL;
+ switch (sysnum) {
+]]), 2)
+
+ for _, v in pairs(s) do
+
+ -- Handle non compat:
+ if v:native() then
+ gen:write(string.format([[
+ /* %s */
+ case %d: {
+]], v.name, v.num))
+
+ gen:store(string.format([[
+ /* %s */
+ case %d:
+]], v.name, v.num), 1)
+
+ gen:store(string.format([[
+ /* %s */
+ case %d:
+]], v.name, v.num), 2)
+
+ local n_args = #v.args
+ if v.type.SYSMUX then
+ n_args = 0
+ end
+
+ if #v.args > 0 and not v.type.SYSMUX then
+ local padding = ""
+
+ gen:write(string.format([[
+ struct %s *p = params;
+]],
+ v.arg_alias))
+
+ gen:store([[
+ switch (ndx) {
+]],
+ 1)
+
+ for idx, arg in ipairs(v.args) do
+ local argtype = util.trim(
+ arg.type:gsub(
+ "__restrict$", ""), nil)
+ if argtype == "int" and
+ arg.name == "_pad" and
+ config.abiChanges("pair_64bit") then
+ gen:store(
+ "#ifdef PAD64_REQUIRED\n",
+ 1)
+ end
+
+ -- Pointer arg?
+ local desc
+ if argtype:find("*") then
+ desc = "userland " .. argtype
+ else
+ desc = argtype;
+ end
+
+ gen:store(string.format([[
+ case %d%s:
+ p = "%s";
+ break;
+]],
+ idx - 1, padding, desc), 1)
+
+ if argtype == "int" and
+ arg.name == "_pad" and
+ config.abiChanges("pair_64bit") then
+ padding = " - _P_"
+ gen:store([[
+#define _P_ 0
+#else
+#define _P_ 1
+#endif
+]],
+ 1)
+ end
+
+ if util.isPtrType(argtype,
+ config.abi_intptr_t) then
+ gen:write(string.format([[
+ uarg[a++] = (%s)p->%s; /* %s */
+]],
+ config.ptr_intptr_t_cast,
+ arg.name, argtype))
+ elseif argtype == "union l_semun" then
+ gen:write(string.format([[
+ uarg[a++] = p->%s.buf; /* %s */
+]],
+ arg.name, argtype))
+ elseif argtype:sub(1,1) == "u" or
+ argtype == "size_t" then
+ gen:write(string.format([[
+ uarg[a++] = p->%s; /* %s */
+]],
+ arg.name, argtype))
+ else
+ if argtype == "int" and
+ arg.name == "_pad" and
+ config.abiChanges(
+ "pair_64bit") then
+ gen:write([[
+#ifdef PAD64_REQUIRED
+]])
+ end
+
+ gen:write(string.format([[
+ iarg[a++] = p->%s; /* %s */
+]],
+ arg.name, argtype))
+
+ if argtype == "int" and
+ arg.name == "_pad" and
+ config.abiChanges(
+ "pair_64bit") then
+ gen:write("#endif\n")
+ end
+ end
+ end
+
+ gen:store([[
+ default:
+ break;
+ };
+]],
+ 1)
+
+ if padding ~= "" then
+ gen:store("#undef _P_\n\n", 1)
+ end
+
+ gen:store(string.format([[
+ if (ndx == 0 || ndx == 1)
+ p = "%s";
+ break;
+]], v.ret), 2)
+ end
+
+ gen:write(string.format("\t\t*n_args = %d;\n\t\tbreak;\n\t}\n",
+ n_args))
+ gen:store("\t\tbreak;\n", 1)
+
+ -- Handle compat (everything >= FREEBSD3):
+ -- Do nothing, only for native.
+ end
+ end
+
+ gen:write([[
+ default:
+ *n_args = 0;
+ break;
+ };
+}
+]])
+ gen:store([[
+ default:
+ break;
+ };
+ if (p != NULL)
+ strlcpy(desc, p, descsz);
+}
+]], 1)
+ gen:store([[
+ default:
+ break;
+ };
+ if (p != NULL)
+ strlcpy(desc, p, descsz);
+}
+]], 2)
+
+ -- Write all stored lines.
+ if gen.storage_levels ~= nil then
+ gen:writeStorage()
+ end
+
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+ config.mergeCapability()
+
+ -- The parsed system call table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ systrace_args.file = config.systrace -- change file here
+ systrace_args.generate(tbl, config, systrace_args.file)
+end
+
+-- Return the module.
+return systrace_args
diff --git a/sys/tools/syscalls/tools/generator.lua b/sys/tools/syscalls/tools/generator.lua
new file mode 100644
index 000000000000..bb6378a27ed6
--- /dev/null
+++ b/sys/tools/syscalls/tools/generator.lua
@@ -0,0 +1,113 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local util = require("tools.util")
+
+local generator = {}
+
+generator.__index = generator
+
+-- Wrapper for Lua write() best practice, for a simpler write call.
+function generator:write(line)
+ assert(self.gen:write(line))
+end
+
+--
+-- A write macro for the PAD64 preprocessor directive.
+-- Used for 32-bit configurations by passing config.abiChanges("pair_64bit") and
+-- padding will be done if necessary.
+--
+-- PARAM: bool, TRUE to pad.
+--
+function generator:pad64(bool)
+ if bool then
+ self:write([[
+#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
+#define PAD64_REQUIRED
+#endif
+]])
+ end
+end
+
+-- Returns the generated tag.
+function generator:tag()
+ return self.tag
+end
+
+generator.storage_levels = {}
+
+-- Optional level to specify which order to store in, which defaults to one if
+-- not provided.
+function generator:store(str, level)
+ level = level or 1
+ self.storage_levels[level] = self.storage_levels[level] or {}
+ table.insert(self.storage_levels[level], str)
+end
+
+-- Write all storage in the order it was stored.
+function generator:writeStorage()
+ if self.storage_levels ~= nil then
+ for _, v in util.ipairsSparse(self.storage_levels) do
+ for _, line in ipairs(v) do
+ generator:write(line)
+ end
+ end
+ end
+end
+
+--
+-- Writes the generated preamble. Default comment is C comments.
+--
+-- PARAM: String str, the title for the file.
+--
+-- PARAM: String comment, nil or optional to change comment (e.g., "#" for sh
+-- comments).
+--
+-- SEE: style(9)
+--
+function generator:preamble(str, comment)
+ if str ~= nil then
+ local comment_start = comment or "/*"
+ local comment_middle = comment or " *"
+ local comment_end = comment or " */"
+ self:write(string.format("%s\n", comment_start))
+ -- Splits our string into lines split by newline, or is just the
+ -- original string if there's no newlines.
+ for line in str:gmatch("[^\n]*") do
+ -- Only add a space after the comment if there's
+ -- text on this line.
+ local space
+ if line ~= "" then
+ space = " "
+ else
+ space = ""
+ end
+ -- Make sure to append the newline back.
+ self:write(string.format("%s%s%s\n", comment_middle,
+ space, line))
+ end
+ self:write(string.format([[%s
+%s DO NOT EDIT-- this file is automatically %s.
+%s
+
+]], comment_middle, comment_middle, self.tag, comment_end))
+ end
+end
+
+-- generator binds to the parameter file.
+function generator:new(obj, fh)
+ obj = obj or { }
+ setmetatable(obj, self)
+ self.__index = self
+
+ self.gen = assert(io.open(fh, "w+"))
+ self.tag = "@" .. "generated"
+
+ return obj
+end
+
+return generator
diff --git a/sys/tools/syscalls/tools/util.lua b/sys/tools/syscalls/tools/util.lua
new file mode 100644
index 000000000000..c9ff98dda786
--- /dev/null
+++ b/sys/tools/syscalls/tools/util.lua
@@ -0,0 +1,194 @@
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
+-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
+-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+--
+
+local util = {}
+
+--
+-- Returns a trimmed string. Default is to trim whitespace.
+--
+-- PARAM: String s, the string to trim.
+--
+-- PARAM: char, nil or optional character to trim.
+--
+function util.trim(s, char)
+ if s == nil then
+ return nil
+ end
+ if char == nil then
+ char = "%s"
+ end
+ return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
+end
+
+-- Returns a table (list) of strings.
+function util.split(s, re)
+ local t = { }
+
+ for v in s:gmatch(re) do
+ table.insert(t, v)
+ end
+ return t
+end
+
+-- Aborts with a message and does a clean exit procedure.
+function util.abort(status, msg)
+ assert(io.stderr:write(msg .. "\n"))
+ -- cleanup
+ os.exit(status)
+end
+
+--
+-- Returns a set.
+--
+-- PARAM: t, a list.
+--
+-- EXAMPLE: param: {"foo", "bar"}, return: {foo = true, bar = true}
+--
+function util.set(t)
+ local s = { }
+
+ for _,v in pairs(t) do
+ s[v] = true
+ end
+ return s
+end
+
+--
+-- Returns a set.
+--
+-- PARAM: str, a string.
+-- PARAM: re, the pattern to construct keys from.
+--
+function util.setFromString(str, re)
+ local s = { }
+
+ for v in str:gmatch(re) do
+ s[v] = true
+ end
+ return s
+end
+
+function util.isEmpty(tbl)
+ if tbl ~= nil then
+ if next(tbl) == nil then
+ return true
+ end
+ return false
+ end
+ return true
+end
+
+--
+-- Iterator that traverses a table following the order of its keys.
+-- An optional parameter f allows the specification of an alternative order.
+--
+-- CREDIT: https://www.lua.org/pil/19.3.html
+-- LICENSE: MIT
+--
+function util.pairsByKeys(t, f)
+ local a = {}
+ for n in pairs(t) do table.insert(a, n) end
+ table.sort(a, f)
+ local i = 0 -- iterator variable
+ local iter = function () -- iterator function
+ i = i + 1
+ if a[i] == nil then
+ return nil
+ else
+ return a[i], t[a[i]]
+ end
+ end
+ return iter
+end
+
+--
+-- Checks for pointer types: '*', caddr_t, or intptr_t.
+--
+-- PARAM: type, the type to check.
+--
+-- PARAM: abi, nil or optional ABI-specified intptr_t.
+--
+function util.isPtrType(type, abi)
+ local default = abi or "intptr_t"
+ return type:find("*") or type:find("caddr_t") or type:find(default)
+end
+
+function util.isPtrArrayType(type)
+ return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
+end
+
+-- Find types that are always 64-bits wide.
+function util.is64bitType(type)
+ return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or
+ type:find("^off_t[ ]*$")
+end
+
+--
+-- Returns the name of the struct pointed to by the argument or nil.
+--
+-- PARAM: type, the type to check.
+--
+function util.structName(type)
+ if util.isPtrType(type) then
+ local is_struct = false
+ for word in type:gmatch("[^ *]+") do
+ if is_struct then
+ return word
+ end
+ if word == "struct" then
+ -- next word is the struct name
+ is_struct = true
+ end
+ end
+ end
+ return nil
+end
+
+-- Strip the ABI function prefix if it exists (e.g., "freebsd32_"). Returns the
+-- function name with the ABI prefix stripped, or the original function name if
+-- there was no ABI function prefix.
+function util.stripAbiPrefix(funcname, abiprefix)
+ local stripped_name
+ if funcname == nil then
+ return nil
+ end
+ if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
+ stripped_name = funcname:gsub("^" .. abiprefix, "")
+ else
+ stripped_name = funcname
+ end
+
+ return stripped_name
+end
+
+-- ipairs for a sparse array.
+-- CREDIT: Lua Game Development Cookbook, Mario Kasuba
+function util.ipairsSparse(t)
+ -- tmp_index will hold sorted indices, otherwise
+ -- this iterator would be no different from pairs iterator
+ local tmp_index = {}
+ local index, _ = next(t)
+ while index do
+ tmp_index[#tmp_index + 1] = index
+ index, _ = next(t, index)
+ end
+ -- sort table indices
+ table.sort(tmp_index)
+ local j = 1
+
+ return function()
+ -- get index value
+ local i = tmp_index[j]
+ j = j + 1
+ if i then
+ return i, t[i]
+ end
+ end
+end
+
+return util