git: 9ded074e875c - main - Refactor makesyscalls.lua into a library
Date: Wed, 30 Oct 2024 21:08:55 UTC
The branch main has been updated by brooks:
URL: https://cgit.FreeBSD.org/src/commit/?id=9ded074e875c29cb92d5f643801990d7bb23cca4
commit 9ded074e875c29cb92d5f643801990d7bb23cca4
Author: agge3 <sterspark@gmail.com>
AuthorDate: 2024-10-21 21:42:13 +0000
Commit: Brooks Davis <brooks@FreeBSD.org>
CommitDate: 2024-10-30 21:04:30 +0000
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>
---
sys/tools/syscalls/README.md | 49 +++
sys/tools/syscalls/config.lua | 312 +++++++++++++++++
sys/tools/syscalls/core/freebsd-syscall.lua | 147 ++++++++
sys/tools/syscalls/core/scarg.lua | 163 +++++++++
sys/tools/syscalls/core/scret.lua | 45 +++
sys/tools/syscalls/core/syscall.lua | 497 +++++++++++++++++++++++++++
sys/tools/syscalls/main.lua | 64 ++++
sys/tools/syscalls/scripts/init_sysent.lua | 193 +++++++++++
sys/tools/syscalls/scripts/libsys_h.lua | 111 ++++++
sys/tools/syscalls/scripts/syscall_h.lua | 97 ++++++
sys/tools/syscalls/scripts/syscall_mk.lua | 90 +++++
sys/tools/syscalls/scripts/syscalls.lua | 109 ++++++
sys/tools/syscalls/scripts/syscalls_map.lua | 74 ++++
sys/tools/syscalls/scripts/sysproto_h.lua | 242 +++++++++++++
sys/tools/syscalls/scripts/systrace_args.lua | 268 +++++++++++++++
sys/tools/syscalls/tools/generator.lua | 113 ++++++
sys/tools/syscalls/tools/util.lua | 194 +++++++++++
17 files changed, 2768 insertions(+)
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
*** 1933 LINES SKIPPED ***