xref: /freebsd-14.2/sys/tools/makesyscalls.lua (revision df38ada2)
1--
2-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3--
4-- Copyright (c) 2019 Kyle Evans <[email protected]>
5--
6-- Redistribution and use in source and binary forms, with or without
7-- modification, are permitted provided that the following conditions
8-- are met:
9-- 1. Redistributions of source code must retain the above copyright
10--    notice, this list of conditions and the following disclaimer.
11-- 2. Redistributions in binary form must reproduce the above copyright
12--    notice, this list of conditions and the following disclaimer in the
13--    documentation and/or other materials provided with the distribution.
14--
15-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25-- SUCH DAMAGE.
26--
27-- $FreeBSD$
28--
29
30
31-- We generally assume that this script will be run by flua, however we've
32-- carefully crafted modules for it that mimic interfaces provided by modules
33-- available in ports.  Currently, this script is compatible with lua from ports
34-- along with the compatible luafilesystem and lua-posix modules.
35local lfs = require("lfs")
36local unistd = require("posix.unistd")
37
38local savesyscall = -1
39local maxsyscall = -1
40local generated_tag = "@" .. "generated"
41
42-- Default configuration; any of these may get replaced by a configuration file
43-- optionally specified.
44local config = {
45	os_id_keyword = "FreeBSD",
46	abi_func_prefix = "",
47	sysnames = "syscalls.c",
48	sysproto = "../sys/sysproto.h",
49	sysproto_h = "_SYS_SYSPROTO_H_",
50	syshdr = "../sys/syscall.h",
51	sysmk = "../sys/syscall.mk",
52	syssw = "init_sysent.c",
53	syscallprefix = "SYS_",
54	switchname = "sysent",
55	namesname = "syscallnames",
56	systrace = "systrace_args.c",
57	capabilities_conf = "capabilities.conf",
58	capenabled = {},
59	mincompat = 0,
60	abi_type_suffix = "",
61	abi_flags = "",
62	abi_flags_mask = 0,
63	abi_headers = "",
64	abi_intptr_t = "intptr_t",
65	abi_size_t = "size_t",
66	abi_u_long = "u_long",
67	abi_long = "long",
68	abi_semid_t = "semid_t",
69	abi_ptr_array_t = "",
70	ptr_intptr_t_cast = "intptr_t",
71	syscall_abi_change = "",
72	sys_abi_change = {},
73	syscall_no_abi_change = "",
74	sys_no_abi_change = {},
75	obsol = "",
76	obsol_dict = {},
77	unimpl = "",
78	unimpl_dict = {},
79}
80
81local config_modified = {}
82local cleantmp = true
83local tmpspace = "/tmp/sysent." .. unistd.getpid() .. "/"
84
85local output_files = {
86	"sysnames",
87	"syshdr",
88	"sysmk",
89	"syssw",
90	"systrace",
91	"sysproto",
92}
93
94-- These ones we'll create temporary files for; generation purposes.
95local temp_files = {
96	"sysaue",
97	"sysdcl",
98	"syscompat",
99	"syscompatdcl",
100	"sysent",
101	"sysinc",
102	"sysarg",
103	"sysprotoend",
104	"systracetmp",
105	"systraceret",
106}
107
108-- Opened files
109local files = {}
110
111local function cleanup()
112	for _, v in pairs(files) do
113		assert(v:close())
114	end
115	if cleantmp then
116		if lfs.dir(tmpspace) then
117			for fname in lfs.dir(tmpspace) do
118				if fname ~= "." and fname ~= ".." then
119					assert(os.remove(tmpspace .. "/" ..
120					    fname))
121				end
122			end
123		end
124
125		if lfs.attributes(tmpspace) and not lfs.rmdir(tmpspace) then
126			assert(io.stderr:write("Failed to clean up tmpdir: " ..
127			    tmpspace .. "\n"))
128		end
129	else
130		assert(io.stderr:write("Temp files left in " .. tmpspace ..
131		    "\n"))
132	end
133end
134
135local function abort(status, msg)
136	assert(io.stderr:write(msg .. "\n"))
137	cleanup()
138	os.exit(status)
139end
140
141-- Each entry should have a value so we can represent abi flags as a bitmask
142-- for convenience.  One may also optionally provide an expr; this gets applied
143-- to each argument type to indicate whether this argument is subject to ABI
144-- change given the configured flags.
145local known_abi_flags = {
146	long_size = {
147		value	= 0x00000001,
148		exprs	= {
149			"_Contains[a-z_]*_long_",
150			"^long [a-z0-9_]+$",
151			"long [*]",
152			"size_t [*]",
153			-- semid_t is not included because it is only used
154			-- as an argument or written out individually and
155			-- said writes are handled by the ksem framework.
156			-- Technically a sign-extension issue exists for
157			-- arguments, but because semid_t is actually a file
158			-- descriptor negative 32-bit values are invalid
159			-- regardless of sign-extension.
160		},
161	},
162	time_t_size = {
163		value	= 0x00000002,
164		exprs	= {
165			"_Contains[a-z_]*_timet_",
166		},
167	},
168	pointer_args = {
169		value	= 0x00000004,
170	},
171	pointer_size = {
172		value	= 0x00000008,
173		exprs	= {
174			"_Contains[a-z_]*_ptr_",
175			"[*][*]",
176		},
177	},
178	pair_64bit = {
179		value	= 0x00000010,
180		exprs	= {
181			"^dev_t[ ]*$",
182			"^id_t[ ]*$",
183			"^off_t[ ]*$",
184		},
185	},
186}
187
188local known_flags = {
189	STD		= 0x00000001,
190	OBSOL		= 0x00000002,
191	RESERVED	= 0x00000004,
192	UNIMPL		= 0x00000008,
193	NODEF		= 0x00000010,
194	NOARGS		= 0x00000020,
195	NOPROTO		= 0x00000040,
196	NOSTD		= 0x00000080,
197	NOTSTATIC	= 0x00000100,
198	CAPENABLED	= 0x00000200,
199	SYSMUX		= 0x00000400,
200
201	-- Compat flags start from here.  We have plenty of space.
202}
203
204-- All compat_options entries should have five entries:
205--	definition: The preprocessor macro that will be set for this
206--	compatlevel: The level this compatibility should be included at.  This
207--	    generally represents the version of FreeBSD that it is compatible
208--	    with, but ultimately it's just the level of mincompat in which it's
209--	    included.
210--	flag: The name of the flag in syscalls.master.
211--	prefix: The prefix to use for _args and syscall prototype.  This will be
212--	    used as-is, without "_" or any other character appended.
213--	descr: The description of this compat option in init_sysent.c comments.
214-- The special "stdcompat" entry will cause the other five to be autogenerated.
215local compat_options = {
216	{
217		definition = "COMPAT_43",
218		compatlevel = 3,
219		flag = "COMPAT",
220		prefix = "o",
221		descr = "old",
222	},
223	{ stdcompat = "FREEBSD4" },
224	{ stdcompat = "FREEBSD6" },
225	{ stdcompat = "FREEBSD7" },
226	{ stdcompat = "FREEBSD10" },
227	{ stdcompat = "FREEBSD11" },
228	{ stdcompat = "FREEBSD12" },
229	{ stdcompat = "FREEBSD13" },
230}
231
232local function trim(s, char)
233	if s == nil then
234		return nil
235	end
236	if char == nil then
237		char = "%s"
238	end
239	return s:gsub("^" .. char .. "+", ""):gsub(char .. "+$", "")
240end
241
242-- config looks like a shell script; in fact, the previous makesyscalls.sh
243-- script actually sourced it in.  It had a pretty common format, so we should
244-- be fine to make various assumptions
245local function process_config(file)
246	local cfg = {}
247	local comment_line_expr = "^%s*#.*"
248	-- We capture any whitespace padding here so we can easily advance to
249	-- the end of the line as needed to check for any trailing bogus bits.
250	-- Alternatively, we could drop the whitespace and instead try to
251	-- use a pattern to strip out the meaty part of the line, but then we
252	-- would need to sanitize the line for potentially special characters.
253	local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]+[`\"]?)"
254
255	if not file then
256		return nil, "No file given"
257	end
258
259	local fh = assert(io.open(file))
260
261	for nextline in fh:lines() do
262		-- Strip any whole-line comments
263		nextline = nextline:gsub(comment_line_expr, "")
264		-- Parse it into key, value pairs
265		local key, value = nextline:match(line_expr)
266		if key ~= nil and value ~= nil then
267			local kvp = key .. "=" .. value
268			key = trim(key)
269			value = trim(value)
270			local delim = value:sub(1,1)
271			if delim == '"' then
272				local trailing_context
273
274				-- Strip off the key/value part
275				trailing_context = nextline:sub(kvp:len() + 1)
276				-- Strip off any trailing comment
277				trailing_context = trailing_context:gsub("#.*$",
278				    "")
279				-- Strip off leading/trailing whitespace
280				trailing_context = trim(trailing_context)
281				if trailing_context ~= "" then
282					print(trailing_context)
283					abort(1, "Malformed line: " .. nextline)
284				end
285
286				value = trim(value, delim)
287			else
288				-- Strip off potential comments
289				value = value:gsub("#.*$", "")
290				-- Strip off any padding whitespace
291				value = trim(value)
292				if value:match("%s") then
293					abort(1, "Malformed config line: " ..
294					    nextline)
295				end
296			end
297			cfg[key] = value
298		elseif not nextline:match("^%s*$") then
299			-- Make sure format violations don't get overlooked
300			-- here, but ignore blank lines.  Comments are already
301			-- stripped above.
302			abort(1, "Malformed config line: " .. nextline)
303		end
304	end
305
306	assert(io.close(fh))
307	return cfg
308end
309
310local function grab_capenabled(file, open_fail_ok)
311	local capentries = {}
312	local commentExpr = "#.*"
313
314	if file == nil then
315		print "No file"
316		return {}
317	end
318
319	local fh = io.open(file)
320	if fh == nil then
321		if not open_fail_ok then
322			abort(1, "Failed to open " .. file)
323		end
324		return {}
325	end
326
327	for nextline in fh:lines() do
328		-- Strip any comments
329		nextline = nextline:gsub(commentExpr, "")
330		if nextline ~= "" then
331			capentries[nextline] = true
332		end
333	end
334
335	assert(io.close(fh))
336	return capentries
337end
338
339local function process_compat()
340	local nval = 0
341	for _, v in pairs(known_flags) do
342		if v > nval then
343			nval = v
344		end
345	end
346
347	nval = nval << 1
348	for _, v in pairs(compat_options) do
349		if v["stdcompat"] ~= nil then
350			local stdcompat = v["stdcompat"]
351			v["definition"] = "COMPAT_" .. stdcompat:upper()
352			v["compatlevel"] = tonumber(stdcompat:match("([0-9]+)$"))
353			v["flag"] = stdcompat:gsub("FREEBSD", "COMPAT")
354			v["prefix"] = stdcompat:lower() .. "_"
355			v["descr"] = stdcompat:lower()
356		end
357
358		local tmpname = "sys" .. v["flag"]:lower()
359		local dcltmpname = tmpname .. "dcl"
360		files[tmpname] = io.tmpfile()
361		files[dcltmpname] = io.tmpfile()
362		v["tmp"] = tmpname
363		v["dcltmp"] = dcltmpname
364
365		known_flags[v["flag"]] = nval
366		v["mask"] = nval
367		nval = nval << 1
368
369		v["count"] = 0
370	end
371end
372
373local function process_abi_flags()
374	local flags, mask = config["abi_flags"], 0
375	for txtflag in flags:gmatch("([^|]+)") do
376		if known_abi_flags[txtflag] == nil then
377			abort(1, "Unknown abi_flag: " .. txtflag)
378		end
379
380		mask = mask | known_abi_flags[txtflag]["value"]
381	end
382
383	config["abi_flags_mask"] = mask
384end
385
386local function process_obsol()
387	local obsol = config["obsol"]
388	for syscall in obsol:gmatch("([^ ]+)") do
389		config["obsol_dict"][syscall] = true
390	end
391end
392
393local function process_unimpl()
394	local unimpl = config["unimpl"]
395	for syscall in unimpl:gmatch("([^ ]+)") do
396		config["unimpl_dict"][syscall] = true
397	end
398end
399
400local function process_syscall_abi_change()
401	local changes_abi = config["syscall_abi_change"]
402	for syscall in changes_abi:gmatch("([^ ]+)") do
403		config["sys_abi_change"][syscall] = true
404	end
405
406	local no_changes = config["syscall_no_abi_change"]
407	for syscall in no_changes:gmatch("([^ ]+)") do
408		config["sys_no_abi_change"][syscall] = true
409	end
410end
411
412local function abi_changes(name)
413	if known_abi_flags[name] == nil then
414		abort(1, "abi_changes: unknown flag: " .. name)
415	end
416
417	return config["abi_flags_mask"] & known_abi_flags[name]["value"] ~= 0
418end
419
420local function strip_abi_prefix(funcname)
421	local abiprefix = config["abi_func_prefix"]
422	local stripped_name
423	if funcname == nil then
424		return nil
425	end
426	if abiprefix ~= "" and funcname:find("^" .. abiprefix) then
427		stripped_name = funcname:gsub("^" .. abiprefix, "")
428	else
429		stripped_name = funcname
430	end
431
432	return stripped_name
433end
434
435local function read_file(tmpfile)
436	if files[tmpfile] == nil then
437		print("Not found: " .. tmpfile)
438		return
439	end
440
441	local fh = files[tmpfile]
442	assert(fh:seek("set"))
443	return assert(fh:read("a"))
444end
445
446local function write_line(tmpfile, line)
447	if files[tmpfile] == nil then
448		print("Not found: " .. tmpfile)
449		return
450	end
451	assert(files[tmpfile]:write(line))
452end
453
454local function write_line_pfile(tmppat, line)
455	for k in pairs(files) do
456		if k:match(tmppat) ~= nil then
457			assert(files[k]:write(line))
458		end
459	end
460end
461
462-- Check both literal intptr_t and the abi version because this needs
463-- to work both before and after the substitution
464local function isptrtype(type)
465	return type:find("*") or type:find("caddr_t") or
466	    type:find("intptr_t") or type:find(config['abi_intptr_t'])
467end
468
469local function isptrarraytype(type)
470	return type:find("[*][*]") or type:find("[*][ ]*const[ ]*[*]")
471end
472
473-- Find types that are always 64-bits wide
474local function is64bittype(type)
475	return type:find("^dev_t[ ]*$") or type:find("^id_t[ ]*$") or type:find("^off_t[ ]*$")
476end
477
478local process_syscall_def
479
480-- These patterns are processed in order on any line that isn't empty.
481local pattern_table = {
482	{
483		pattern = "%s*$" .. config['os_id_keyword'],
484		process = function(_, _)
485			-- Ignore... ID tag
486		end,
487	},
488	{
489		dump_prevline = true,
490		pattern = "^#%s*include",
491		process = function(line)
492			line = line .. "\n"
493			write_line('sysinc', line)
494		end,
495	},
496	{
497		dump_prevline = true,
498		pattern = "^#",
499		process = function(line)
500			if line:find("^#%s*if") then
501				savesyscall = maxsyscall
502			elseif line:find("^#%s*else") then
503				maxsyscall = savesyscall
504			end
505			line = line .. "\n"
506			write_line('sysent', line)
507			write_line('sysdcl', line)
508			write_line('sysarg', line)
509			write_line_pfile('syscompat[0-9]*$', line)
510			write_line('sysnames', line)
511			write_line_pfile('systrace.*', line)
512		end,
513	},
514	{
515		dump_prevline = true,
516		pattern = "%%ABI_HEADERS%%",
517		process = function()
518			if config['abi_headers'] ~= "" then
519				line = config['abi_headers'] .. "\n"
520				write_line('sysinc', line)
521			end
522		end,
523	},
524	{
525		-- Buffer anything else
526		pattern = ".+",
527		process = function(line, prevline)
528			local incomplete = line:find("\\$") ~= nil
529			-- Lines that end in \ get the \ stripped
530			-- Lines that start with a syscall number, prepend \n
531			line = trim(line):gsub("\\$", "")
532			if line:find("^[0-9]") and prevline then
533				process_syscall_def(prevline)
534				prevline = nil
535			end
536
537			prevline = (prevline or '') .. line
538			incomplete = incomplete or prevline:find(",$") ~= nil
539			incomplete = incomplete or prevline:find("{") ~= nil and
540			    prevline:find("}") == nil
541			if prevline:find("^[0-9]") and not incomplete then
542				process_syscall_def(prevline)
543				prevline = nil
544			end
545
546			return prevline
547		end,
548	},
549}
550
551local function process_sysfile(file)
552	local capentries = {}
553	local commentExpr = "^%s*;.*"
554
555	if file == nil then
556		print "No file"
557		return {}
558	end
559
560	local fh = io.open(file)
561	if fh == nil then
562		print("Failed to open " .. file)
563		return {}
564	end
565
566	local function do_match(nextline, prevline)
567		local pattern, handler, dump
568		for _, v in pairs(pattern_table) do
569			pattern = v['pattern']
570			handler = v['process']
571			dump = v['dump_prevline']
572			if nextline:match(pattern) then
573				if dump and prevline then
574					process_syscall_def(prevline)
575					prevline = nil
576				end
577
578				return handler(nextline, prevline)
579			end
580		end
581
582		abort(1, "Failed to handle: " .. nextline)
583	end
584
585	local prevline
586	for nextline in fh:lines() do
587		-- Strip any comments
588		nextline = nextline:gsub(commentExpr, "")
589		if nextline ~= "" then
590			prevline = do_match(nextline, prevline)
591		end
592	end
593
594	-- Dump any remainder
595	if prevline ~= nil and prevline:find("^[0-9]") then
596		process_syscall_def(prevline)
597	end
598
599	assert(io.close(fh))
600	return capentries
601end
602
603local function get_mask(flags)
604	local mask = 0
605	for _, v in ipairs(flags) do
606		if known_flags[v] == nil then
607			abort(1, "Checking for unknown flag " .. v)
608		end
609
610		mask = mask | known_flags[v]
611	end
612
613	return mask
614end
615
616local function get_mask_pat(pflags)
617	local mask = 0
618	for k, v in pairs(known_flags) do
619		if k:find(pflags) then
620			mask = mask | v
621		end
622	end
623
624	return mask
625end
626
627local function align_sysent_comment(col)
628	write_line("sysent", "\t")
629	col = col + 8 - col % 8
630	while col < 56 do
631		write_line("sysent", "\t")
632		col = col + 8
633	end
634end
635
636local function strip_arg_annotations(arg)
637	arg = arg:gsub("_Contains_[^ ]*[_)] ?", "")
638	arg = arg:gsub("_In[^ ]*[_)] ?", "")
639	arg = arg:gsub("_Out[^ ]*[_)] ?", "")
640	return trim(arg)
641end
642
643local function check_abi_changes(arg)
644	for k, v in pairs(known_abi_flags) do
645		local exprs = v["exprs"]
646		if abi_changes(k) and exprs ~= nil then
647			for _, e in pairs(exprs) do
648				if arg:find(e) then
649					return true
650				end
651			end
652		end
653	end
654
655	return false
656end
657
658local function process_args(args)
659	local funcargs = {}
660	local changes_abi = false
661
662	for arg in args:gmatch("([^,]+)") do
663		local arg_abi_change = check_abi_changes(arg)
664		changes_abi = changes_abi or arg_abi_change
665
666		arg = strip_arg_annotations(arg)
667
668		local argname = arg:match("([^* ]+)$")
669
670		-- argtype is... everything else.
671		local argtype = trim(arg:gsub(argname .. "$", ""), nil)
672
673		if argtype == "" and argname == "void" then
674			goto out
675		end
676
677		-- is64bittype() needs a bare type so check it after argname
678		-- is removed
679		changes_abi = changes_abi or (abi_changes("pair_64bit") and is64bittype(argtype))
680
681		argtype = argtype:gsub("intptr_t", config["abi_intptr_t"])
682		argtype = argtype:gsub("semid_t", config["abi_semid_t"])
683		if isptrtype(argtype) then
684			argtype = argtype:gsub("size_t", config["abi_size_t"])
685			argtype = argtype:gsub("^long", config["abi_long"]);
686			argtype = argtype:gsub("^u_long", config["abi_u_long"]);
687			argtype = argtype:gsub("^const u_long", "const " .. config["abi_u_long"]);
688		elseif argtype:find("^long$") then
689			argtype = config["abi_long"]
690		end
691		if isptrarraytype(argtype) and config["abi_ptr_array_t"] ~= "" then
692			-- `* const *` -> `**`
693			argtype = argtype:gsub("[*][ ]*const[ ]*[*]", "**")
694			-- e.g., `struct aiocb **` -> `uint32_t *`
695			argtype = argtype:gsub("[^*]*[*]", config["abi_ptr_array_t"] .. " ", 1)
696		end
697
698		-- XX TODO: Forward declarations? See: sysstubfwd in CheriBSD
699		if arg_abi_change then
700			local abi_type_suffix = config["abi_type_suffix"]
701			argtype = argtype:gsub("(struct [^ ]*)", "%1" ..
702			    abi_type_suffix)
703			argtype = argtype:gsub("(union [^ ]*)", "%1" ..
704			    abi_type_suffix)
705		end
706
707		if abi_changes("pair_64bit") and is64bittype(argtype) then
708			if #funcargs % 2 == 1 then
709				funcargs[#funcargs + 1] = {
710					type = "int",
711					name = "_pad",
712				}
713			end
714			funcargs[#funcargs + 1] = {
715				type = "uint32_t",
716				name = argname .. "1",
717			}
718			funcargs[#funcargs + 1] = {
719				type = "uint32_t",
720				name = argname .. "2",
721			}
722		else
723			funcargs[#funcargs + 1] = {
724				type = argtype,
725				name = argname,
726			}
727		end
728	end
729
730	::out::
731	return funcargs, changes_abi
732end
733
734local function handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
735    auditev, syscallret, funcname, funcalias, funcargs, argalias)
736	local argssize
737
738	if flags & known_flags["SYSMUX"] ~= 0 then
739		argssize = "0"
740	elseif #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
741		argssize = "AS(" .. argalias .. ")"
742	else
743		argssize = "0"
744	end
745
746	write_line("systrace", string.format([[
747	/* %s */
748	case %d: {
749]], funcname, sysnum))
750	write_line("systracetmp", string.format([[
751	/* %s */
752	case %d:
753]], funcname, sysnum))
754	write_line("systraceret", string.format([[
755	/* %s */
756	case %d:
757]], funcname, sysnum))
758
759	if #funcargs > 0 and flags & known_flags["SYSMUX"] == 0 then
760		write_line("systracetmp", "\t\tswitch (ndx) {\n")
761		write_line("systrace", string.format(
762		    "\t\tstruct %s *p = params;\n", argalias))
763
764
765		local argtype, argname, desc, padding
766		padding = ""
767		for idx, arg in ipairs(funcargs) do
768			argtype = arg["type"]
769			argname = arg["name"]
770
771			argtype = trim(argtype:gsub("__restrict$", ""), nil)
772			if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
773				write_line("systracetmp", "#ifdef PAD64_REQUIRED\n")
774			end
775			-- Pointer arg?
776			if argtype:find("*") then
777				desc = "userland " .. argtype
778			else
779				desc = argtype;
780			end
781			write_line("systracetmp", string.format(
782			    "\t\tcase %d%s:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n",
783			    idx - 1, padding, desc))
784			if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
785				padding = " - _P_"
786				write_line("systracetmp", "#define _P_ 0\n#else\n#define _P_ 1\n#endif\n")
787			end
788
789			if isptrtype(argtype) then
790				write_line("systrace", string.format(
791				    "\t\tuarg[a++] = (%s)p->%s; /* %s */\n",
792				    config["ptr_intptr_t_cast"],
793				    argname, argtype))
794			elseif argtype == "union l_semun" then
795				write_line("systrace", string.format(
796				    "\t\tuarg[a++] = p->%s.buf; /* %s */\n",
797				    argname, argtype))
798			elseif argtype:sub(1,1) == "u" or argtype == "size_t" then
799				write_line("systrace", string.format(
800				    "\t\tuarg[a++] = p->%s; /* %s */\n",
801				    argname, argtype))
802			else
803				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
804					write_line("systrace", "#ifdef PAD64_REQUIRED\n")
805				end
806				write_line("systrace", string.format(
807				    "\t\tiarg[a++] = p->%s; /* %s */\n",
808				    argname, argtype))
809				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
810					write_line("systrace", "#endif\n")
811				end
812			end
813		end
814
815		write_line("systracetmp",
816		    "\t\tdefault:\n\t\t\tbreak;\n\t\t};\n")
817		if padding ~= "" then
818			write_line("systracetmp", "#undef _P_\n\n")
819		end
820
821		write_line("systraceret", string.format([[
822		if (ndx == 0 || ndx == 1)
823			p = "%s";
824		break;
825]], syscallret))
826	end
827	local n_args = #funcargs
828	if flags & known_flags["SYSMUX"] ~= 0 then
829		n_args = 0
830	end
831	write_line("systrace", string.format(
832	    "\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", n_args))
833	write_line("systracetmp", "\t\tbreak;\n")
834
835	local nargflags = get_mask({"NOARGS", "NOPROTO", "NODEF"})
836	if flags & nargflags == 0 then
837		if #funcargs > 0 then
838			write_line("sysarg", string.format("struct %s {\n",
839			    argalias))
840			for _, v in ipairs(funcargs) do
841				local argname, argtype = v["name"], v["type"]
842				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
843					write_line("sysarg", "#ifdef PAD64_REQUIRED\n")
844				end
845				write_line("sysarg", string.format(
846				    "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
847				    argname, argtype,
848				    argtype, argname,
849				    argname, argtype))
850				if argtype == "int" and argname == "_pad" and abi_changes("pair_64bit") then
851					write_line("sysarg", "#endif\n")
852				end
853			end
854			write_line("sysarg", "};\n")
855		else
856			write_line("sysarg", string.format(
857			    "struct %s {\n\tregister_t dummy;\n};\n", argalias))
858		end
859	end
860
861	local protoflags = get_mask({"NOPROTO", "NODEF"})
862	if flags & protoflags == 0 then
863		if funcname == "nosys" or funcname == "lkmnosys" or
864		    funcname == "sysarch" or funcname:find("^freebsd") or
865		    funcname:find("^linux") then
866			write_line("sysdcl", string.format(
867			    "%s\t%s(struct thread *, struct %s *)",
868			    rettype, funcname, argalias))
869		else
870			write_line("sysdcl", string.format(
871			    "%s\tsys_%s(struct thread *, struct %s *)",
872			    rettype, funcname, argalias))
873		end
874		write_line("sysdcl", ";\n")
875		write_line("sysaue", string.format("#define\t%sAUE_%s\t%s\n",
876		    config['syscallprefix'], funcalias, auditev))
877	end
878
879	write_line("sysent",
880	    string.format("\t{ .sy_narg = %s, .sy_call = (sy_call_t *)", argssize))
881	local column = 8 + 2 + #argssize + 15
882
883	if flags & known_flags["SYSMUX"] ~= 0 then
884		write_line("sysent", string.format(
885		    "nosys, .sy_auevent = AUE_NULL, " ..
886		    ".sy_flags = %s, .sy_thrcnt = SY_THR_STATIC },",
887		    sysflags))
888		column = column + #"nosys" + #"AUE_NULL" + 3
889	elseif flags & known_flags["NOSTD"] ~= 0 then
890		write_line("sysent", string.format(
891		    "lkmressys, .sy_auevent = AUE_NULL, " ..
892		    ".sy_flags = %s, .sy_thrcnt = SY_THR_ABSENT },",
893		    sysflags))
894		column = column + #"lkmressys" + #"AUE_NULL" + 3
895	else
896		if funcname == "nosys" or funcname == "lkmnosys" or
897		    funcname == "sysarch" or funcname:find("^freebsd") or
898		    funcname:find("^linux") then
899			write_line("sysent", string.format(
900			    "%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
901			    funcname, auditev, sysflags, thr_flag))
902			column = column + #funcname + #auditev + #sysflags + 3
903		else
904			write_line("sysent", string.format(
905			    "sys_%s, .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
906			    funcname, auditev, sysflags, thr_flag))
907			column = column + #funcname + #auditev + #sysflags + 7
908		end
909	end
910
911	align_sysent_comment(column)
912	write_line("sysent", string.format("/* %d = %s */\n",
913	    sysnum, funcalias))
914	write_line("sysnames", string.format("\t\"%s\",\t\t\t/* %d = %s */\n",
915	    funcalias, sysnum, funcalias))
916
917	if flags & known_flags["NODEF"] == 0 then
918		write_line("syshdr", string.format("#define\t%s%s\t%d\n",
919		    config['syscallprefix'], funcalias, sysnum))
920		write_line("sysmk", string.format(" \\\n\t%s.o",
921		    funcalias))
922	end
923end
924
925local function handle_obsol(sysnum, funcname, comment)
926	write_line("sysent",
927	    "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
928	    ".sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT },")
929	align_sysent_comment(34)
930
931	write_line("sysent", string.format("/* %d = obsolete %s */\n",
932	    sysnum, comment))
933	write_line("sysnames", string.format(
934	    "\t\"obs_%s\",\t\t\t/* %d = obsolete %s */\n",
935	    funcname, sysnum, comment))
936	write_line("syshdr", string.format("\t\t\t\t/* %d is obsolete %s */\n",
937	    sysnum, comment))
938end
939
940local function handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
941    auditev, funcname, funcalias, funcargs, argalias)
942	local argssize, out, outdcl, wrap, prefix, descr
943
944	if #funcargs > 0 or flags & known_flags["NODEF"] ~= 0 then
945		argssize = "AS(" .. argalias .. ")"
946	else
947		argssize = "0"
948	end
949
950	for _, v in pairs(compat_options) do
951		if flags & v["mask"] ~= 0 then
952			if config["mincompat"] > v["compatlevel"] then
953				funcname = strip_abi_prefix(funcname)
954				funcname = v["prefix"] .. funcname
955				return handle_obsol(sysnum, funcname, funcname)
956			end
957			v["count"] = v["count"] + 1
958			out = v["tmp"]
959			outdcl = v["dcltmp"]
960			wrap = v["flag"]:lower()
961			prefix = v["prefix"]
962			descr = v["descr"]
963			goto compatdone
964		end
965	end
966
967	::compatdone::
968	local dprotoflags = get_mask({"NOPROTO", "NODEF"})
969	local nargflags = dprotoflags | known_flags["NOARGS"]
970	if #funcargs > 0 and flags & nargflags == 0 then
971		write_line(out, string.format("struct %s {\n", argalias))
972		for _, v in ipairs(funcargs) do
973			local argname, argtype = v["name"], v["type"]
974			write_line(out, string.format(
975			    "\tchar %s_l_[PADL_(%s)]; %s %s; char %s_r_[PADR_(%s)];\n",
976			    argname, argtype,
977			    argtype, argname,
978			    argname, argtype))
979		end
980		write_line(out, "};\n")
981	elseif flags & nargflags == 0 then
982		write_line("sysarg", string.format(
983		    "struct %s {\n\tregister_t dummy;\n};\n", argalias))
984	end
985	if flags & dprotoflags == 0 then
986		write_line(outdcl, string.format(
987		    "%s\t%s%s(struct thread *, struct %s *);\n",
988		    rettype, prefix, funcname, argalias))
989		write_line("sysaue", string.format(
990		    "#define\t%sAUE_%s%s\t%s\n", config['syscallprefix'],
991		    prefix, funcname, auditev))
992	end
993
994	if flags & known_flags['NOSTD'] ~= 0 then
995		write_line("sysent", string.format(
996		    "\t{ .sy_narg = %s, .sy_call = (sy_call_t *)%s, " ..
997		    ".sy_auevent = %s, .sy_flags = 0, " ..
998		    ".sy_thrcnt = SY_THR_ABSENT },",
999		    "0", "lkmressys", "AUE_NULL"))
1000		align_sysent_comment(8 + 2 + #"0" + 15 + #"lkmressys" +
1001		    #"AUE_NULL" + 3)
1002	else
1003		write_line("sysent", string.format(
1004		    "\t{ %s(%s,%s), .sy_auevent = %s, .sy_flags = %s, .sy_thrcnt = %s },",
1005		    wrap, argssize, funcname, auditev, sysflags, thr_flag))
1006		align_sysent_comment(8 + 9 + #argssize + 1 + #funcname +
1007		    #auditev + #sysflags + 4)
1008	end
1009
1010	write_line("sysent", string.format("/* %d = %s %s */\n",
1011	    sysnum, descr, funcalias))
1012	write_line("sysnames", string.format(
1013	    "\t\"%s.%s\",\t\t/* %d = %s %s */\n",
1014	    wrap, funcalias, sysnum, descr, funcalias))
1015	-- Do not provide freebsdN_* symbols in libc for < FreeBSD 7
1016	local nosymflags = get_mask({"COMPAT", "COMPAT4", "COMPAT6"})
1017	if flags & nosymflags ~= 0 then
1018		write_line("syshdr", string.format(
1019		    "\t\t\t\t/* %d is %s %s */\n",
1020		    sysnum, descr, funcalias))
1021	elseif flags & known_flags["NODEF"] == 0 then
1022		write_line("syshdr", string.format("#define\t%s%s%s\t%d\n",
1023		    config['syscallprefix'], prefix, funcalias, sysnum))
1024		write_line("sysmk", string.format(" \\\n\t%s%s.o",
1025		    prefix, funcalias))
1026	end
1027end
1028
1029local function handle_unimpl(sysnum, sysstart, sysend, comment)
1030	if sysstart == nil and sysend == nil then
1031		sysstart = tonumber(sysnum)
1032		sysend = tonumber(sysnum)
1033	end
1034
1035	sysnum = sysstart
1036	while sysnum <= sysend do
1037		write_line("sysent", string.format(
1038		    "\t{ .sy_narg = 0, .sy_call = (sy_call_t *)nosys, " ..
1039		    ".sy_auevent = AUE_NULL, .sy_flags = 0, " ..
1040		    ".sy_thrcnt = SY_THR_ABSENT },\t\t\t/* %d = %s */\n",
1041		    sysnum, comment))
1042		write_line("sysnames", string.format(
1043		    "\t\"#%d\",\t\t\t/* %d = %s */\n",
1044		    sysnum, sysnum, comment))
1045		sysnum = sysnum + 1
1046	end
1047end
1048
1049local function handle_reserved(sysnum, sysstart, sysend, comment)
1050	handle_unimpl(sysnum, sysstart, sysend, "reserved for local use")
1051end
1052
1053process_syscall_def = function(line)
1054	local sysstart, sysend, flags, funcname, sysflags
1055	local thr_flag, syscallret
1056	local orig = line
1057	flags = 0
1058	thr_flag = "SY_THR_STATIC"
1059
1060	-- Parse out the interesting information first
1061	local initialExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s*"
1062	local sysnum, auditev, allflags = line:match(initialExpr)
1063
1064	if sysnum == nil or auditev == nil or allflags == nil then
1065		-- XXX TODO: Better?
1066		abort(1, "Completely malformed: " .. line)
1067	end
1068
1069	if sysnum:find("-") then
1070		sysstart, sysend = sysnum:match("^([%d]+)-([%d]+)$")
1071		if sysstart == nil or sysend == nil then
1072			abort(1, "Malformed range: " .. sysnum)
1073		end
1074		sysnum = nil
1075		sysstart = tonumber(sysstart)
1076		sysend = tonumber(sysend)
1077		if sysstart ~= maxsyscall + 1 then
1078			abort(1, "syscall number out of sync, missing " ..
1079			    maxsyscall + 1)
1080		end
1081	else
1082		sysnum = tonumber(sysnum)
1083		if sysnum ~= maxsyscall + 1 then
1084			abort(1, "syscall number out of sync, missing " ..
1085			    maxsyscall + 1)
1086		end
1087	end
1088
1089	-- Split flags
1090	for flag in allflags:gmatch("([^|]+)") do
1091		if known_flags[flag] == nil then
1092			abort(1, "Unknown flag " .. flag .. " for " ..  sysnum)
1093		end
1094		flags = flags | known_flags[flag]
1095	end
1096
1097	if (flags & get_mask({"RESERVED", "UNIMPL"})) == 0 and sysnum == nil then
1098		abort(1, "Range only allowed with RESERVED and UNIMPL: " .. line)
1099	end
1100
1101	if (flags & known_flags["NOTSTATIC"]) ~= 0 then
1102		thr_flag = "SY_THR_ABSENT"
1103	end
1104
1105	-- Strip earlier bits out, leave declaration + alt
1106	line = line:gsub("^.+" .. allflags .. "%s*", "")
1107
1108	local decl_fnd = line:find("^{") ~= nil
1109	if decl_fnd and line:find("}") == nil then
1110		abort(1, "Malformed, no closing brace: " .. line)
1111	end
1112
1113	local decl, alt
1114	if decl_fnd then
1115		line = line:gsub("^{", "")
1116		decl, alt = line:match("([^}]*)}[%s]*(.*)$")
1117	else
1118		alt = line
1119	end
1120
1121	if decl == nil and alt == nil then
1122		abort(1, "Malformed bits: " .. line)
1123	end
1124
1125	local funcalias, funcomment, argalias, rettype, args
1126	if not decl_fnd and alt ~= nil and alt ~= "" then
1127		-- Peel off one entry for name
1128		funcname = trim(alt:match("^([^%s]+)"), nil)
1129		alt = alt:gsub("^([^%s]+)[%s]*", "")
1130	end
1131	-- Do we even need it?
1132	if flags & get_mask({"OBSOL", "UNIMPL"}) ~= 0 then
1133		local NF = 0
1134		for _ in orig:gmatch("[^%s]+") do
1135			NF = NF + 1
1136		end
1137
1138		funcomment = funcname or ''
1139		if NF < 6 then
1140			funcomment = funcomment .. " " .. alt
1141		end
1142
1143		funcomment = trim(funcomment)
1144
1145--		if funcname ~= nil then
1146--		else
1147--			funcomment = trim(alt)
1148--		end
1149		goto skipalt
1150	end
1151
1152	if alt ~= nil and alt ~= "" then
1153		local altExpr = "^([^%s]+)%s+([^%s]+)%s+([^%s]+)"
1154		funcalias, argalias, rettype = alt:match(altExpr)
1155		funcalias = trim(funcalias)
1156		if funcalias == nil or argalias == nil or rettype == nil then
1157			abort(1, "Malformed alt: " .. line)
1158		end
1159	end
1160	if decl_fnd then
1161		-- Don't clobber rettype set in the alt information
1162		if rettype == nil then
1163			rettype = "int"
1164		end
1165		-- Peel off the return type
1166		syscallret = line:match("([^%s]+)%s")
1167		line = line:match("[^%s]+%s(.+)")
1168		-- Pointer incoming
1169		if line:sub(1,1) == "*" then
1170			syscallret = syscallret .. " "
1171		end
1172		while line:sub(1,1) == "*" do
1173			line = line:sub(2)
1174			syscallret = syscallret .. "*"
1175		end
1176		funcname = line:match("^([^(]+)%(")
1177		if funcname == nil then
1178			abort(1, "Not a signature? " .. line)
1179		end
1180		args = line:match("^[^(]+%((.+)%)[^)]*$")
1181		args = trim(args, '[,%s]')
1182	end
1183
1184	::skipalt::
1185
1186	if funcname == nil then
1187		funcname = funcalias
1188	end
1189
1190	funcname = trim(funcname)
1191
1192	if config["obsol_dict"][funcname] then
1193		local compat_prefix = ""
1194		for _, v in pairs(compat_options) do
1195			if flags & v["mask"] ~= 0 then
1196				compat_prefix = v["prefix"]
1197				goto obsol_compat_done
1198			end
1199		end
1200		::obsol_compat_done::
1201		args = nil
1202		flags = known_flags['OBSOL']
1203		funcomment = compat_prefix .. funcname
1204	end
1205	if config["unimpl_dict"][funcname] then
1206		flags = known_flags['UNIMPL']
1207		funcomment = funcname
1208	end
1209
1210	sysflags = "0"
1211
1212	-- NODEF events do not get audited
1213	if flags & known_flags['NODEF'] ~= 0 then
1214		auditev = 'AUE_NULL'
1215	end
1216
1217	-- If applicable; strip the ABI prefix from the name
1218	local stripped_name = strip_abi_prefix(funcname)
1219
1220	if flags & known_flags['CAPENABLED'] ~= 0 or
1221	    config["capenabled"][funcname] ~= nil or
1222	    config["capenabled"][stripped_name] ~= nil then
1223		sysflags = "SYF_CAPENABLED"
1224	end
1225
1226	local funcargs = {}
1227	local changes_abi = false
1228	if args ~= nil then
1229		funcargs, changes_abi = process_args(args)
1230	end
1231	if config["sys_no_abi_change"][funcname] then
1232		changes_abi = false
1233	end
1234	local noproto = config["abi_flags"] ~= "" and not changes_abi
1235
1236	local argprefix = ''
1237	local funcprefix = ''
1238	if abi_changes("pointer_args") then
1239		for _, v in ipairs(funcargs) do
1240			if isptrtype(v["type"]) then
1241				if config["sys_no_abi_change"][funcname] then
1242					print("WARNING: " .. funcname ..
1243					    " in syscall_no_abi_change, but pointers args are present")
1244				end
1245				changes_abi = true
1246				goto ptrfound
1247			end
1248		end
1249		::ptrfound::
1250	end
1251	if config["sys_abi_change"][funcname] then
1252		changes_abi = true
1253	end
1254	if changes_abi then
1255		-- argalias should be:
1256		--   COMPAT_PREFIX + ABI Prefix + funcname
1257		argprefix = config['abi_func_prefix']
1258		funcprefix = config['abi_func_prefix']
1259		funcalias = funcprefix .. funcname
1260		noproto = false
1261	end
1262	if funcname ~= nil then
1263		funcname = funcprefix .. funcname
1264	end
1265	if funcalias == nil or funcalias == "" then
1266		funcalias = funcname
1267	end
1268
1269	if argalias == nil and funcname ~= nil then
1270		argalias = funcname .. "_args"
1271		for _, v in pairs(compat_options) do
1272			local mask = v["mask"]
1273			if (flags & mask) ~= 0 then
1274				-- Multiple aliases doesn't seem to make
1275				-- sense.
1276				argalias = v["prefix"] .. argalias
1277				goto out
1278			end
1279		end
1280		::out::
1281	elseif argalias ~= nil then
1282		argalias = argprefix .. argalias
1283	end
1284
1285	local ncompatflags = get_mask({"STD", "NODEF", "NOARGS", "NOPROTO",
1286	    "NOSTD"})
1287	local compatflags = get_mask_pat("COMPAT.*")
1288	if noproto or flags & known_flags["SYSMUX"] ~= 0 then
1289		flags = flags | known_flags["NOPROTO"];
1290	end
1291	if flags & known_flags["OBSOL"] ~= 0 then
1292		handle_obsol(sysnum, funcname, funcomment)
1293	elseif flags & known_flags["RESERVED"] ~= 0 then
1294		handle_reserved(sysnum, sysstart, sysend)
1295	elseif flags & known_flags["UNIMPL"] ~= 0 then
1296		handle_unimpl(sysnum, sysstart, sysend, funcomment)
1297	elseif flags & compatflags ~= 0 then
1298		if flags & known_flags['STD'] ~= 0 then
1299			abort(1, "Incompatible COMPAT/STD: " .. line)
1300		end
1301		handle_compat(sysnum, thr_flag, flags, sysflags, rettype,
1302		    auditev, funcname, funcalias, funcargs, argalias)
1303	elseif flags & ncompatflags ~= 0 then
1304		handle_noncompat(sysnum, thr_flag, flags, sysflags, rettype,
1305		    auditev, syscallret, funcname, funcalias, funcargs,
1306		    argalias)
1307	else
1308		abort(1, "Bad flags? " .. line)
1309	end
1310
1311	if sysend ~= nil then
1312		maxsyscall = sysend
1313	elseif sysnum ~= nil then
1314		maxsyscall = sysnum
1315	end
1316end
1317
1318-- Entry point
1319
1320if #arg < 1 or #arg > 2 then
1321	error("usage: " .. arg[0] .. " input-file <config-file>")
1322end
1323
1324local sysfile, configfile = arg[1], arg[2]
1325
1326-- process_config either returns nil and a message, or a
1327-- table that we should merge into the global config
1328if configfile ~= nil then
1329	local res = assert(process_config(configfile))
1330
1331	for k, v in pairs(res) do
1332		if v ~= config[k] then
1333			config[k] = v
1334			config_modified[k] = true
1335		end
1336	end
1337end
1338
1339-- We ignore errors here if we're relying on the default configuration.
1340if not config_modified["capenabled"] then
1341	config["capenabled"] = grab_capenabled(config['capabilities_conf'],
1342	    config_modified["capabilities_conf"] == nil)
1343elseif config["capenabled"] ~= "" then
1344	-- Due to limitations in the config format mostly, we'll have a comma
1345	-- separated list.  Parse it into lines
1346	local capenabled = {}
1347	-- print("here: " .. config["capenabled"])
1348	for sysc in config["capenabled"]:gmatch("([^,]+)") do
1349		capenabled[sysc] = true
1350	end
1351	config["capenabled"] = capenabled
1352end
1353process_compat()
1354process_abi_flags()
1355process_syscall_abi_change()
1356process_obsol()
1357process_unimpl()
1358
1359if not lfs.mkdir(tmpspace) then
1360	error("Failed to create tempdir " .. tmpspace)
1361end
1362
1363-- XXX Revisit the error handling here, we should probably move the rest of this
1364-- into a function that we pcall() so we can catch the errors and clean up
1365-- gracefully.
1366for _, v in ipairs(temp_files) do
1367	local tmpname = tmpspace .. v
1368	files[v] = io.open(tmpname, "w+")
1369	-- XXX Revisit these with a pcall() + error handler
1370	if not files[v] then
1371		abort(1, "Failed to open temp file: " .. tmpname)
1372	end
1373end
1374
1375for _, v in ipairs(output_files) do
1376	local tmpname = tmpspace .. v
1377	files[v] = io.open(tmpname, "w+")
1378	-- XXX Revisit these with a pcall() + error handler
1379	if not files[v] then
1380		abort(1, "Failed to open temp output file: " .. tmpname)
1381	end
1382end
1383
1384-- Write out all of the preamble bits
1385write_line("sysent", string.format([[
1386
1387/* The casts are bogus but will do for now. */
1388struct sysent %s[] = {
1389]], config['switchname']))
1390
1391write_line("syssw", string.format([[/*
1392 * System call switch table.
1393 *
1394 * DO NOT EDIT-- this file is automatically %s.
1395 * $%s$
1396 */
1397
1398]], generated_tag, config['os_id_keyword']))
1399
1400write_line("sysarg", string.format([[/*
1401 * System call prototypes.
1402 *
1403 * DO NOT EDIT-- this file is automatically %s.
1404 * $%s$
1405 */
1406
1407#ifndef %s
1408#define	%s
1409
1410#include <sys/signal.h>
1411#include <sys/acl.h>
1412#include <sys/cpuset.h>
1413#include <sys/domainset.h>
1414#include <sys/_ffcounter.h>
1415#include <sys/_semaphore.h>
1416#include <sys/ucontext.h>
1417#include <sys/wait.h>
1418
1419#include <bsm/audit_kevents.h>
1420
1421struct proc;
1422
1423struct thread;
1424
1425#define	PAD_(t)	(sizeof(register_t) <= sizeof(t) ? \
1426		0 : sizeof(register_t) - sizeof(t))
1427
1428#if BYTE_ORDER == LITTLE_ENDIAN
1429#define	PADL_(t)	0
1430#define	PADR_(t)	PAD_(t)
1431#else
1432#define	PADL_(t)	PAD_(t)
1433#define	PADR_(t)	0
1434#endif
1435
1436]], generated_tag, config['os_id_keyword'], config['sysproto_h'],
1437    config['sysproto_h']))
1438if abi_changes("pair_64bit") then
1439	write_line("sysarg", string.format([[
1440#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1441#define PAD64_REQUIRED
1442#endif
1443]]))
1444end
1445if abi_changes("pair_64bit") then
1446	write_line("systrace", string.format([[
1447#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
1448#define PAD64_REQUIRED
1449#endif
1450]]))
1451end
1452for _, v in pairs(compat_options) do
1453	write_line(v["tmp"], string.format("\n#ifdef %s\n\n", v["definition"]))
1454end
1455
1456write_line("sysnames", string.format([[/*
1457 * System call names.
1458 *
1459 * DO NOT EDIT-- this file is automatically %s.
1460 * $%s$
1461 */
1462
1463const char *%s[] = {
1464]], generated_tag, config['os_id_keyword'], config['namesname']))
1465
1466write_line("syshdr", string.format([[/*
1467 * System call numbers.
1468 *
1469 * DO NOT EDIT-- this file is automatically %s.
1470 * $%s$
1471 */
1472
1473]], generated_tag, config['os_id_keyword']))
1474
1475write_line("sysmk", string.format([[# FreeBSD system call object files.
1476# DO NOT EDIT-- this file is automatically %s.
1477# $%s$
1478MIASM = ]], generated_tag, config['os_id_keyword']))
1479
1480write_line("systrace", string.format([[/*
1481 * System call argument to DTrace register array converstion.
1482 *
1483 * DO NOT EDIT-- this file is automatically %s.
1484 * $%s$
1485 * This file is part of the DTrace syscall provider.
1486 */
1487
1488static void
1489systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args)
1490{
1491	int64_t *iarg = (int64_t *)uarg;
1492	int a = 0;
1493	switch (sysnum) {
1494]], generated_tag, config['os_id_keyword']))
1495
1496write_line("systracetmp", [[static void
1497systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1498{
1499	const char *p = NULL;
1500	switch (sysnum) {
1501]])
1502
1503write_line("systraceret", [[static void
1504systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
1505{
1506	const char *p = NULL;
1507	switch (sysnum) {
1508]])
1509
1510-- Processing the sysfile will parse out the preprocessor bits and put them into
1511-- the appropriate place.  Any syscall-looking lines get thrown into the sysfile
1512-- buffer, one per line, for later processing once they're all glued together.
1513process_sysfile(sysfile)
1514
1515write_line("sysinc",
1516    "\n#define AS(name) (sizeof(struct name) / sizeof(register_t))\n")
1517
1518for _, v in pairs(compat_options) do
1519	if v["count"] > 0 then
1520		write_line("sysinc", string.format([[
1521
1522#ifdef %s
1523#define %s(n, name) .sy_narg = n, .sy_call = (sy_call_t *)__CONCAT(%s, name)
1524#else
1525#define %s(n, name) .sy_narg = 0, .sy_call = (sy_call_t *)nosys
1526#endif
1527]], v["definition"], v["flag"]:lower(), v["prefix"], v["flag"]:lower()))
1528	end
1529
1530	write_line(v["dcltmp"], string.format("\n#endif /* %s */\n\n",
1531	    v["definition"]))
1532end
1533
1534write_line("sysprotoend", string.format([[
1535
1536#undef PAD_
1537#undef PADL_
1538#undef PADR_
1539
1540#endif /* !%s */
1541]], config["sysproto_h"]))
1542
1543write_line("sysmk", "\n")
1544write_line("sysent", "};\n")
1545write_line("sysnames", "};\n")
1546-- maxsyscall is the highest seen; MAXSYSCALL should be one higher
1547write_line("syshdr", string.format("#define\t%sMAXSYSCALL\t%d\n",
1548    config["syscallprefix"], maxsyscall + 1))
1549write_line("systrace", [[
1550	default:
1551		*n_args = 0;
1552		break;
1553	};
1554}
1555]])
1556
1557write_line("systracetmp", [[
1558	default:
1559		break;
1560	};
1561	if (p != NULL)
1562		strlcpy(desc, p, descsz);
1563}
1564]])
1565
1566write_line("systraceret", [[
1567	default:
1568		break;
1569	};
1570	if (p != NULL)
1571		strlcpy(desc, p, descsz);
1572}
1573]])
1574
1575-- Finish up; output
1576write_line("syssw", read_file("sysinc"))
1577write_line("syssw", read_file("sysent"))
1578
1579write_line("sysproto", read_file("sysarg"))
1580write_line("sysproto", read_file("sysdcl"))
1581for _, v in pairs(compat_options) do
1582	write_line("sysproto", read_file(v["tmp"]))
1583	write_line("sysproto", read_file(v["dcltmp"]))
1584end
1585write_line("sysproto", read_file("sysaue"))
1586write_line("sysproto", read_file("sysprotoend"))
1587
1588write_line("systrace", read_file("systracetmp"))
1589write_line("systrace", read_file("systraceret"))
1590
1591for _, v in ipairs(output_files) do
1592	local target = config[v]
1593	if target ~= "/dev/null" then
1594		local fh = assert(io.open(target, "w+"))
1595		if fh == nil then
1596			abort(1, "Failed to open '" .. target .. "'")
1597		end
1598		assert(fh:write(read_file(v)))
1599		assert(fh:close())
1600	end
1601end
1602
1603cleanup()
1604