Path: blob/main/sys/tools/syscalls/core/syscall.lua
39530 views
--1-- SPDX-License-Identifier: BSD-2-Clause2--3-- Copyright (c) 2024 Tyler Baxter <[email protected]>4-- Copyright (c) 2023 Warner Losh <[email protected]>5-- Copyright (c) 2019 Kyle Evans <[email protected]>6--78local config = require("config")9local scarg = require("core.scarg")10local scret = require("core.scret")11local util = require("tools.util")1213local syscall = {}1415syscall.__index = syscall1617syscall.known_flags = util.set {18"STD",19"OBSOL",20"RESERVED",21"UNIMPL",22"NODEF",23"NOARGS",24"NOPROTO",25"NOSTD",2627-- flags beyond this point are modifiers28"CAPENABLED",29"NOLIB",30"NORETURN",31"NOTSTATIC",32"SYSMUX",33}3435-- Native is an arbitrarily large number to have a constant and not36-- interfere with compat numbers.37local native = 10000003839-- Processes and assigns the appropriate thread flag for this system call.40function syscall:processThr()41self.thr = "SY_THR_STATIC"42for k, _ in pairs(self.type) do43if k == "NOTSTATIC" then44self.thr = "SY_THR_ABSENT"45end46end47end4849-- Processes and assigns the appropriate capability flag for this system call.50-- "SYF_CAPENABLED" for capability enabled; "0" for NOT capability enabled.51function syscall:processCap()52self.cap = "0"53local stripped = util.stripAbiPrefix(self.name, self.prefix)54for k, _ in pairs(self.type) do55if k == "CAPENABLED" then56self.cap = "SYF_CAPENABLED"57end58end59end6061-- Check that this system call has a known type.62local function checkType(type)63for k, _ in pairs(type) do64if not syscall.known_flags[k] and not65k:match("^COMPAT") then66util.abort(1, "Bad type: " .. k)67end68end69end7071-- If there are ABI changes from native, process this system call to match the72-- target ABI.73function syscall:processChangesAbi()74-- First, confirm we want to uphold our changes_abi flag.75if config.syscall_no_abi_change[self.name] then76self.changes_abi = false77end78self.noproto = not util.isEmpty(config.abi_flags) and79not self.changes_abi80if config.abiChanges("pointer_args") then81for _, v in ipairs(self.args) do82if util.isPtrType(v.type, config.abi_intptr_t) then83if config.syscall_no_abi_change[self.name] then84print("WARNING: " .. self.name ..85" in syscall_no_abi_change, " ..86"but pointers args are present")87end88self.changes_abi = true89goto ptrfound90end91end92::ptrfound::93end94if config.syscall_abi_change[self.name] then95self.changes_abi = true96end97if self.changes_abi then98self.noproto = false99end100end101102-- Final processing of flags. Process any flags that haven't already been103-- processed (e.g., dictionaries from syscalls.conf).104function syscall:processFlags()105if config.obsol[self.name] or (self:compatLevel() > 0 and106self:compatLevel() < tonumber(config.mincompat)) then107self.args = nil108self.type.OBSOL = true109-- Don't apply any ABI handling, declared as obsolete.110self.changes_abi = false111end112if config.unimpl[self.name] then113self.type.UNIMPL = true114end115if self.noproto or self.type.SYSMUX then116self.type.NOPROTO = true117end118if self.type.NODEF then119self.audit = "AUE_NULL"120end121end122123-- Returns TRUE if prefix and arg_prefix are assigned; FALSE if they're left124-- unassigned. Relies on a valid changes_abi flag, so should be called AFTER125-- processChangesAbi().126function syscall:processPrefix()127-- If there are ABI changes from native, assign the correct prefixes.128if self.changes_abi then129self.arg_prefix = config.abi_func_prefix130self.prefix = config.abi_func_prefix131return true132end133return false134end135136-- Validate that we're not skipping system calls by comparing this system call137-- number to the previous system call number. Called higher up the call stack138-- by class FreeBSDSyscall.139function syscall:validate(prev)140return prev + 1 == self.num141end142143-- Return the compat prefix for this system call.144function syscall:compatPrefix()145local c = self:compatLevel()146if self.type.OBSOL then147return "obs_"148end149if self.type.RESERVED then150return "reserved #"151end152if self.type.UNIMPL then153return "unimp_"154end155if c == 3 then156return "o"157end158if c < native then159return "freebsd" .. tostring(c) .. "_"160end161return ""162end163164-- Return the symbol name for this system call.165function syscall:symbol()166return self:compatPrefix() .. self.name167end168169--170-- Return the compatibility level for this system call.171-- 0 is obsolete.172-- < 0 is this isn't really a system call we care about.173-- 3 is 4.3BSD in theory, but anything before FreeBSD 4.174-- >= 4 is FreeBSD version, this system call was replaced with a new175-- version.176--177function syscall:compatLevel()178if self.type.UNIMPL or self.type.RESERVED then179return -1180elseif self.type.OBSOL then181return 0182elseif self.type.COMPAT then183return 3184end185for k, _ in pairs(self.type) do186local l = k:match("^COMPAT(%d+)")187if l ~= nil then188return tonumber(l)189end190end191return native192end193194-- Adds the definition for this system call. Guarded by whether we already have195-- a system call number or not.196function syscall:addDef(line)197if self.num == nil then198local words = util.split(line, "%S+")199self.num = words[1]200self.audit = words[2]201self.type = util.setFromString(words[3], "[^|]+")202checkType(self.type)203self.name = words[4]204-- These next three are optional, and either all present205-- or all absent.206self.altname = words[5]207self.alttag = words[6]208self.rettype = words[7]209return true210end211return false212end213214-- Adds the function declaration for this system call. If addDef() found an215-- opening curly brace, then we're looking for a function declaration.216function syscall:addFunc(line)217if self.name == "{" then218local words = util.split(line, "%S+")219-- Expect line is `type syscall(` or `type syscall(void);`.220if #words ~= 2 then221util.abort(1, "Malformed line " .. line)222end223224local ret = scret:new({}, line)225self.ret = ret:add()226-- Don't clobber rettype set in the alt information.227if self.rettype == nil then228self.rettype = "int"229end230231self.name = words[2]:match("([%w_]+)%(")232if words[2]:match("%);$") then233-- Now we're looking for ending curly brace.234self.expect_rbrace = true235end236return true237end238return false239end240241-- Adds the argument(s) for this system call. Once addFunc() assigns a name242-- for this system call, arguments are next in syscalls.master.243function syscall:addArgs(line)244if not self.expect_rbrace then245if line:match("%);$") then246self.expect_rbrace = true247return true248end249local arg = scarg:new({}, line)250-- We don't want to add this argument if it doesn't process.251-- scarg:process() handles those conditions.252if arg:process() then253arg:append(self.args)254end255-- If this argument has ABI changes, set globally for this256-- system call.257self.changes_abi = self.changes_abi or arg:changesAbi()258return true259end260return false261end262263-- Once we have a good syscall, add some final information to it.264function syscall:finalize()265if self.name == nil then266self.name = ""267end268269-- Preserve the original name as the alias.270self.alias = self.name271272self:processChangesAbi() -- process changes to the ABI273self:processFlags() -- process any unprocessed flags274275-- If there's changes to the ABI, these prefixes will be changed by276-- processPrefix(); otherwise, they'll remain empty.277self.prefix = ""278self.arg_prefix = ""279self:processPrefix()280281self:processCap() -- capability flag282self:processThr() -- thread flag283284-- Assign argument alias.285if self.alttag ~= nil then286self.arg_alias = self.alttag287elseif self.arg_alias == nil and self.name ~= nil then288-- argalias should be:289-- COMPAT_PREFIX + ABI Prefix + funcname290self.arg_alias = self:compatPrefix() .. self.arg_prefix ..291self.name .. "_args"292elseif self.arg_alias ~= nil then293self.arg_alias = self.arg_prefix .. self.arg_alias294end295296-- An empty string would not want a prefix; the entry doesn't have297-- a name so we want to keep the empty string.298if self.name ~= nil and self.name ~= "" then299self.name = self.prefix .. self.name300end301302self:processArgstrings()303self:processArgsize()304end305306-- Assigns the correct args_size. Defaults to "0", except if there's arguments307-- or NODEF flag.308function syscall:processArgsize()309if self.type.SYSMUX then -- catch this first310self.args_size = "0"311elseif self.arg_alias ~= nil and312(#self.args ~= 0 or self.type.NODEF) then313self.args_size = "AS(" .. self.arg_alias .. ")"314else315self.args_size = "0"316end317end318319-- Constructs argstr_* strings for generated declerations/wrappers.320function syscall:processArgstrings()321local type = ""322local type_var = ""323local var = ""324local comma = ""325326for _, v in ipairs(self.args) do327local argname, argtype = v.name, v.type328type = type .. comma .. argtype329type_var = type_var .. comma .. argtype .. " " .. argname330var = var .. comma .. argname331comma = ", "332end333if type == "" then334type = "void"335type_var = "void"336end337338self.argstr_type = type339self.argstr_type_var = type_var340self.argstr_var = var341end342343-- Interface to add this system call to the master system call table.344-- The system call is built up one line at a time. The states describe the345-- current parsing state.346-- Returns TRUE when ready to add and FALSE while still parsing.347function syscall:add(line)348if self:addDef(line) then349return self:isAdded(line)350end351if self:addFunc(line) then352return false -- Function added; keep going.353end354if self:addArgs(line) then355return false -- Arguments added; keep going.356end357return self:isAdded(line) -- Final validation, before adding.358end359360-- Returns TRUE if this system call was succesfully added. There's two entry361-- points to this function: (1) the entry in syscalls.master is one-line, or362-- (2) the entry is a full system call. This function handles those cases and363-- decides whether to exit early for (1) or validate a full system call for364-- (2). This function also handles cases where we don't want to add, and365-- instead want to abort.366function syscall:isAdded(line)367-- This system call is a range - exit early.368if tonumber(self.num) == nil then369-- The only allowed types are RESERVED and UNIMPL.370if not (self.type.RESERVED or self.type.UNIMPL) then371util.abort(1, "Range only allowed with RESERVED " ..372"and UNIMPL: " .. line)373end374self:finalize()375return true376-- This system call is a loadable system call - exit early.377elseif self.altname ~= nil and self.alttag ~= nil and378self.rettype ~= nil then379self:finalize()380return true381-- This system call is only one line, and should only be one line382-- (we didn't make it to addFunc()) - exit early.383elseif self.name ~= "{" and self.ret == nil then384self:finalize()385return true386-- This is a full system call and we've passed multiple states to387-- get here - final exit.388elseif self.expect_rbrace then389if not line:match("}$") then390util.abort(1, "Expected '}' found '" .. line ..391"' instead.")392end393self:finalize()394return true395end396return false397end398399-- Return TRUE if this system call is native.400function syscall:native()401return self:compatLevel() == native402end403404-- Make a shallow copy of `self` and replace the system call number with num405-- (which should be a number).406-- For system call ranges.407function syscall:shallowCopy(num)408local obj = syscall:new()409410-- shallow copy411for k, v in pairs(self) do412obj[k] = v413end414obj.num = num -- except override range415return obj416end417418-- Make a deep copy of the parameter object. Save copied tables in `copies`,419-- indexed by original table.420-- CREDIT: http://lua-users.org/wiki/CopyTable421-- For a full system call (the nested arguments table should be a deep copy).422local function deepCopy(orig, copies)423copies = copies or {}424local orig_type = type(orig)425local copy426if orig_type == 'table' then427if copies[orig] then428copy = copies[orig]429else430copy = {}431copies[orig] = copy432for orig_key, orig_value in next, orig, nil do433copy[deepCopy(orig_key, copies)] =434deepCopy(orig_value, copies)435end436setmetatable(copy, deepCopy(getmetatable(orig), copies))437end438else -- number, string, boolean, etc439copy = orig440end441return copy442end443444--445-- In syscalls.master, system calls come in two types: (1) a fully defined446-- system call with function declaration, with a distinct number for each system447-- call; or (2) a one-line entry, sometimes with a distinct number and sometimes448-- with a range of numbers. One-line entries can be obsolete, reserved, no449-- definition, etc. Ranges are only allowed for reserved and unimplemented.450--451-- This function provides the iterator to traverse system calls by number. If452-- the entry is a fully defined system call with a distinct number, the iterator453-- creates a deep copy and captures any nested objects; if the entry is a range454-- of numbers, the iterator creates shallow copies from the start of the range455-- to the end of the range.456--457function syscall:iter()458local s = tonumber(self.num)459local e460if s == nil then461s, e = string.match(self.num, "(%d+)%-(%d+)")462s, e = tonumber(s), tonumber(e)463return function ()464if s <= e then465s = s + 1466return self:shallowCopy(s - 1)467end468end469else470e = s471self.num = s -- Replace string with number, like the clones.472return function ()473if s == e then474local deep_copy = deepCopy(self)475s = e + 1476return deep_copy477end478end479end480end481482function syscall:new(obj)483obj = obj or { }484setmetatable(obj, self)485self.__index = self486487self.expect_rbrace = false488self.changes_abi = false489self.args = {}490self.noproto = false491492return obj493end494495return syscall496497498