xref: /freebsd/sys/tools/syscalls/core/syscall.lua (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1--
2-- SPDX-License-Identifier: BSD-2-Clause
3--
4-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
5-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
6-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
7--
8
9local config = require("config")
10local scarg = require("core.scarg")
11local scret = require("core.scret")
12local util = require("tools.util")
13
14local syscall = {}
15
16syscall.__index = syscall
17
18syscall.known_flags = util.set {
19	"STD",
20	"OBSOL",
21	"RESERVED",
22	"UNIMPL",
23	"NODEF",
24	"NOARGS",
25	"NOPROTO",
26	"NOSTD",
27
28	-- flags beyond this point are modifiers
29	"CAPENABLED",
30	"NOLIB",
31	"NOTSTATIC",
32	"SYSMUX",
33}
34
35-- Native is an arbitrarily large number to have a constant and not
36-- interfere with compat numbers.
37local native = 1000000
38
39-- Processes and assigns the appropriate thread flag for this system call.
40function syscall:processThr()
41	self.thr = "SY_THR_STATIC"
42	for k, _ in pairs(self.type) do
43		if k == "NOTSTATIC" then
44			self.thr = "SY_THR_ABSENT"
45		end
46	end
47end
48
49-- Processes and assigns the appropriate capability flag for this system call.
50-- "SYF_CAPENABLED" for capability enabled; "0" for NOT capability enabled.
51function syscall:processCap()
52	self.cap = "0"
53	local stripped = util.stripAbiPrefix(self.name, self.prefix)
54	for k, _ in pairs(self.type) do
55		if k == "CAPENABLED" then
56			self.cap = "SYF_CAPENABLED"
57		end
58	end
59end
60
61-- Check that this system call has a known type.
62local function checkType(type)
63	for k, _ in pairs(type) do
64		if not syscall.known_flags[k] and not
65			k:match("^COMPAT") then
66			util.abort(1, "Bad type: " .. k)
67		end
68	end
69end
70
71-- If there are ABI changes from native, process this system call to match the
72-- target ABI.
73function syscall:processChangesAbi()
74	-- First, confirm we want to uphold our changes_abi flag.
75	if config.syscall_no_abi_change[self.name] then
76		self.changes_abi = false
77	end
78	self.noproto = not util.isEmpty(config.abi_flags) and
79	    not self.changes_abi
80	if config.abiChanges("pointer_args") then
81		for _, v in ipairs(self.args) do
82			if util.isPtrType(v.type, config.abi_intptr_t) then
83				if config.syscall_no_abi_change[self.name] then
84					print("WARNING: " .. self.name ..
85					    " in syscall_no_abi_change, " ..
86					    "but pointers args are present")
87				end
88				self.changes_abi = true
89				goto ptrfound
90			end
91		end
92		::ptrfound::
93	end
94	if config.syscall_abi_change[self.name] then
95		self.changes_abi = true
96	end
97	if self.changes_abi then
98		self.noproto = false
99	end
100end
101
102-- Final processing of flags. Process any flags that haven't already been
103-- processed (e.g., dictionaries from syscalls.conf).
104function syscall:processFlags()
105	if config.obsol[self.name] or (self:compatLevel() > 0 and
106	    self:compatLevel() < tonumber(config.mincompat)) then
107		self.args = nil
108		self.type.OBSOL = true
109		-- Don't apply any ABI handling, declared as obsolete.
110		self.changes_abi = false
111	end
112	if config.unimpl[self.name] then
113		self.type.UNIMPL = true
114	end
115	if self.noproto or self.type.SYSMUX then
116		self.type.NOPROTO = true
117	end
118	if self.type.NODEF then
119		self.audit = "AUE_NULL"
120	end
121end
122
123-- Returns TRUE if prefix and arg_prefix are assigned; FALSE if they're left
124-- unassigned.  Relies on a valid changes_abi flag, so should be called AFTER
125-- processChangesAbi().
126function syscall:processPrefix()
127	-- If there are ABI changes from native, assign the correct prefixes.
128	if self.changes_abi then
129		self.arg_prefix = config.abi_func_prefix
130		self.prefix = config.abi_func_prefix
131		return true
132	end
133	return false
134end
135
136-- Validate that we're not skipping system calls by comparing this system call
137-- number to the previous system call number.  Called higher up the call stack
138-- by class FreeBSDSyscall.
139function syscall:validate(prev)
140	return prev + 1 == self.num
141end
142
143-- Return the compat prefix for this system call.
144function syscall:compatPrefix()
145	local c = self:compatLevel()
146	if self.type.OBSOL then
147		return "obs_"
148	end
149	if self.type.RESERVED then
150		return "reserved #"
151	end
152	if self.type.UNIMPL then
153		return "unimp_"
154	end
155	if c == 3 then
156		return "o"
157	end
158	if c < native then
159		return "freebsd" .. tostring(c) .. "_"
160	end
161	return ""
162end
163
164-- Return the symbol name for this system call.
165function syscall:symbol()
166	return self:compatPrefix() .. self.name
167end
168
169--
170-- Return the compatibility level for this system call.
171-- 	0 is obsolete.
172-- 	< 0 is this isn't really a system call we care about.
173-- 	3 is 4.3BSD in theory, but anything before FreeBSD 4.
174-- 	>= 4 is FreeBSD version, this system call was replaced with a new
175-- 	    version.
176--
177function syscall:compatLevel()
178	if self.type.UNIMPL or self.type.RESERVED then
179		return -1
180	elseif self.type.OBSOL then
181		return 0
182	elseif self.type.COMPAT then
183		return 3
184	end
185	for k, _ in pairs(self.type) do
186		local l = k:match("^COMPAT(%d+)")
187		if l ~= nil then
188			return tonumber(l)
189		end
190	end
191	return native
192end
193
194-- Adds the definition for this system call. Guarded by whether we already have
195-- a system call number or not.
196function syscall:addDef(line)
197	if self.num == nil then
198		local words = util.split(line, "%S+")
199		self.num = words[1]
200		self.audit = words[2]
201		self.type = util.setFromString(words[3], "[^|]+")
202		checkType(self.type)
203		self.name = words[4]
204		-- These next three are optional, and either all present
205		-- or all absent.
206		self.altname = words[5]
207		self.alttag = words[6]
208		self.rettype = words[7]
209		return true
210	end
211	return false
212end
213
214-- Adds the function declaration for this system call. If addDef() found an
215-- opening curly brace, then we're looking for a function declaration.
216function syscall:addFunc(line)
217	if self.name == "{" then
218		local words = util.split(line, "%S+")
219		-- Expect line is `type syscall(` or `type syscall(void);`.
220		if #words ~= 2 then
221			util.abort(1, "Malformed line " .. line)
222		end
223
224		local ret = scret:new({}, line)
225		self.ret = ret:add()
226		-- Don't clobber rettype set in the alt information.
227		if self.rettype == nil then
228			self.rettype = "int"
229		end
230
231		self.name = words[2]:match("([%w_]+)%(")
232		if words[2]:match("%);$") then
233			-- Now we're looking for ending curly brace.
234			self.expect_rbrace = true
235		end
236		return true
237	end
238	return false
239end
240
241-- Adds the argument(s) for this system call. Once addFunc() assigns a name
242-- for this system call, arguments are next in syscalls.master.
243function syscall:addArgs(line)
244	if not self.expect_rbrace then
245		if line:match("%);$") then
246			self.expect_rbrace = true
247			return true
248		end
249		local arg = scarg:new({}, line)
250		-- We don't want to add this argument if it doesn't process.
251		-- scarg:process() handles those conditions.
252		if arg:process() then
253			arg:append(self.args)
254		end
255		-- If this argument has ABI changes, set globally for this
256		-- system call.
257		self.changes_abi = self.changes_abi or arg:changesAbi()
258		return true
259	end
260	return false
261end
262
263-- Once we have a good syscall, add some final information to it.
264function syscall:finalize()
265	if self.name == nil then
266		self.name = ""
267	end
268
269	-- Preserve the original name as the alias.
270	self.alias = self.name
271
272	self:processChangesAbi()	-- process changes to the ABI
273	self:processFlags()		-- process any unprocessed flags
274
275	-- If there's changes to the ABI, these prefixes will be changed by
276	-- processPrefix(); otherwise, they'll remain empty.
277	self.prefix = ""
278	self.arg_prefix = ""
279	self:processPrefix()
280
281	self:processCap()	-- capability flag
282	self:processThr()	-- thread flag
283
284	-- Assign argument alias.
285	if self.alttag ~= nil then
286		self.arg_alias = self.alttag
287	elseif self.arg_alias == nil and self.name ~= nil then
288		-- argalias should be:
289		--   COMPAT_PREFIX + ABI Prefix + funcname
290		self.arg_alias = self:compatPrefix() .. self.arg_prefix ..
291		    self.name .. "_args"
292	elseif self.arg_alias ~= nil then
293		self.arg_alias = self.arg_prefix .. self.arg_alias
294	end
295
296	-- An empty string would not want a prefix; the entry doesn't have
297	-- a name so we want to keep the empty string.
298	if self.name ~= nil and self.name ~= "" then
299		self.name = self.prefix .. self.name
300	end
301
302	self:processArgstrings()
303	self:processArgsize()
304end
305
306-- Assigns the correct args_size. Defaults to "0", except if there's arguments
307-- or NODEF flag.
308function syscall:processArgsize()
309	if self.type.SYSMUX then	-- catch this first
310		self.args_size = "0"
311	elseif self.arg_alias ~= nil and
312	    (#self.args ~= 0 or self.type.NODEF) then
313		self.args_size = "AS(" .. self.arg_alias .. ")"
314	else
315		self.args_size = "0"
316	end
317end
318
319-- Constructs argstr_* strings for generated declerations/wrappers.
320function syscall:processArgstrings()
321	local type = ""
322	local type_var = ""
323	local var = ""
324	local comma = ""
325
326	for _, v in ipairs(self.args) do
327		local argname, argtype = v.name, v.type
328		type = type .. comma .. argtype
329		type_var = type_var .. comma .. argtype ..  " " .. argname
330		var = var .. comma .. argname
331		comma = ", "
332	end
333	if type == "" then
334		type = "void"
335		type_var = "void"
336	end
337
338	self.argstr_type = type
339	self.argstr_type_var = type_var
340	self.argstr_var = var
341end
342
343-- Interface to add this system call to the master system call table.
344-- The system call is built up one line at a time. The states describe the
345-- current parsing state.
346-- Returns TRUE when ready to add and FALSE while still parsing.
347function syscall:add(line)
348	if self:addDef(line) then
349		return self:isAdded(line)
350	end
351	if self:addFunc(line) then
352		return false -- Function added; keep going.
353	end
354	if self:addArgs(line) then
355		return false -- Arguments added; keep going.
356	end
357	return self:isAdded(line) -- Final validation, before adding.
358end
359
360-- Returns TRUE if this system call was succesfully added. There's two entry
361-- points to this function: (1) the entry in syscalls.master is one-line, or
362-- (2) the entry is a full system call. This function handles those cases and
363-- decides whether to exit early for (1) or validate a full system call for
364-- (2).  This function also handles cases where we don't want to add, and
365-- instead want to abort.
366function syscall:isAdded(line)
367	-- This system call is a range - exit early.
368	if tonumber(self.num) == nil then
369		-- The only allowed types are RESERVED and UNIMPL.
370		if not (self.type.RESERVED or self.type.UNIMPL) then
371			util.abort(1, "Range only allowed with RESERVED " ..
372			    "and UNIMPL: " ..  line)
373		end
374		self:finalize()
375		return true
376	-- This system call is a loadable system call - exit early.
377	elseif self.altname ~= nil and self.alttag ~= nil and
378		   self.rettype ~= nil then
379		self:finalize()
380		return true
381	-- This system call is only one line, and should only be one line
382	-- (we didn't make it to addFunc()) - exit early.
383	elseif self.name ~= "{"  and self.ret == nil then
384		self:finalize()
385		return true
386	-- This is a full system call and we've passed multiple states to
387	-- get here - final exit.
388	elseif self.expect_rbrace then
389		if not line:match("}$") then
390			util.abort(1, "Expected '}' found '" .. line ..
391			    "' instead.")
392		end
393		self:finalize()
394		return true
395	end
396	return false
397end
398
399-- Return TRUE if this system call is native.
400function syscall:native()
401	return self:compatLevel() == native
402end
403
404-- Make a shallow copy of `self` and replace the system call number with num
405-- (which should be a number).
406-- For system call ranges.
407function syscall:shallowCopy(num)
408	local obj = syscall:new()
409
410	-- shallow copy
411	for k, v in pairs(self) do
412		obj[k] = v
413	end
414	obj.num = num	-- except override range
415	return obj
416end
417
418-- Make a deep copy of the parameter object. Save copied tables in `copies`,
419-- indexed by original table.
420-- CREDIT: http://lua-users.org/wiki/CopyTable
421-- For a full system call (the nested arguments table should be a deep copy).
422local function deepCopy(orig, copies)
423	copies = copies or {}
424	local orig_type = type(orig)
425	local copy
426	if orig_type == 'table' then
427		if copies[orig] then
428			copy = copies[orig]
429		else
430			copy = {}
431			copies[orig] = copy
432			for orig_key, orig_value in next, orig, nil do
433				copy[deepCopy(orig_key, copies)] =
434				    deepCopy(orig_value, copies)
435			end
436			setmetatable(copy, deepCopy(getmetatable(orig), copies))
437		end
438	else -- number, string, boolean, etc
439		copy = orig
440	end
441	return copy
442end
443
444--
445-- In syscalls.master, system calls come in two types: (1) a fully defined
446-- system call with function declaration, with a distinct number for each system
447-- call; or (2) a one-line entry, sometimes with a distinct number and sometimes
448-- with a range of numbers. One-line entries can be obsolete, reserved, no
449-- definition, etc. Ranges are only allowed for reserved and unimplemented.
450--
451-- This function provides the iterator to traverse system calls by number. If
452-- the entry is a fully defined system call with a distinct number, the iterator
453-- creates a deep copy and captures any nested objects; if the entry is a range
454-- of numbers, the iterator creates shallow copies from the start of the range
455-- to the end of the range.
456--
457function syscall:iter()
458	local s = tonumber(self.num)
459	local e
460	if s == nil then
461		s, e = string.match(self.num, "(%d+)%-(%d+)")
462		s, e = tonumber(s), tonumber(e)
463		return function ()
464			if s <= e then
465				s = s + 1
466				return self:shallowCopy(s - 1)
467			end
468		end
469	else
470		e = s
471		self.num = s	-- Replace string with number, like the clones.
472		return function ()
473			if s == e then
474				local deep_copy = deepCopy(self)
475				s = e + 1
476				return deep_copy
477			end
478		end
479	end
480end
481
482function syscall:new(obj)
483	obj = obj or { }
484	setmetatable(obj, self)
485	self.__index = self
486
487	self.expect_rbrace = false
488	self.changes_abi = false
489	self.args = {}
490	self.noproto = false
491
492	return obj
493end
494
495return syscall
496