summaryrefslogtreecommitdiff
path: root/sys/tools/syscalls/config.lua
blob: 91a8a5af68dd8a0e241b77d553408e4f1fd8fa08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
--
-- SPDX-License-Identifier: BSD-2-Clause
--
-- Copyright (c) 2021-2024 SRI International
-- Copyright (c) 2024 Tyler Baxter <agge@FreeBSD.org>
-- Copyright (c) 2023 Warner Losh <imp@bsdimp.com>
-- Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
--

--
-- Code to read in the config file that drives this. Since we inherit from the
-- FreeBSD makesyscall.sh legacy, all config is done through a config file that
-- sets a number of variables (as noted below); it used to be a .sh file that
-- was sourced in. This dodges the need to write a command line parser.
--

local util = require("tools.util")

--
-- Global config map.
-- Default configuration is native. Any of these may get replaced by an
-- optionally specified configuration file.
--
local config = {
	sysnames = "syscalls.c",
	syshdr = "../sys/syscall.h",
	syshdr_extra = nil;
	sysmk = "/dev/null",
	syssw = "init_sysent.c",
	systrace = "systrace_args.c",
	sysproto = "../sys/sysproto.h",
	libsysmap = "/dev/null",
	libsys_h = "/dev/null",
	sysproto_h = "_SYS_SYSPROTO_H_",
	syscallprefix = "SYS_",
	switchname = "sysent",
	namesname = "syscallnames",
	abi_flags = {},
	abi_func_prefix = "",
	abi_type_suffix = "",
	abi_long = "long",
	abi_u_long = "u_long",
	abi_semid_t = "semid_t",
	abi_size_t = "size_t",
	abi_ptr_array_t = "",
	abi_headers = "",
	abi_intptr_t = "intptr_t",
	ptr_intptr_t_cast = "intptr_t",
	obsol = {},
	unimpl = {},
	compat_set = "native",
	mincompat = 0,
	-- System calls that require ABI-specific handling.
	syscall_abi_change = {},
	-- System calls that appear to require handling, but don't.
	syscall_no_abi_change = {},
	-- Keep track of modifications if there are.
	modifications = {},
	-- Stores compat_sets from syscalls.conf; config.mergeCompat()
	-- instantiates.
	compat_options = {},
}

--
-- For each entry, the ABI flag is the key. One may also optionally provide an
-- expr, which are contained in an array associated with each key; expr gets
-- applied to each argument type to indicate whether this argument is subject to
-- ABI change given the configured flags.
--
config.known_abi_flags = {
	long_size = {
		"_Contains[a-z_]*_long_",
		"^long [a-z0-9_]+$",
		"long [*]",
		"size_t [*]",
		-- semid_t is not included because it is only used
		-- as an argument or written out individually and
		-- said writes are handled by the ksem framework.
		-- Technically a sign-extension issue exists for
		-- arguments, but because semid_t is actually a file
		-- descriptor negative 32-bit values are invalid
		-- regardless of sign-extension.
	},
	time_t_size = {
		"_Contains[a-z_]*_timet_",
	},
	pointer_args = {
		-- no expr
	},
	pointer_size = {
		"_Contains[a-z_]*_ptr_",
		"[*][*]",
	},
	pair_64bit = {
		"^dev_t[ ]*$",
		"^id_t[ ]*$",
		"^off_t[ ]*$",
	},
}

-- All compat option entries should have five entries:
--	definition: The preprocessor macro that will be set for this.
--	compatlevel: The level this compatibility should be included at. This
--	    generally represents the version of FreeBSD that it is compatible 
--	    with, but ultimately it's just the level of mincompat in which it's
--	    included.
--	flag: The name of the flag in syscalls.master.
--	prefix: The prefix to use for _args and syscall prototype.  This will be
--	    used as-is, without "_" or any other character appended.
--	descr: The description of this compat option in init_sysent.c comments.
-- The special "stdcompat" entry will cause the other five to be autogenerated.
local compat_option_sets = {
	native = {
		{
			definition = "COMPAT_43",
			compatlevel = 3,
			flag = "COMPAT",
			prefix = "o",
			descr = "old",
		},
		{ stdcompat = "FREEBSD4" },
		{ stdcompat = "FREEBSD6" },
		{ stdcompat = "FREEBSD7" },
		{ stdcompat = "FREEBSD10" },
		{ stdcompat = "FREEBSD11" },
		{ stdcompat = "FREEBSD12" },
		{ stdcompat = "FREEBSD13" },
		{ stdcompat = "FREEBSD14" },
	},
}

--
-- config looks like a shell script; in fact, the previous makesyscalls.sh
-- script actually sourced it in.  It had a pretty common format, so we should
-- be fine to make various assumptions.
--
-- This function processes config to be merged into our global config map with
-- config.merge(). It aborts if there's malformed lines and returns NIL and a
-- message if no file was provided.
--
function config.process(file)
	local cfg = {}
	local comment_line_expr = "^%s*#.*"
	-- We capture any whitespace padding here so we can easily advance to
	-- the end of the line as needed to check for any trailing bogus bits.
	-- Alternatively, we could drop the whitespace and instead try to
	-- use a pattern to strip out the meaty part of the line, but then we
	-- would need to sanitize the line for potentially special characters.
	local line_expr = "^([%w%p]+%s*)=(%s*[`\"]?[^\"`]*[`\"]?)"

	if not file then
		return nil, "No file given"
	end

	local fh = assert(io.open(file))

	for nextline in fh:lines() do
		-- Strip any whole-line comments.
		nextline = nextline:gsub(comment_line_expr, "")
		-- Parse it into key, value pairs.
		local key, value = nextline:match(line_expr)
		if key ~= nil and value ~= nil then
			local kvp = key .. "=" .. value
			key = util.trim(key)
			value = util.trim(value)
			local delim = value:sub(1,1)
			if delim == '"' then
				local trailing_context

				-- Strip off the key/value part.
				trailing_context = nextline:sub(kvp:len() + 1)
				-- Strip off any trailing comment.
				trailing_context = trailing_context:gsub("#.*$",
				    "")
				-- Strip off leading/trailing whitespace.
				trailing_context = util.trim(trailing_context)
				if trailing_context ~= "" then
					print(trailing_context)
					util.abort(1,
					    "Malformed line: " .. nextline)
				end

				value = util.trim(value, delim)
			else
				-- Strip off potential comments.
				value = value:gsub("#.*$", "")
				-- Strip off any padding whitespace.
				value = util.trim(value)
				if value:match("%s") then
					util.abort(1,
					    "Malformed config line: " ..
					    nextline)
				end
			end
			cfg[key] = value
		elseif not nextline:match("^%s*$") then
			-- Make sure format violations don't get overlooked
			-- here, but ignore blank lines.  Comments are already
			-- stripped above.
			util.abort(1, "Malformed config line: " .. nextline)
		end
	end

	assert(fh:close())
	return cfg
end

-- Merges processed configuration file into the global config map (see above),
-- or returns NIL and a message if no file was provided.
function config.merge(fh)
	if not fh then
		return nil, "No file given"
	end

	local res = assert(config.process(fh))

	for k, v in pairs(res) do
		if v ~= config[k] then
			-- Handling of string lists:
			if k:find("abi_flags") then
				-- Match for pipe, that's how abi_flags
				-- is formatted.
				config[k] = util.setFromString(v, "[^|]+")
			elseif k:find("syscall_abi_change") or
			    k:find("syscall_no_abi_change") or
			    k:find("obsol") or
			    k:find("unimpl") then
				-- Match for space, that's how these
				-- are formatted.
				config[k] = util.setFromString(v, "[^ ]+")
			else
				config[k] = v
			end
			-- Construct config modified table as config
			-- is processed.
			config.modifications[k] = true
		else
			-- config wasn't modified.
			config.modifications[k] = false
		end
	end
end

-- Returns TRUE if there are ABI changes from native for the provided ABI flag.
function config.abiChanges(name)
	if config.known_abi_flags[name] == nil then
		util.abort(1, "abi_changes: unknown flag: " .. name)
	end
	return config.abi_flags[name] ~= nil
end

-- Instantiates config.compat_options.
function config.mergeCompat()
	if config.compat_set ~= "" then
		if not compat_option_sets[config.compat_set] then
			util.abort(1, "Undefined compat set: " ..
			    config.compat_set)
		end

		config.compat_options = compat_option_sets[config.compat_set]
	end
end

return config