1-- 2-- SPDX-License-Identifier: BSD-2-Clause 3-- 4-- Copyright (c) 2021-2024 SRI International 5-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org> 6-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com> 7-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 8-- 9 10-- 11-- Code to read in the config file that drives this. Since we inherit from the 12-- FreeBSD makesyscall.sh legacy, all config is done through a config file that 13-- sets a number of variables (as noted below); it used to be a .sh file that 14-- was sourced in. This dodges the need to write a command line parser. 15-- 16 17local util = require("tools.util") 18 19-- 20-- Global config map. 21-- Default configuration is native. Any of these may get replaced by an 22-- optionally specified configuration file. 23-- 24local config = { 25 sysnames = "syscalls.c", 26 syshdr = "../sys/syscall.h", 27 syshdr_extra = nil; 28 sysmk = "/dev/null", 29 syssw = "init_sysent.c", 30 systrace = "systrace_args.c", 31 sysproto = "../sys/sysproto.h", 32 libsysmap = "/dev/null", 33 libsys_h = "/dev/null", 34 sysproto_h = "_SYS_SYSPROTO_H_", 35 syscallprefix = "SYS_", 36 switchname = "sysent", 37 namesname = "syscallnames", 38 abi_flags = {}, 39 abi_func_prefix = "", 40 abi_type_suffix = "", 41 abi_long = "long", 42 abi_u_long = "u_long", 43 abi_semid_t = "semid_t", 44 abi_size_t = "size_t", 45 abi_ptr_array_t = "", 46 abi_headers = "", 47 abi_intptr_t = "intptr_t", 48 ptr_intptr_t_cast = "intptr_t", 49 obsol = {}, 50 unimpl = {}, 51 compat_set = "native", 52 mincompat = 0, 53 -- System calls that require ABI-specific handling. 54 syscall_abi_change = {}, 55 -- System calls that appear to require handling, but don't. 56 syscall_no_abi_change = {}, 57 -- Keep track of modifications if there are. 58 modifications = {}, 59 -- Stores compat_sets from syscalls.conf; config.mergeCompat() 60 -- instantiates. 61 compat_options = {}, 62} 63 64-- 65-- For each entry, the ABI flag is the key. One may also optionally provide an 66-- expr, which are contained in an array associated with each key; expr gets 67-- applied to each argument type to indicate whether this argument is subject to 68-- ABI change given the configured flags. 69-- 70config.known_abi_flags = { 71 long_size = { 72 "_Contains[a-z_]*_long_", 73 "^long [a-z0-9_]+$", 74 "long [*]", 75 "size_t [*]", 76 -- semid_t is not included because it is only used 77 -- as an argument or written out individually and 78 -- said writes are handled by the ksem framework. 79 -- Technically a sign-extension issue exists for 80 -- arguments, but because semid_t is actually a file 81 -- descriptor negative 32-bit values are invalid 82 -- regardless of sign-extension. 83 }, 84 time_t_size = { 85 "_Contains[a-z_]*_timet_", 86 }, 87 pointer_args = { 88 -- no expr 89 }, 90 pointer_size = { 91 "_Contains[a-z_]*_ptr_", 92 "[*][*]", 93 }, 94 pair_64bit = { 95 "^dev_t[ ]*$", 96 "^id_t[ ]*$", 97 "^off_t[ ]*$", 98 }, 99} 100 101-- All compat option entries should have five entries: 102-- definition: The preprocessor macro that will be set for this. 103-- compatlevel: The level this compatibility should be included at. This 104-- generally represents the version of FreeBSD that it is compatible 105-- with, but ultimately it's just the level of mincompat in which it's 106-- included. 107-- flag: The name of the flag in syscalls.master. 108-- prefix: The prefix to use for _args and syscall prototype. This will be 109-- used as-is, without "_" or any other character appended. 110-- descr: The description of this compat option in init_sysent.c comments. 111-- The special "stdcompat" entry will cause the other five to be autogenerated. 112local compat_option_sets = { 113 native = { 114 { 115 definition = "COMPAT_43", 116 compatlevel = 3, 117 flag = "COMPAT", 118 prefix = "o", 119 descr = "old", 120 }, 121 { stdcompat = "FREEBSD4" }, 122 { stdcompat = "FREEBSD6" }, 123 { stdcompat = "FREEBSD7" }, 124 { stdcompat = "FREEBSD10" }, 125 { stdcompat = "FREEBSD11" }, 126 { stdcompat = "FREEBSD12" }, 127 { stdcompat = "FREEBSD13" }, 128 { stdcompat = "FREEBSD14" }, 129 }, 130} 131 132-- 133-- config looks like a shell script; in fact, the previous makesyscalls.sh 134-- script actually sourced it in. It had a pretty common format, so we should 135-- be fine to make various assumptions. 136-- 137-- This function processes config to be merged into our global config map with 138-- config.merge(). It aborts if there's malformed lines and returns NIL and a 139-- message if no file was provided. 140-- 141function config.process(file) 142 local cfg = {} 143 local comment_line_expr = "^%s*#.*" 144 -- We capture any whitespace padding here so we can easily advance to 145 -- the end of the line as needed to check for any trailing bogus bits. 146 -- Alternatively, we could drop the whitespace and instead try to 147 -- use a pattern to strip out the meaty part of the line, but then we 148 -- would need to sanitize the line for potentially special characters. 149 local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)" 150 151 if not file then 152 return nil, "No file given" 153 end 154 155 local fh = assert(io.open(file)) 156 157 for nextline in fh:lines() do 158 -- Strip any whole-line comments. 159 nextline = nextline:gsub(comment_line_expr, "") 160 -- Parse it into key, value pairs. 161 local key, value = nextline:match(line_expr) 162 if key ~= nil and value ~= nil then 163 local kvp = key .. "=" .. value 164 key = util.trim(key) 165 value = util.trim(value) 166 local delim = value:sub(1,1) 167 if delim == '"' then 168 local trailing_context 169 170 -- Strip off the key/value part. 171 trailing_context = nextline:sub(kvp:len() + 1) 172 -- Strip off any trailing comment. 173 trailing_context = trailing_context:gsub("#.*$", 174 "") 175 -- Strip off leading/trailing whitespace. 176 trailing_context = util.trim(trailing_context) 177 if trailing_context ~= "" then 178 print(trailing_context) 179 util.abort(1, 180 "Malformed line: " .. nextline) 181 end 182 183 value = util.trim(value, delim) 184 else 185 -- Strip off potential comments. 186 value = value:gsub("#.*$", "") 187 -- Strip off any padding whitespace. 188 value = util.trim(value) 189 if value:match("%s") then 190 util.abort(1, 191 "Malformed config line: " .. 192 nextline) 193 end 194 end 195 cfg[key] = value 196 elseif not nextline:match("^%s*$") then 197 -- Make sure format violations don't get overlooked 198 -- here, but ignore blank lines. Comments are already 199 -- stripped above. 200 util.abort(1, "Malformed config line: " .. nextline) 201 end 202 end 203 204 assert(fh:close()) 205 return cfg 206end 207 208-- Merges processed configuration file into the global config map (see above), 209-- or returns NIL and a message if no file was provided. 210function config.merge(fh) 211 if not fh then 212 return nil, "No file given" 213 end 214 215 local res = assert(config.process(fh)) 216 217 for k, v in pairs(res) do 218 if v ~= config[k] then 219 -- Handling of string lists: 220 if k:find("abi_flags") then 221 -- Match for pipe, that's how abi_flags 222 -- is formatted. 223 config[k] = util.setFromString(v, "[^|]+") 224 elseif k:find("syscall_abi_change") or 225 k:find("syscall_no_abi_change") or 226 k:find("obsol") or 227 k:find("unimpl") then 228 -- Match for space, that's how these 229 -- are formatted. 230 config[k] = util.setFromString(v, "[^ ]+") 231 else 232 config[k] = v 233 end 234 -- Construct config modified table as config 235 -- is processed. 236 config.modifications[k] = true 237 else 238 -- config wasn't modified. 239 config.modifications[k] = false 240 end 241 end 242end 243 244-- Returns TRUE if there are ABI changes from native for the provided ABI flag. 245function config.abiChanges(name) 246 if config.known_abi_flags[name] == nil then 247 util.abort(1, "abi_changes: unknown flag: " .. name) 248 end 249 return config.abi_flags[name] ~= nil 250end 251 252-- Instantiates config.compat_options. 253function config.mergeCompat() 254 if config.compat_set ~= "" then 255 if not compat_option_sets[config.compat_set] then 256 util.abort(1, "Undefined compat set: " .. 257 config.compat_set) 258 end 259 260 config.compat_options = compat_option_sets[config.compat_set] 261 end 262end 263 264return config 265