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