xref: /freebsd/stand/lua/core.lua (revision 7efc058f76fa7af45860d864f4f9cd93b2c35de4)
1088b4f5fSWarner Losh--
2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3088b4f5fSWarner Losh-- All rights reserved.
4088b4f5fSWarner Losh--
5088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
6088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
7088b4f5fSWarner Losh-- are met:
8088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
9088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
10088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
11088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
12088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
13088b4f5fSWarner Losh--
14088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24088b4f5fSWarner Losh-- SUCH DAMAGE.
25088b4f5fSWarner Losh--
26088b4f5fSWarner Losh-- $FreeBSD$
27088b4f5fSWarner Losh--
28088b4f5fSWarner Losh
29aedd6be5SKyle Evanslocal config = require('config')
30fa4a2394SKyle Evans
31aedd6be5SKyle Evanslocal core = {}
32088b4f5fSWarner Losh
336d4ed94dSKyle Evanslocal compose_loader_cmd = function(cmd_name, argstr)
349f71d421SKyle Evans	if argstr ~= nil then
35aedd6be5SKyle Evans		cmd_name = cmd_name .. " " .. argstr
366d4ed94dSKyle Evans	end
37aedd6be5SKyle Evans	return cmd_name
386d4ed94dSKyle Evansend
396d4ed94dSKyle Evans
406d3bcc06SKyle Evans-- Internal function
41f0cb3b6bSKyle Evans-- Parses arguments to boot and returns two values: kernel_name, argstr
42f0cb3b6bSKyle Evans-- Defaults to nil and "" respectively.
436d3bcc06SKyle Evans-- This will also parse arguments to autoboot, but the with_kernel argument
446d3bcc06SKyle Evans-- will need to be explicitly overwritten to false
456d3bcc06SKyle Evanslocal parse_boot_args = function(argv, with_kernel)
469f71d421SKyle Evans	if with_kernel == nil then
47aedd6be5SKyle Evans		with_kernel = true
486d3bcc06SKyle Evans	end
491d38e553SKyle Evans	if #argv == 0 then
501d38e553SKyle Evans		if with_kernel then
511d38e553SKyle Evans			return nil, ""
521d38e553SKyle Evans		else
531d38e553SKyle Evans			return ""
541d38e553SKyle Evans		end
551d38e553SKyle Evans	end
56aedd6be5SKyle Evans	local kernel_name
57aedd6be5SKyle Evans	local argstr = ""
58f0cb3b6bSKyle Evans
59f0cb3b6bSKyle Evans	for k, v in ipairs(argv) do
609f71d421SKyle Evans		if with_kernel and v:sub(1,1) ~= "-" then
61aedd6be5SKyle Evans			kernel_name = v
62f0cb3b6bSKyle Evans		else
63aedd6be5SKyle Evans			argstr = argstr .. " " .. v
64f0cb3b6bSKyle Evans		end
65f0cb3b6bSKyle Evans	end
669f71d421SKyle Evans	if with_kernel then
67aedd6be5SKyle Evans		return kernel_name, argstr
686d3bcc06SKyle Evans	else
69aedd6be5SKyle Evans		return argstr
706d3bcc06SKyle Evans	end
71f0cb3b6bSKyle Evansend
72f0cb3b6bSKyle Evans
73f0cb3b6bSKyle Evans-- Globals
74f0cb3b6bSKyle Evansfunction boot(...)
75aedd6be5SKyle Evans	local argv = {...}
76aedd6be5SKyle Evans	local cmd_name = ""
77aedd6be5SKyle Evans	cmd_name, argv = core.popFrontTable(argv)
78aedd6be5SKyle Evans	local kernel, argstr = parse_boot_args(argv)
799f71d421SKyle Evans	if kernel ~= nil then
80aedd6be5SKyle Evans		loader.perform("unload")
81aedd6be5SKyle Evans		config.selectkernel(kernel)
82f0cb3b6bSKyle Evans	end
83aedd6be5SKyle Evans	core.boot(argstr)
84f0cb3b6bSKyle Evansend
85f0cb3b6bSKyle Evans
866d3bcc06SKyle Evansfunction autoboot(...)
876d3bcc06SKyle Evans	local argv = {...}
88aedd6be5SKyle Evans	local cmd_name = ""
89aedd6be5SKyle Evans	cmd_name, argv = core.popFrontTable(argv)
90aedd6be5SKyle Evans	local argstr = parse_boot_args(argv, false)
91aedd6be5SKyle Evans	core.autoboot(argstr)
926d3bcc06SKyle Evansend
936d3bcc06SKyle Evans
94b5746545SKyle Evans-- Module exports
95fe672a15SKyle Evans-- Commonly appearing constants
96aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
97aedd6be5SKyle Evanscore.KEY_ENTER		= 13
98aedd6be5SKyle Evanscore.KEY_DELETE		= 127
99fe672a15SKyle Evans
100aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
10139006570SKyle Evans
102aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
103aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
104aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
105aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
106aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
107a7cf0562SKyle Evans
108088b4f5fSWarner Loshfunction core.setVerbose(b)
1099f71d421SKyle Evans	if b == nil then
110aedd6be5SKyle Evans		b = not core.verbose
111088b4f5fSWarner Losh	end
112088b4f5fSWarner Losh
11349550489SKyle Evans	if b then
114aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
115088b4f5fSWarner Losh	else
116aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
117088b4f5fSWarner Losh	end
118aedd6be5SKyle Evans	core.verbose = b
119088b4f5fSWarner Loshend
120088b4f5fSWarner Losh
121088b4f5fSWarner Loshfunction core.setSingleUser(b)
1229f71d421SKyle Evans	if b == nil then
123aedd6be5SKyle Evans		b = not core.su
124088b4f5fSWarner Losh	end
125088b4f5fSWarner Losh
12649550489SKyle Evans	if b then
127aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
128088b4f5fSWarner Losh	else
129aedd6be5SKyle Evans		loader.unsetenv("boot_single")
130088b4f5fSWarner Losh	end
131aedd6be5SKyle Evans	core.su = b
132088b4f5fSWarner Loshend
133088b4f5fSWarner Losh
1346401094fSKyle Evansfunction core.getACPIPresent(checkingSystemDefaults)
135aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
1366401094fSKyle Evans
1379f71d421SKyle Evans	if c ~= nil then
13849550489SKyle Evans		if checkingSystemDefaults then
139aedd6be5SKyle Evans			return true
1406401094fSKyle Evans		end
1416401094fSKyle Evans		-- Otherwise, respect disabled if it's set
142aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
1439f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
1446401094fSKyle Evans	end
145aedd6be5SKyle Evans	return false
1466401094fSKyle Evansend
1476401094fSKyle Evans
148088b4f5fSWarner Loshfunction core.setACPI(b)
1499f71d421SKyle Evans	if b == nil then
150aedd6be5SKyle Evans		b = not core.acpi
151088b4f5fSWarner Losh	end
152088b4f5fSWarner Losh
15349550489SKyle Evans	if b then
154aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
155aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
156aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
157088b4f5fSWarner Losh	else
158aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
159aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
160aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
161088b4f5fSWarner Losh	end
162aedd6be5SKyle Evans	core.acpi = b
163088b4f5fSWarner Loshend
164088b4f5fSWarner Losh
165088b4f5fSWarner Loshfunction core.setSafeMode(b)
1669f71d421SKyle Evans	if b == nil then
167aedd6be5SKyle Evans		b = not core.sm
168088b4f5fSWarner Losh	end
16949550489SKyle Evans	if b then
170aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
171aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
172aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
173aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
174aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
175aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
176aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
177088b4f5fSWarner Losh	else
178aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
179aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
180aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
181aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
182aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
183aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
184aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
185088b4f5fSWarner Losh	end
186aedd6be5SKyle Evans	core.sm = b
187088b4f5fSWarner Loshend
188088b4f5fSWarner Losh
189088b4f5fSWarner Loshfunction core.kernelList()
190aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1913f4eb56bSKyle Evans	local v = loader.getenv("kernels")
192088b4f5fSWarner Losh
193aedd6be5SKyle Evans	local kernels = {}
194aedd6be5SKyle Evans	local unique = {}
195aedd6be5SKyle Evans	local i = 0
1969f71d421SKyle Evans	if k ~= nil then
197aedd6be5SKyle Evans		i = i + 1
198aedd6be5SKyle Evans		kernels[i] = k
199aedd6be5SKyle Evans		unique[k] = true
200088b4f5fSWarner Losh	end
201088b4f5fSWarner Losh
2023f4eb56bSKyle Evans	if v ~= nil then
203088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
2049f71d421SKyle Evans			if unique[n] == nil then
205aedd6be5SKyle Evans				i = i + 1
206aedd6be5SKyle Evans				kernels[i] = n
207aedd6be5SKyle Evans				unique[n] = true
208088b4f5fSWarner Losh			end
209088b4f5fSWarner Losh		end
210a108046fSConrad Meyer
2113f4eb56bSKyle Evans		-- We will not automatically detect kernels to be displayed if
2123f4eb56bSKyle Evans		-- loader.conf(5) explicitly set 'kernels'.
2133f4eb56bSKyle Evans		return kernels
2143f4eb56bSKyle Evans	end
2153f4eb56bSKyle Evans
216a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
217a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
218a108046fSConrad Meyer	-- named "kernel" is considered eligible.
219a108046fSConrad Meyer	for file in lfs.dir("/boot") do
220aedd6be5SKyle Evans		local fname = "/boot/" .. file
221a108046fSConrad Meyer
2229f71d421SKyle Evans		if file == "." or file == ".." then
223aedd6be5SKyle Evans			goto continue
224a108046fSConrad Meyer		end
225a108046fSConrad Meyer
2269f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
227aedd6be5SKyle Evans			goto continue
228a108046fSConrad Meyer		end
229a108046fSConrad Meyer
2309f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
231aedd6be5SKyle Evans			goto continue
232a108046fSConrad Meyer		end
233a108046fSConrad Meyer
2349f71d421SKyle Evans		if unique[file] == nil then
235aedd6be5SKyle Evans			i = i + 1
236aedd6be5SKyle Evans			kernels[i] = file
237aedd6be5SKyle Evans			unique[file] = true
238a108046fSConrad Meyer		end
239a108046fSConrad Meyer
240a108046fSConrad Meyer		::continue::
241a108046fSConrad Meyer	end
242aedd6be5SKyle Evans	return kernels
243088b4f5fSWarner Loshend
244088b4f5fSWarner Losh
245*7efc058fSKyle Evansfunction core.bootenvDefault()
246*7efc058fSKyle Evans	return loader.getenv("zfs_be_active")
247*7efc058fSKyle Evansend
248*7efc058fSKyle Evans
249*7efc058fSKyle Evansfunction core.bootenvList()
250*7efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
251*7efc058fSKyle Evans	local bootenvs = {}
252*7efc058fSKyle Evans	local curenv
253*7efc058fSKyle Evans	local curenv_idx = 0
254*7efc058fSKyle Evans	local envcount = 0
255*7efc058fSKyle Evans	local unique = {}
256*7efc058fSKyle Evans
257*7efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
258*7efc058fSKyle Evans		return bootenvs
259*7efc058fSKyle Evans	end
260*7efc058fSKyle Evans
261*7efc058fSKyle Evans	-- Currently selected bootenv is always first/default
262*7efc058fSKyle Evans	curenv = core.bootenvDefault()
263*7efc058fSKyle Evans	if curenv ~= nil then
264*7efc058fSKyle Evans		envcount = envcount + 1
265*7efc058fSKyle Evans		bootenvs[envcount] = curenv
266*7efc058fSKyle Evans		unique[curenv] = true
267*7efc058fSKyle Evans	end
268*7efc058fSKyle Evans
269*7efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
270*7efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
271*7efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
272*7efc058fSKyle Evans			envcount = envcount + 1
273*7efc058fSKyle Evans			bootenvs[envcount] = curenv
274*7efc058fSKyle Evans			unique[curenv] = true
275*7efc058fSKyle Evans		end
276*7efc058fSKyle Evans	end
277*7efc058fSKyle Evans	return bootenvs
278*7efc058fSKyle Evansend
279*7efc058fSKyle Evans
280088b4f5fSWarner Loshfunction core.setDefaults()
281aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
282aedd6be5SKyle Evans	core.setSafeMode(false)
283aedd6be5SKyle Evans	core.setSingleUser(false)
284aedd6be5SKyle Evans	core.setVerbose(false)
285088b4f5fSWarner Loshend
286088b4f5fSWarner Losh
2876d4ed94dSKyle Evansfunction core.autoboot(argstr)
288aedd6be5SKyle Evans	config.loadelf()
289aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("autoboot", argstr))
290088b4f5fSWarner Loshend
291088b4f5fSWarner Losh
2926d4ed94dSKyle Evansfunction core.boot(argstr)
293aedd6be5SKyle Evans	config.loadelf()
294aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("boot", argstr))
295088b4f5fSWarner Loshend
296088b4f5fSWarner Losh
297e07fc39cSKyle Evansfunction core.isSingleUserBoot()
298aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
299aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
300e07fc39cSKyle Evansend
301e07fc39cSKyle Evans
302*7efc058fSKyle Evansfunction core.isZFSBoot()
303*7efc058fSKyle Evans	local c = loader.getenv("currdev")
304*7efc058fSKyle Evans
305*7efc058fSKyle Evans	if c ~= nil then
306*7efc058fSKyle Evans		return c:match("^zfs:") ~= nil
307*7efc058fSKyle Evans	end
308*7efc058fSKyle Evans	return false
309*7efc058fSKyle Evansend
310*7efc058fSKyle Evans
311b140d14bSKyle Evansfunction core.isSerialBoot()
312aedd6be5SKyle Evans	local c = loader.getenv("console")
313088b4f5fSWarner Losh
3149f71d421SKyle Evans	if c ~= nil then
3159f71d421SKyle Evans		if c:find("comconsole") ~= nil then
316aedd6be5SKyle Evans			return true
317088b4f5fSWarner Losh		end
318088b4f5fSWarner Losh	end
319088b4f5fSWarner Losh
320aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
3219f71d421SKyle Evans	if s ~= nil then
322aedd6be5SKyle Evans		return true
323088b4f5fSWarner Losh	end
324088b4f5fSWarner Losh
325aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
3269f71d421SKyle Evans	if m ~= nil then
327aedd6be5SKyle Evans		return true
328088b4f5fSWarner Losh	end
329aedd6be5SKyle Evans	return false
330088b4f5fSWarner Loshend
331088b4f5fSWarner Losh
332c1ab36f5SKyle Evansfunction core.isSystem386()
3339f71d421SKyle Evans	return loader.machine_arch == "i386"
334c1ab36f5SKyle Evansend
335c1ab36f5SKyle Evans
3365c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
3375c1b5165SKyle Evansfunction core.shallowCopyTable(tbl)
338aedd6be5SKyle Evans	local new_tbl = {}
3395c1b5165SKyle Evans	for k, v in pairs(tbl) do
3409f71d421SKyle Evans		if type(v) == "table" then
341aedd6be5SKyle Evans			new_tbl[k] = core.shallowCopyTable(v)
3425c1b5165SKyle Evans		else
343aedd6be5SKyle Evans			new_tbl[k] = v
3445c1b5165SKyle Evans		end
3455c1b5165SKyle Evans	end
346aedd6be5SKyle Evans	return new_tbl
3475c1b5165SKyle Evansend
3485c1b5165SKyle Evans
3496d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3506d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3516d4ed94dSKyle Evans-- for our uses
3526d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3536d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3549f71d421SKyle Evans	if #tbl == 0 then
355aedd6be5SKyle Evans		return nil, nil
3569f71d421SKyle Evans	elseif #tbl == 1 then
357aedd6be5SKyle Evans		return tbl[1], {}
3586d4ed94dSKyle Evans	end
3596d4ed94dSKyle Evans
360aedd6be5SKyle Evans	local first_value = tbl[1]
361aedd6be5SKyle Evans	local new_tbl = {}
3626d4ed94dSKyle Evans	-- This is not a cheap operation
3636d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3649f71d421SKyle Evans		if k > 1 then
365aedd6be5SKyle Evans			new_tbl[k - 1] = v
3666d4ed94dSKyle Evans		end
3676d4ed94dSKyle Evans	end
3686d4ed94dSKyle Evans
369aedd6be5SKyle Evans	return first_value, new_tbl
3706d4ed94dSKyle Evansend
3716d4ed94dSKyle Evans
3726f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3736f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3746f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3756f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3769f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
377aedd6be5SKyle Evans	core.setACPI(true)
3786f412147SKyle Evansend
379aedd6be5SKyle Evansreturn core
380