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