--1-- SPDX-License-Identifier: BSD-2-Clause2--3-- Copyright (c) 2021-2024 SRI International4-- Copyright (c) 2024 Tyler Baxter <[email protected]>5-- Copyright (c) 2023 Warner Losh <[email protected]>6-- Copyright (c) 2019 Kyle Evans <[email protected]>7--89--10-- Code to read in the config file that drives this. Since we inherit from the11-- FreeBSD makesyscall.sh legacy, all config is done through a config file that12-- sets a number of variables (as noted below); it used to be a .sh file that13-- was sourced in. This dodges the need to write a command line parser.14--1516local util = require("tools.util")1718--19-- Global config map.20-- Default configuration is native. Any of these may get replaced by an21-- optionally specified configuration file.22--23local config = {24sysnames = "syscalls.c",25syshdr = "../sys/syscall.h",26syshdr_extra = nil;27sysmk = "/dev/null",28syssw = "init_sysent.c",29systrace = "systrace_args.c",30sysproto = "../sys/sysproto.h",31libsysmap = "/dev/null",32libsys_h = "/dev/null",33sysproto_h = "_SYS_SYSPROTO_H_",34syscallprefix = "SYS_",35switchname = "sysent",36namesname = "syscallnames",37abi_flags = {},38abi_func_prefix = "",39abi_type_suffix = "",40abi_long = "long",41abi_u_long = "u_long",42abi_semid_t = "semid_t",43abi_size_t = "size_t",44abi_ptr_array_t = "",45abi_headers = "",46abi_intptr_t = "intptr_t",47ptr_intptr_t_cast = "intptr_t",48obsol = {},49unimpl = {},50compat_set = "native",51mincompat = 0,52-- System calls that require ABI-specific handling.53syscall_abi_change = {},54-- System calls that appear to require handling, but don't.55syscall_no_abi_change = {},56-- Keep track of modifications if there are.57modifications = {},58-- Stores compat_sets from syscalls.conf; config.mergeCompat()59-- instantiates.60compat_options = {},61}6263--64-- For each entry, the ABI flag is the key. One may also optionally provide an65-- expr, which are contained in an array associated with each key; expr gets66-- applied to each argument type to indicate whether this argument is subject to67-- ABI change given the configured flags.68--69config.known_abi_flags = {70long_size = {71"_Contains[a-z_]*_long_",72"^long [a-z0-9_]+$",73"long [*]",74"size_t [*]",75-- semid_t is not included because it is only used76-- as an argument or written out individually and77-- said writes are handled by the ksem framework.78-- Technically a sign-extension issue exists for79-- arguments, but because semid_t is actually a file80-- descriptor negative 32-bit values are invalid81-- regardless of sign-extension.82},83time_t_size = {84"_Contains[a-z_]*_timet_",85},86pointer_args = {87-- no expr88},89pointer_size = {90"_Contains[a-z_]*_ptr_",91"[*][*]",92},93pair_64bit = {94"^dev_t[ ]*$",95"^id_t[ ]*$",96"^off_t[ ]*$",97},98}99100-- All compat option entries should have five entries:101-- definition: The preprocessor macro that will be set for this.102-- compatlevel: The level this compatibility should be included at. This103-- generally represents the version of FreeBSD that it is compatible104-- with, but ultimately it's just the level of mincompat in which it's105-- included.106-- flag: The name of the flag in syscalls.master.107-- prefix: The prefix to use for _args and syscall prototype. This will be108-- used as-is, without "_" or any other character appended.109-- descr: The description of this compat option in init_sysent.c comments.110-- The special "stdcompat" entry will cause the other five to be autogenerated.111local compat_option_sets = {112native = {113{114definition = "COMPAT_43",115compatlevel = 3,116flag = "COMPAT",117prefix = "o",118descr = "old",119},120{ stdcompat = "FREEBSD4" },121{ stdcompat = "FREEBSD6" },122{ stdcompat = "FREEBSD7" },123{ stdcompat = "FREEBSD10" },124{ stdcompat = "FREEBSD11" },125{ stdcompat = "FREEBSD12" },126{ stdcompat = "FREEBSD13" },127{ stdcompat = "FREEBSD14" },128},129}130131--132-- config looks like a shell script; in fact, the previous makesyscalls.sh133-- script actually sourced it in. It had a pretty common format, so we should134-- be fine to make various assumptions.135--136-- This function processes config to be merged into our global config map with137-- config.merge(). It aborts if there's malformed lines and returns NIL and a138-- message if no file was provided.139--140function config.process(file)141local cfg = {}142local comment_line_expr = "^%s*#.*"143-- We capture any whitespace padding here so we can easily advance to144-- the end of the line as needed to check for any trailing bogus bits.145-- Alternatively, we could drop the whitespace and instead try to146-- use a pattern to strip out the meaty part of the line, but then we147-- would need to sanitize the line for potentially special characters.148local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)"149150if not file then151return nil, "No file given"152end153154local fh = assert(io.open(file))155156for nextline in fh:lines() do157-- Strip any whole-line comments.158nextline = nextline:gsub(comment_line_expr, "")159-- Parse it into key, value pairs.160local key, value = nextline:match(line_expr)161if key ~= nil and value ~= nil then162local kvp = key .. "=" .. value163key = util.trim(key)164value = util.trim(value)165local delim = value:sub(1,1)166if delim == '"' then167local trailing_context168169-- Strip off the key/value part.170trailing_context = nextline:sub(kvp:len() + 1)171-- Strip off any trailing comment.172trailing_context = trailing_context:gsub("#.*$",173"")174-- Strip off leading/trailing whitespace.175trailing_context = util.trim(trailing_context)176if trailing_context ~= "" then177print(trailing_context)178util.abort(1,179"Malformed line: " .. nextline)180end181182value = util.trim(value, delim)183else184-- Strip off potential comments.185value = value:gsub("#.*$", "")186-- Strip off any padding whitespace.187value = util.trim(value)188if value:match("%s") then189util.abort(1,190"Malformed config line: " ..191nextline)192end193end194cfg[key] = value195elseif not nextline:match("^%s*$") then196-- Make sure format violations don't get overlooked197-- here, but ignore blank lines. Comments are already198-- stripped above.199util.abort(1, "Malformed config line: " .. nextline)200end201end202203assert(fh:close())204return cfg205end206207-- Merges processed configuration file into the global config map (see above),208-- or returns NIL and a message if no file was provided.209function config.merge(fh)210if not fh then211return nil, "No file given"212end213214local res = assert(config.process(fh))215216for k, v in pairs(res) do217if v ~= config[k] then218-- Handling of string lists:219if k:find("abi_flags") then220-- Match for pipe, that's how abi_flags221-- is formatted.222config[k] = util.setFromString(v, "[^|]+")223elseif k:find("syscall_abi_change") or224k:find("syscall_no_abi_change") or225k:find("obsol") or226k:find("unimpl") then227-- Match for space, that's how these228-- are formatted.229config[k] = util.setFromString(v, "[^ ]+")230else231config[k] = v232end233-- Construct config modified table as config234-- is processed.235config.modifications[k] = true236else237-- config wasn't modified.238config.modifications[k] = false239end240end241end242243-- Returns TRUE if there are ABI changes from native for the provided ABI flag.244function config.abiChanges(name)245if config.known_abi_flags[name] == nil then246util.abort(1, "abi_changes: unknown flag: " .. name)247end248return config.abi_flags[name] ~= nil249end250251-- Instantiates config.compat_options.252function config.mergeCompat()253if config.compat_set ~= "" then254if not compat_option_sets[config.compat_set] then255util.abort(1, "Undefined compat set: " ..256config.compat_set)257end258259config.compat_options = compat_option_sets[config.compat_set]260end261end262263return config264265266