The branch main has been updated by brooks:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9ded074e875c29cb92d5f643801990d7bb23cca4

commit 9ded074e875c29cb92d5f643801990d7bb23cca4
Author:     agge3 <[email protected]>
AuthorDate: 2024-10-21 21:42:13 +0000
Commit:     Brooks Davis <[email protected]>
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 <[email protected]>
    Co-authored-by: Kyle Evans <[email protected]>
    Co-authored-by: Brooks Davis <[email protected]>
    Sponsored by:    Google (GSoC 24)
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/1362
    Signed-off-by: agge3 <[email protected]>
---
 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 <[email protected]>
+-- Copyright (c) 2023 Warner Losh <[email protected]>
+-- Copyright (c) 2019 Kyle Evans <[email protected]>
+--
+
+--
+-- 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 <[email protected]>
+-- Copyright (c) 2023 Warner Losh <[email protected]>
+-- Copyright (c) 2019 Kyle Evans <[email protected]>
+--
+
+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 <[email protected]>
+-- Copyright (c) 2023 Warner Losh <[email protected]>
+-- Copyright (c) 2019 Kyle Evans <[email protected]>
+--
+
+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 <[email protected]>
+-- Copyright (c) 2023 Warner Losh <[email protected]>
+-- Copyright (c) 2019 Kyle Evans <[email protected]>
+--
+
+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 <[email protected]>
+-- Copyright (c) 2023 Warner Losh <[email protected]>
+-- Copyright (c) 2019 Kyle Evans <[email protected]>
+--
+
+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 ***

Reply via email to