1#!/usr/libexec/flua 2-- 3-- SPDX-License-Identifier: BSD-2-Clause 4-- 5-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org> 6-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org> 7-- 8 9-- Setup to be a module, or ran as its own script. 10local sysproto_h = {} 11local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script. 12if script then 13 -- Add library root to the package path. 14 local path = arg[0]:gsub("/[^/]+.lua$", "") 15 package.path = package.path .. ";" .. path .. "/../?.lua" 16end 17 18local FreeBSDSyscall = require("core.freebsd-syscall") 19local generator = require("tools.generator") 20 21-- File has not been decided yet; config will decide file. Default defined as 22-- /dev/null. 23sysproto_h.file = "/dev/null" 24 25function sysproto_h.generate(tbl, config, fh) 26 -- Grab the master system calls table. 27 local s = tbl.syscalls 28 29 -- Bind the generator to the parameter file. 30 local gen = generator:new({}, fh) 31 gen.storage_levels = {} -- make sure storage is clear 32 33 -- Write the generated preamble. 34 gen:preamble("System call prototypes.") 35 36 -- Write out all the preprocessor directives. 37 gen:write(string.format([[ 38#ifndef %s 39#define %s 40 41#include <sys/types.h> 42#include <sys/signal.h> 43#include <sys/cpuset.h> 44#include <sys/domainset.h> 45#include <sys/_ffcounter.h> 46#include <sys/_semaphore.h> 47#include <sys/ucontext.h> 48#include <sys/wait.h> 49 50#include <bsm/audit_kevents.h> 51 52struct proc; 53 54struct thread; 55 56#define PAD_(t) (sizeof(syscallarg_t) <= sizeof(t) ? \ 57 0 : sizeof(syscallarg_t) - sizeof(t)) 58 59#if BYTE_ORDER == LITTLE_ENDIAN 60#define PADL_(t) 0 61#define PADR_(t) PAD_(t) 62#else 63#define PADL_(t) PAD_(t) 64#define PADR_(t) 0 65#endif 66 67]], config.sysproto_h, config.sysproto_h)) 68 69 -- 64-bit padding preprocessor directive. 70 gen:pad64(config.abiChanges("pair_64bit")) 71 72 -- 73 -- Storing each compat entry requires storing multiple levels of file 74 -- generation; compat entries are given ranges of 10 instead to cope 75 -- with this. For example, 13 is indexed as 130; 131 is the second 76 -- storage level of 13. 77 -- 78 79 -- Store all the compat #ifdef from compat_options at their zero index. 80 for _, v in pairs(config.compat_options) do 81 -- Tag an extra newline to the end, so it doesn't have to be 82 -- worried about later. 83 gen:store(string.format("\n#ifdef %s\n\n", v.definition), 84 v.compatlevel * 10) 85 end 86 87 for _, v in pairs(s) do 88 local c = v:compatLevel() 89 90 -- Audit defines are stored at an arbitrarily large number so 91 -- that they're always at the last storage level, and compat 92 -- entries can be indexed by their compat level (more 93 -- intuitive). 94 local audit_idx = 10000 -- this should do 95 96 gen:write(v.prolog) 97 gen:store(v.prolog, 1) 98 for _, w in pairs(config.compat_options) do 99 gen:store(v.prolog, w.compatlevel * 10) 100 end 101 102 -- Handle non-compat: 103 if v:native() then 104 -- All these negation conditions are because (in 105 -- general) these are cases where code for sysproto.h 106 -- is not generated. 107 if not v.type.NOARGS and not v.type.NOPROTO and 108 not v.type.NODEF then 109 if #v.args > 0 then 110 gen:write(string.format( 111 "struct %s {\n", v.arg_alias)) 112 for _, arg in ipairs(v.args) do 113 if arg.type == "int" and 114 arg.name == "_pad" and 115 config.abiChanges( 116 "pair_64bit") then 117 gen:write("#ifdef PAD64_REQUIRED\n") 118 end 119 120 gen:write(string.format([[ 121 char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)]; 122]], 123 arg.name, arg.type, 124 arg.type, arg.name, 125 arg.name, arg.type)) 126 127 if arg.type == "int" and 128 arg.name == "_pad" and 129 config.abiChanges( 130 "pair_64bit") then 131 gen:write("#endif\n") 132 end 133 end 134 gen:write("};\n") 135 else 136 gen:write(string.format( 137 "struct %s {\n\tsyscallarg_t dummy;\n};\n", 138 v.arg_alias)) 139 end 140 end 141 if not v.type.NOPROTO and not v.type.NODEF then 142 local sys_prefix = "sys_" 143 if v.name == "nosys" or v.name == "lkmnosys" or 144 v.name == "sysarch" or 145 v.name:find("^freebsd") or 146 v.name:find("^linux") then 147 sys_prefix = "" 148 end 149 gen:store(string.format( 150 "%s\t%s%s(struct thread *, struct %s *);\n", 151 v.rettype, sys_prefix, v.name, v.arg_alias), 152 1) 153 gen:store(string.format( 154 "#define\t%sAUE_%s\t%s\n", 155 config.syscallprefix, v:symbol(), v.audit), 156 audit_idx) 157 end 158 159 -- Handle compat (everything >= FREEBSD3): 160 elseif c >= 3 then 161 local idx = c * 10 162 if not v.type.NOARGS and not v.type.NOPROTO and 163 not v.type.NODEF then 164 if #v.args > 0 then 165 gen:store(string.format( 166 "struct %s {\n", v.arg_alias), idx) 167 for _, arg in ipairs(v.args) do 168 gen:store(string.format([[ 169 char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)]; 170]], 171 arg.name, arg.type, 172 arg.type, arg.name, 173 arg.name, arg.type), idx) 174 end 175 gen:store("};\n", idx) 176 else 177 -- Not stored, written on the first run. 178 gen:write(string.format([[ 179struct %s { 180 syscallarg_t dummy; 181}; 182]], 183 v.arg_alias)) 184 end 185 end 186 if not v.type.NOPROTO and not v.type.NODEF then 187 gen:store(string.format([[ 188%s %s%s(struct thread *, struct %s *); 189]], 190 v.rettype, v:compatPrefix(), v.name, 191 v.arg_alias), idx + 1) 192 gen:store(string.format([[ 193#define %sAUE_%s%s %s 194]], 195 config.syscallprefix, v:compatPrefix(), 196 v.name, v.audit), audit_idx) 197 end 198 end 199 -- Do nothing for obsolete, unimplemented, and reserved. 200 end 201 202 -- Append #endif to the end of each compat option. 203 for _, v in pairs(config.compat_options) do 204 -- Based on how they're indexed, 9 is the last index. 205 local end_idx = (v.compatlevel * 10) + 9 206 -- Need an extra newline after #endif. 207 gen:store(string.format("\n#endif /* %s */\n\n", v.definition), 208 end_idx) 209 end 210 211 gen:write(tbl.epilog) 212 gen:store(tbl.epilog, 1) 213 for _, w in pairs(config.compat_options) do 214 gen:store(tbl.epilog, w.compatlevel * 10) 215 end 216 217 if gen.storage_levels ~= nil then 218 gen:writeStorage() 219 end 220 221 -- After storage has been unrolled, tag on the ending bits. 222 gen:write(string.format([[ 223 224#undef PAD_ 225#undef PADL_ 226#undef PADR_ 227 228#endif /* !%s */ 229]], config.sysproto_h)) 230end 231 232-- Entry of script: 233if script then 234 local config = require("config") 235 236 if #arg < 1 or #arg > 2 then 237 error("usage: " .. arg[0] .. " syscall.master") 238 end 239 240 local sysfile, configfile = arg[1], arg[2] 241 242 config.merge(configfile) 243 config.mergeCompat() 244 245 -- The parsed system call table. 246 local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config} 247 248 sysproto_h.file = config.sysproto -- change file here 249 sysproto_h.generate(tbl, config, sysproto_h.file) 250end 251 252-- Return the module. 253return sysproto_h 254