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