xref: /freebsd/sys/tools/syscalls/scripts/sysproto_h.lua (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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