diff options
author | agge3 <sterspark@gmail.com> | 2024-10-21 21:42:13 +0000 |
---|---|---|
committer | Brooks Davis <brooks@FreeBSD.org> | 2024-10-30 21:04:30 +0000 |
commit | 9ded074e875c29cb92d5f643801990d7bb23cca4 (patch) | |
tree | 95e739d9a9c1d1c1bc4a6925f48749adc03c99dc | |
parent | d6bc956e143b194c221a9e697cd45a8a62028e86 (diff) | |
download | src-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.md | 49 | ||||
-rw-r--r-- | sys/tools/syscalls/config.lua | 312 | ||||
-rw-r--r-- | sys/tools/syscalls/core/freebsd-syscall.lua | 147 | ||||
-rw-r--r-- | sys/tools/syscalls/core/scarg.lua | 163 | ||||
-rw-r--r-- | sys/tools/syscalls/core/scret.lua | 45 | ||||
-rw-r--r-- | sys/tools/syscalls/core/syscall.lua | 497 | ||||
-rwxr-xr-x | sys/tools/syscalls/main.lua | 64 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/init_sysent.lua | 193 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/libsys_h.lua | 111 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/syscall_h.lua | 97 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/syscall_mk.lua | 90 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/syscalls.lua | 109 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/syscalls_map.lua | 74 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/sysproto_h.lua | 242 | ||||
-rwxr-xr-x | sys/tools/syscalls/scripts/systrace_args.lua | 268 | ||||
-rw-r--r-- | sys/tools/syscalls/tools/generator.lua | 113 | ||||
-rw-r--r-- | sys/tools/syscalls/tools/util.lua | 194 |
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 |