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 -- Handle non-compat: 97 if v:native() then 98 -- All these negation conditions are because (in 99 -- general) these are cases where code for sysproto.h 100 -- is not generated. 101 if not v.type.NOARGS and not v.type.NOPROTO and 102 not v.type.NODEF then 103 if #v.args > 0 then 104 gen:write(string.format( 105 "struct %s {\n", v.arg_alias)) 106 for _, arg in ipairs(v.args) do 107 if arg.type == "int" and 108 arg.name == "_pad" and 109 config.abiChanges( 110 "pair_64bit") then 111 gen:write("#ifdef PAD64_REQUIRED\n") 112 end 113 114 gen:write(string.format([[ 115 char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)]; 116]], 117 arg.name, arg.type, 118 arg.type, arg.name, 119 arg.name, arg.type)) 120 121 if arg.type == "int" and 122 arg.name == "_pad" and 123 config.abiChanges( 124 "pair_64bit") then 125 gen:write("#endif\n") 126 end 127 end 128 gen:write("};\n") 129 else 130 gen:write(string.format( 131 "struct %s {\n\tsyscallarg_t dummy;\n};\n", 132 v.arg_alias)) 133 end 134 end 135 if not v.type.NOPROTO and not v.type.NODEF then 136 local sys_prefix = "sys_" 137 if v.name == "nosys" or v.name == "lkmnosys" or 138 v.name == "sysarch" or 139 v.name:find("^freebsd") or 140 v.name:find("^linux") then 141 sys_prefix = "" 142 end 143 gen:store(string.format( 144 "%s\t%s%s(struct thread *, struct %s *);\n", 145 v.rettype, sys_prefix, v.name, v.arg_alias), 146 1) 147 gen:store(string.format( 148 "#define\t%sAUE_%s\t%s\n", 149 config.syscallprefix, v:symbol(), v.audit), 150 audit_idx) 151 end 152 153 -- Handle compat (everything >= FREEBSD3): 154 elseif c >= 3 then 155 local idx = c * 10 156 if not v.type.NOARGS and not v.type.NOPROTO and 157 not v.type.NODEF then 158 if #v.args > 0 then 159 gen:store(string.format( 160 "struct %s {\n", v.arg_alias), idx) 161 for _, arg in ipairs(v.args) do 162 gen:store(string.format([[ 163 char %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)]; 164]], 165 arg.name, arg.type, 166 arg.type, arg.name, 167 arg.name, arg.type), idx) 168 end 169 gen:store("};\n", idx) 170 else 171 -- Not stored, written on the first run. 172 gen:write(string.format([[ 173struct %s { 174 syscallarg_t dummy; 175}; 176]], 177 v.arg_alias)) 178 end 179 end 180 if not v.type.NOPROTO and not v.type.NODEF then 181 gen:store(string.format([[ 182%s %s%s(struct thread *, struct %s *); 183]], 184 v.rettype, v:compatPrefix(), v.name, 185 v.arg_alias), idx + 1) 186 gen:store(string.format([[ 187#define %sAUE_%s%s %s 188]], 189 config.syscallprefix, v:compatPrefix(), 190 v.name, v.audit), audit_idx) 191 end 192 end 193 -- Do nothing for obsolete, unimplemented, and reserved. 194 end 195 196 -- Append #endif to the end of each compat option. 197 for _, v in pairs(config.compat_options) do 198 -- Based on how they're indexed, 9 is the last index. 199 local end_idx = (v.compatlevel * 10) + 9 200 -- Need an extra newline after #endif. 201 gen:store(string.format("\n#endif /* %s */\n\n", v.definition), 202 end_idx) 203 end 204 205 if gen.storage_levels ~= nil then 206 gen:writeStorage() 207 end 208 209 -- After storage has been unrolled, tag on the ending bits. 210 gen:write(string.format([[ 211 212#undef PAD_ 213#undef PADL_ 214#undef PADR_ 215 216#endif /* !%s */ 217]], config.sysproto_h)) 218end 219 220-- Entry of script: 221if script then 222 local config = require("config") 223 224 if #arg < 1 or #arg > 2 then 225 error("usage: " .. arg[0] .. " syscall.master") 226 end 227 228 local sysfile, configfile = arg[1], arg[2] 229 230 config.merge(configfile) 231 config.mergeCompat() 232 233 -- The parsed system call table. 234 local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config} 235 236 sysproto_h.file = config.sysproto -- change file here 237 sysproto_h.generate(tbl, config, sysproto_h.file) 238end 239 240-- Return the module. 241return sysproto_h 242