1-- 2-- SPDX-License-Identifier: BSD-2-Clause 3-- 4-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org> 5-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com> 6-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 7-- 8 9local config = require("config") 10local scarg = require("core.scarg") 11local scret = require("core.scret") 12local util = require("tools.util") 13 14local syscall = {} 15 16syscall.__index = syscall 17 18syscall.known_flags = util.set { 19 "STD", 20 "OBSOL", 21 "RESERVED", 22 "UNIMPL", 23 "NODEF", 24 "NOARGS", 25 "NOPROTO", 26 "NOSTD", 27 28 -- flags beyond this point are modifiers 29 "CAPENABLED", 30 "NOLIB", 31 "NORETURN", 32 "NOTSTATIC", 33 "SYSMUX", 34} 35 36-- Native is an arbitrarily large number to have a constant and not 37-- interfere with compat numbers. 38local native = 1000000 39 40-- Processes and assigns the appropriate thread flag for this system call. 41function syscall:processThr() 42 self.thr = "SY_THR_STATIC" 43 for k, _ in pairs(self.type) do 44 if k == "NOTSTATIC" then 45 self.thr = "SY_THR_ABSENT" 46 end 47 end 48end 49 50-- Processes and assigns the appropriate capability flag for this system call. 51-- "SYF_CAPENABLED" for capability enabled; "0" for NOT capability enabled. 52function syscall:processCap() 53 self.cap = "0" 54 local stripped = util.stripAbiPrefix(self.name, self.prefix) 55 for k, _ in pairs(self.type) do 56 if k == "CAPENABLED" then 57 self.cap = "SYF_CAPENABLED" 58 end 59 end 60end 61 62-- Check that this system call has a known type. 63local function checkType(type) 64 for k, _ in pairs(type) do 65 if not syscall.known_flags[k] and not 66 k:match("^COMPAT") then 67 util.abort(1, "Bad type: " .. k) 68 end 69 end 70end 71 72-- If there are ABI changes from native, process this system call to match the 73-- target ABI. 74function syscall:processChangesAbi() 75 -- First, confirm we want to uphold our changes_abi flag. 76 if config.syscall_no_abi_change[self.name] then 77 self.changes_abi = false 78 end 79 self.noproto = not util.isEmpty(config.abi_flags) and 80 not self.changes_abi 81 if config.abiChanges("pointer_args") then 82 for _, v in ipairs(self.args) do 83 if util.isPtrType(v.type, config.abi_intptr_t) then 84 if config.syscall_no_abi_change[self.name] then 85 print("WARNING: " .. self.name .. 86 " in syscall_no_abi_change, " .. 87 "but pointers args are present") 88 end 89 self.changes_abi = true 90 goto ptrfound 91 end 92 end 93 ::ptrfound:: 94 end 95 if config.syscall_abi_change[self.name] then 96 self.changes_abi = true 97 end 98 if self.changes_abi then 99 self.noproto = false 100 end 101end 102 103-- Final processing of flags. Process any flags that haven't already been 104-- processed (e.g., dictionaries from syscalls.conf). 105function syscall:processFlags() 106 if config.obsol[self.name] or (self:compatLevel() > 0 and 107 self:compatLevel() < tonumber(config.mincompat)) then 108 self.args = nil 109 self.type.OBSOL = true 110 -- Don't apply any ABI handling, declared as obsolete. 111 self.changes_abi = false 112 end 113 if config.unimpl[self.name] then 114 self.type.UNIMPL = true 115 end 116 if self.noproto or self.type.SYSMUX then 117 self.type.NOPROTO = true 118 end 119 if self.type.NODEF then 120 self.audit = "AUE_NULL" 121 end 122end 123 124-- Returns TRUE if prefix and arg_prefix are assigned; FALSE if they're left 125-- unassigned. Relies on a valid changes_abi flag, so should be called AFTER 126-- processChangesAbi(). 127function syscall:processPrefix() 128 -- If there are ABI changes from native, assign the correct prefixes. 129 if self.changes_abi then 130 self.arg_prefix = config.abi_func_prefix 131 self.prefix = config.abi_func_prefix 132 return true 133 end 134 return false 135end 136 137-- Validate that we're not skipping system calls by comparing this system call 138-- number to the previous system call number. Called higher up the call stack 139-- by class FreeBSDSyscall. 140function syscall:validate(prev) 141 return prev + 1 == self.num 142end 143 144-- Return the compat prefix for this system call. 145function syscall:compatPrefix() 146 local c = self:compatLevel() 147 if self.type.OBSOL then 148 return "obs_" 149 end 150 if self.type.RESERVED then 151 return "reserved #" 152 end 153 if self.type.UNIMPL then 154 return "unimp_" 155 end 156 if c == 3 then 157 return "o" 158 end 159 if c < native then 160 return "freebsd" .. tostring(c) .. "_" 161 end 162 return "" 163end 164 165-- Return the symbol name for this system call. 166function syscall:symbol() 167 return self:compatPrefix() .. self.name 168end 169 170-- 171-- Return the compatibility level for this system call. 172-- 0 is obsolete. 173-- < 0 is this isn't really a system call we care about. 174-- 3 is 4.3BSD in theory, but anything before FreeBSD 4. 175-- >= 4 is FreeBSD version, this system call was replaced with a new 176-- version. 177-- 178function syscall:compatLevel() 179 if self.type.UNIMPL or self.type.RESERVED then 180 return -1 181 elseif self.type.OBSOL then 182 return 0 183 elseif self.type.COMPAT then 184 return 3 185 end 186 for k, _ in pairs(self.type) do 187 local l = k:match("^COMPAT(%d+)") 188 if l ~= nil then 189 return tonumber(l) 190 end 191 end 192 return native 193end 194 195-- Adds the definition for this system call. Guarded by whether we already have 196-- a system call number or not. 197function syscall:addDef(line) 198 if self.num == nil then 199 local words = util.split(line, "%S+") 200 self.num = words[1] 201 self.audit = words[2] 202 self.type = util.setFromString(words[3], "[^|]+") 203 checkType(self.type) 204 self.name = words[4] 205 -- These next three are optional, and either all present 206 -- or all absent. 207 self.altname = words[5] 208 self.alttag = words[6] 209 self.rettype = words[7] 210 return true 211 end 212 return false 213end 214 215-- Adds the function declaration for this system call. If addDef() found an 216-- opening curly brace, then we're looking for a function declaration. 217function syscall:addFunc(line) 218 if self.name == "{" then 219 local words = util.split(line, "%S+") 220 -- Expect line is `type syscall(` or `type syscall(void);`. 221 if #words ~= 2 then 222 util.abort(1, "Malformed line " .. line) 223 end 224 225 local ret = scret:new({}, line) 226 self.ret = ret:add() 227 -- Don't clobber rettype set in the alt information. 228 if self.rettype == nil then 229 self.rettype = "int" 230 end 231 232 self.name = words[2]:match("([%w_]+)%(") 233 if words[2]:match("%);$") then 234 -- Now we're looking for ending curly brace. 235 self.expect_rbrace = true 236 end 237 return true 238 end 239 return false 240end 241 242-- Adds the argument(s) for this system call. Once addFunc() assigns a name 243-- for this system call, arguments are next in syscalls.master. 244function syscall:addArgs(line) 245 if not self.expect_rbrace then 246 if line:match("%);$") then 247 self.expect_rbrace = true 248 return true 249 end 250 local arg = scarg:new({}, line) 251 -- We don't want to add this argument if it doesn't process. 252 -- scarg:process() handles those conditions. 253 if arg:process() then 254 arg:append(self.args) 255 end 256 -- If this argument has ABI changes, set globally for this 257 -- system call. 258 self.changes_abi = self.changes_abi or arg:changesAbi() 259 return true 260 end 261 return false 262end 263 264-- Once we have a good syscall, add some final information to it. 265function syscall:finalize() 266 if self.name == nil then 267 self.name = "" 268 end 269 270 -- Preserve the original name as the alias. 271 self.alias = self.name 272 273 self:processChangesAbi() -- process changes to the ABI 274 self:processFlags() -- process any unprocessed flags 275 276 -- If there's changes to the ABI, these prefixes will be changed by 277 -- processPrefix(); otherwise, they'll remain empty. 278 self.prefix = "" 279 self.arg_prefix = "" 280 self:processPrefix() 281 282 self:processCap() -- capability flag 283 self:processThr() -- thread flag 284 285 -- Assign argument alias. 286 if self.alttag ~= nil then 287 self.arg_alias = self.alttag 288 elseif self.arg_alias == nil and self.name ~= nil then 289 -- argalias should be: 290 -- COMPAT_PREFIX + ABI Prefix + funcname 291 self.arg_alias = self:compatPrefix() .. self.arg_prefix .. 292 self.name .. "_args" 293 elseif self.arg_alias ~= nil then 294 self.arg_alias = self.arg_prefix .. self.arg_alias 295 end 296 297 -- An empty string would not want a prefix; the entry doesn't have 298 -- a name so we want to keep the empty string. 299 if self.name ~= nil and self.name ~= "" then 300 self.name = self.prefix .. self.name 301 end 302 303 self:processArgstrings() 304 self:processArgsize() 305end 306 307-- Assigns the correct args_size. Defaults to "0", except if there's arguments 308-- or NODEF flag. 309function syscall:processArgsize() 310 if self.type.SYSMUX then -- catch this first 311 self.args_size = "0" 312 elseif self.arg_alias ~= nil and 313 (#self.args ~= 0 or self.type.NODEF) then 314 self.args_size = "AS(" .. self.arg_alias .. ")" 315 else 316 self.args_size = "0" 317 end 318end 319 320-- Constructs argstr_* strings for generated declerations/wrappers. 321function syscall:processArgstrings() 322 local type = "" 323 local type_var = "" 324 local var = "" 325 local comma = "" 326 327 for _, v in ipairs(self.args) do 328 local argname, argtype = v.name, v.type 329 type = type .. comma .. argtype 330 type_var = type_var .. comma .. argtype .. " " .. argname 331 var = var .. comma .. argname 332 comma = ", " 333 end 334 if type == "" then 335 type = "void" 336 type_var = "void" 337 end 338 339 self.argstr_type = type 340 self.argstr_type_var = type_var 341 self.argstr_var = var 342end 343 344-- Interface to add this system call to the master system call table. 345-- The system call is built up one line at a time. The states describe the 346-- current parsing state. 347-- Returns TRUE when ready to add and FALSE while still parsing. 348function syscall:add(line) 349 if self:addDef(line) then 350 return self:isAdded(line) 351 end 352 if self:addFunc(line) then 353 return false -- Function added; keep going. 354 end 355 if self:addArgs(line) then 356 return false -- Arguments added; keep going. 357 end 358 return self:isAdded(line) -- Final validation, before adding. 359end 360 361-- Returns TRUE if this system call was succesfully added. There's two entry 362-- points to this function: (1) the entry in syscalls.master is one-line, or 363-- (2) the entry is a full system call. This function handles those cases and 364-- decides whether to exit early for (1) or validate a full system call for 365-- (2). This function also handles cases where we don't want to add, and 366-- instead want to abort. 367function syscall:isAdded(line) 368 -- This system call is a range - exit early. 369 if tonumber(self.num) == nil then 370 -- The only allowed types are RESERVED and UNIMPL. 371 if not (self.type.RESERVED or self.type.UNIMPL) then 372 util.abort(1, "Range only allowed with RESERVED " .. 373 "and UNIMPL: " .. line) 374 end 375 self:finalize() 376 return true 377 -- This system call is a loadable system call - exit early. 378 elseif self.altname ~= nil and self.alttag ~= nil and 379 self.rettype ~= nil then 380 self:finalize() 381 return true 382 -- This system call is only one line, and should only be one line 383 -- (we didn't make it to addFunc()) - exit early. 384 elseif self.name ~= "{" and self.ret == nil then 385 self:finalize() 386 return true 387 -- This is a full system call and we've passed multiple states to 388 -- get here - final exit. 389 elseif self.expect_rbrace then 390 if not line:match("}$") then 391 util.abort(1, "Expected '}' found '" .. line .. 392 "' instead.") 393 end 394 self:finalize() 395 return true 396 end 397 return false 398end 399 400-- Return TRUE if this system call is native. 401function syscall:native() 402 return self:compatLevel() == native 403end 404 405-- Make a shallow copy of `self` and replace the system call number with num 406-- (which should be a number). 407-- For system call ranges. 408function syscall:shallowCopy(num) 409 local obj = syscall:new() 410 411 -- shallow copy 412 for k, v in pairs(self) do 413 obj[k] = v 414 end 415 obj.num = num -- except override range 416 return obj 417end 418 419-- Make a deep copy of the parameter object. Save copied tables in `copies`, 420-- indexed by original table. 421-- CREDIT: http://lua-users.org/wiki/CopyTable 422-- For a full system call (the nested arguments table should be a deep copy). 423local function deepCopy(orig, copies) 424 copies = copies or {} 425 local orig_type = type(orig) 426 local copy 427 if orig_type == 'table' then 428 if copies[orig] then 429 copy = copies[orig] 430 else 431 copy = {} 432 copies[orig] = copy 433 for orig_key, orig_value in next, orig, nil do 434 copy[deepCopy(orig_key, copies)] = 435 deepCopy(orig_value, copies) 436 end 437 setmetatable(copy, deepCopy(getmetatable(orig), copies)) 438 end 439 else -- number, string, boolean, etc 440 copy = orig 441 end 442 return copy 443end 444 445-- 446-- In syscalls.master, system calls come in two types: (1) a fully defined 447-- system call with function declaration, with a distinct number for each system 448-- call; or (2) a one-line entry, sometimes with a distinct number and sometimes 449-- with a range of numbers. One-line entries can be obsolete, reserved, no 450-- definition, etc. Ranges are only allowed for reserved and unimplemented. 451-- 452-- This function provides the iterator to traverse system calls by number. If 453-- the entry is a fully defined system call with a distinct number, the iterator 454-- creates a deep copy and captures any nested objects; if the entry is a range 455-- of numbers, the iterator creates shallow copies from the start of the range 456-- to the end of the range. 457-- 458function syscall:iter() 459 local s = tonumber(self.num) 460 local e 461 if s == nil then 462 s, e = string.match(self.num, "(%d+)%-(%d+)") 463 s, e = tonumber(s), tonumber(e) 464 return function () 465 if s <= e then 466 s = s + 1 467 return self:shallowCopy(s - 1) 468 end 469 end 470 else 471 e = s 472 self.num = s -- Replace string with number, like the clones. 473 return function () 474 if s == e then 475 local deep_copy = deepCopy(self) 476 s = e + 1 477 return deep_copy 478 end 479 end 480 end 481end 482 483function syscall:new(obj) 484 obj = obj or { } 485 setmetatable(obj, self) 486 self.__index = self 487 488 self.expect_rbrace = false 489 self.changes_abi = false 490 self.args = {} 491 self.noproto = false 492 493 return obj 494end 495 496return syscall 497