xref: /freebsd/stand/lua/core.lua (revision 2bb86aefecd5d75e7c4f3197e7c9d4357e57511a)
1088b4f5fSWarner Losh--
272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
372e39d71SKyle Evans--
4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
5beaafe4fSKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6088b4f5fSWarner Losh-- All rights reserved.
7088b4f5fSWarner Losh--
8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
10088b4f5fSWarner Losh-- are met:
11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
14088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
15088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
16088b4f5fSWarner Losh--
17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27088b4f5fSWarner Losh-- SUCH DAMAGE.
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh-- $FreeBSD$
30088b4f5fSWarner Losh--
31088b4f5fSWarner Losh
32011eae6cSKyle Evanslocal config = require("config")
33fa4a2394SKyle Evans
34aedd6be5SKyle Evanslocal core = {}
35088b4f5fSWarner Losh
36322a2dddSKyle Evanslocal function composeLoaderCmd(cmd_name, argstr)
379f71d421SKyle Evans	if argstr ~= nil then
38aedd6be5SKyle Evans		cmd_name = cmd_name .. " " .. argstr
396d4ed94dSKyle Evans	end
40aedd6be5SKyle Evans	return cmd_name
416d4ed94dSKyle Evansend
426d4ed94dSKyle Evans
43b5746545SKyle Evans-- Module exports
44fe672a15SKyle Evans-- Commonly appearing constants
45aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
46aedd6be5SKyle Evanscore.KEY_ENTER		= 13
47aedd6be5SKyle Evanscore.KEY_DELETE		= 127
48fe672a15SKyle Evans
49aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
50*2bb86aefSKyle Evanscore.KEYSTR_CSI		= core.KEYSTR_ESCAPE .. "["
5139006570SKyle Evans
52aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
53aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
54aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
55aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
56aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
57a7cf0562SKyle Evans
5804af4229SKyle Evansfunction core.setVerbose(verbose)
5904af4229SKyle Evans	if verbose == nil then
6004af4229SKyle Evans		verbose = not core.verbose
61088b4f5fSWarner Losh	end
62088b4f5fSWarner Losh
6304af4229SKyle Evans	if verbose then
64aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
65088b4f5fSWarner Losh	else
66aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
67088b4f5fSWarner Losh	end
6804af4229SKyle Evans	core.verbose = verbose
69088b4f5fSWarner Loshend
70088b4f5fSWarner Losh
7104af4229SKyle Evansfunction core.setSingleUser(single_user)
7204af4229SKyle Evans	if single_user == nil then
7304af4229SKyle Evans		single_user = not core.su
74088b4f5fSWarner Losh	end
75088b4f5fSWarner Losh
7604af4229SKyle Evans	if single_user then
77aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
78088b4f5fSWarner Losh	else
79aedd6be5SKyle Evans		loader.unsetenv("boot_single")
80088b4f5fSWarner Losh	end
8104af4229SKyle Evans	core.su = single_user
82088b4f5fSWarner Loshend
83088b4f5fSWarner Losh
8404af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults)
85aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
866401094fSKyle Evans
879f71d421SKyle Evans	if c ~= nil then
8804af4229SKyle Evans		if checking_system_defaults then
89aedd6be5SKyle Evans			return true
906401094fSKyle Evans		end
916401094fSKyle Evans		-- Otherwise, respect disabled if it's set
92aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
939f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
946401094fSKyle Evans	end
95aedd6be5SKyle Evans	return false
966401094fSKyle Evansend
976401094fSKyle Evans
9804af4229SKyle Evansfunction core.setACPI(acpi)
9904af4229SKyle Evans	if acpi == nil then
10004af4229SKyle Evans		acpi = not core.acpi
101088b4f5fSWarner Losh	end
102088b4f5fSWarner Losh
10304af4229SKyle Evans	if acpi then
104aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
105aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
106aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
107088b4f5fSWarner Losh	else
108aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
109aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
110aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
111088b4f5fSWarner Losh	end
11204af4229SKyle Evans	core.acpi = acpi
113088b4f5fSWarner Loshend
114088b4f5fSWarner Losh
11504af4229SKyle Evansfunction core.setSafeMode(safe_mode)
11604af4229SKyle Evans	if safe_mode == nil then
11704af4229SKyle Evans		safe_mode = not core.sm
118088b4f5fSWarner Losh	end
11904af4229SKyle Evans	if safe_mode then
120aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
121aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
122aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
123aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
124aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
125aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
126aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
127088b4f5fSWarner Losh	else
128aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
129aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
130aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
131aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
132aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
133aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
134aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
135088b4f5fSWarner Losh	end
13604af4229SKyle Evans	core.sm = safe_mode
137088b4f5fSWarner Loshend
138088b4f5fSWarner Losh
139088b4f5fSWarner Loshfunction core.kernelList()
140aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1413f4eb56bSKyle Evans	local v = loader.getenv("kernels")
1423889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
143088b4f5fSWarner Losh
144aedd6be5SKyle Evans	local kernels = {}
145aedd6be5SKyle Evans	local unique = {}
146aedd6be5SKyle Evans	local i = 0
1479f71d421SKyle Evans	if k ~= nil then
148aedd6be5SKyle Evans		i = i + 1
149aedd6be5SKyle Evans		kernels[i] = k
150aedd6be5SKyle Evans		unique[k] = true
151088b4f5fSWarner Losh	end
152088b4f5fSWarner Losh
1533f4eb56bSKyle Evans	if v ~= nil then
154088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
1559f71d421SKyle Evans			if unique[n] == nil then
156aedd6be5SKyle Evans				i = i + 1
157aedd6be5SKyle Evans				kernels[i] = n
158aedd6be5SKyle Evans				unique[n] = true
159088b4f5fSWarner Losh			end
160088b4f5fSWarner Losh		end
1613889e6cdSKyle Evans	end
162a108046fSConrad Meyer
1633889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
1643889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
1653889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
1663889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
1673f4eb56bSKyle Evans		return kernels
1683f4eb56bSKyle Evans	end
1693f4eb56bSKyle Evans
170a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
171a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
172a108046fSConrad Meyer	-- named "kernel" is considered eligible.
173a108046fSConrad Meyer	for file in lfs.dir("/boot") do
174aedd6be5SKyle Evans		local fname = "/boot/" .. file
175a108046fSConrad Meyer
1769f71d421SKyle Evans		if file == "." or file == ".." then
177aedd6be5SKyle Evans			goto continue
178a108046fSConrad Meyer		end
179a108046fSConrad Meyer
1809f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
181aedd6be5SKyle Evans			goto continue
182a108046fSConrad Meyer		end
183a108046fSConrad Meyer
1849f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
185aedd6be5SKyle Evans			goto continue
186a108046fSConrad Meyer		end
187a108046fSConrad Meyer
1889f71d421SKyle Evans		if unique[file] == nil then
189aedd6be5SKyle Evans			i = i + 1
190aedd6be5SKyle Evans			kernels[i] = file
191aedd6be5SKyle Evans			unique[file] = true
192a108046fSConrad Meyer		end
193a108046fSConrad Meyer
194a108046fSConrad Meyer		::continue::
195a108046fSConrad Meyer	end
196aedd6be5SKyle Evans	return kernels
197088b4f5fSWarner Loshend
198088b4f5fSWarner Losh
1997efc058fSKyle Evansfunction core.bootenvDefault()
2007efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2017efc058fSKyle Evansend
2027efc058fSKyle Evans
2037efc058fSKyle Evansfunction core.bootenvList()
2047efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2057efc058fSKyle Evans	local bootenvs = {}
2067efc058fSKyle Evans	local curenv
2077efc058fSKyle Evans	local envcount = 0
2087efc058fSKyle Evans	local unique = {}
2097efc058fSKyle Evans
2107efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2117efc058fSKyle Evans		return bootenvs
2127efc058fSKyle Evans	end
2137efc058fSKyle Evans
2147efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2157efc058fSKyle Evans	curenv = core.bootenvDefault()
2167efc058fSKyle Evans	if curenv ~= nil then
2177efc058fSKyle Evans		envcount = envcount + 1
2187efc058fSKyle Evans		bootenvs[envcount] = curenv
2197efc058fSKyle Evans		unique[curenv] = true
2207efc058fSKyle Evans	end
2217efc058fSKyle Evans
2227efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2237efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2247efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2257efc058fSKyle Evans			envcount = envcount + 1
2267efc058fSKyle Evans			bootenvs[envcount] = curenv
2277efc058fSKyle Evans			unique[curenv] = true
2287efc058fSKyle Evans		end
2297efc058fSKyle Evans	end
2307efc058fSKyle Evans	return bootenvs
2317efc058fSKyle Evansend
2327efc058fSKyle Evans
233088b4f5fSWarner Loshfunction core.setDefaults()
234aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
235aedd6be5SKyle Evans	core.setSafeMode(false)
236aedd6be5SKyle Evans	core.setSingleUser(false)
237aedd6be5SKyle Evans	core.setVerbose(false)
238088b4f5fSWarner Loshend
239088b4f5fSWarner Losh
2406d4ed94dSKyle Evansfunction core.autoboot(argstr)
241aedd6be5SKyle Evans	config.loadelf()
242322a2dddSKyle Evans	loader.perform(composeLoaderCmd("autoboot", argstr))
243088b4f5fSWarner Loshend
244088b4f5fSWarner Losh
2456d4ed94dSKyle Evansfunction core.boot(argstr)
246aedd6be5SKyle Evans	config.loadelf()
247322a2dddSKyle Evans	loader.perform(composeLoaderCmd("boot", argstr))
248088b4f5fSWarner Loshend
249088b4f5fSWarner Losh
250e07fc39cSKyle Evansfunction core.isSingleUserBoot()
251aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
252aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
253e07fc39cSKyle Evansend
254e07fc39cSKyle Evans
2557efc058fSKyle Evansfunction core.isZFSBoot()
2567efc058fSKyle Evans	local c = loader.getenv("currdev")
2577efc058fSKyle Evans
2587efc058fSKyle Evans	if c ~= nil then
2597efc058fSKyle Evans		return c:match("^zfs:") ~= nil
2607efc058fSKyle Evans	end
2617efc058fSKyle Evans	return false
2627efc058fSKyle Evansend
2637efc058fSKyle Evans
264b140d14bSKyle Evansfunction core.isSerialBoot()
265aedd6be5SKyle Evans	local c = loader.getenv("console")
266088b4f5fSWarner Losh
2679f71d421SKyle Evans	if c ~= nil then
2689f71d421SKyle Evans		if c:find("comconsole") ~= nil then
269aedd6be5SKyle Evans			return true
270088b4f5fSWarner Losh		end
271088b4f5fSWarner Losh	end
272088b4f5fSWarner Losh
273aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
2749f71d421SKyle Evans	if s ~= nil then
275aedd6be5SKyle Evans		return true
276088b4f5fSWarner Losh	end
277088b4f5fSWarner Losh
278aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
2799f71d421SKyle Evans	if m ~= nil then
280aedd6be5SKyle Evans		return true
281088b4f5fSWarner Losh	end
282aedd6be5SKyle Evans	return false
283088b4f5fSWarner Loshend
284088b4f5fSWarner Losh
285c1ab36f5SKyle Evansfunction core.isSystem386()
2869f71d421SKyle Evans	return loader.machine_arch == "i386"
287c1ab36f5SKyle Evansend
288c1ab36f5SKyle Evans
2899937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted?
2909937e979SKyle Evansfunction core.isMenuSkipped()
2919937e979SKyle Evans	if core.isSerialBoot() then
2929937e979SKyle Evans		return true
2939937e979SKyle Evans	end
2949937e979SKyle Evans	local c = string.lower(loader.getenv("console") or "")
2959937e979SKyle Evans	if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then
2969937e979SKyle Evans		return true
2979937e979SKyle Evans	end
2989937e979SKyle Evans
2999937e979SKyle Evans	c = string.lower(loader.getenv("beastie_disable") or "")
3009937e979SKyle Evans	return c == "yes"
3019937e979SKyle Evansend
3029937e979SKyle Evans
3035c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
304ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
305aedd6be5SKyle Evans	local new_tbl = {}
3065c1b5165SKyle Evans	for k, v in pairs(tbl) do
3079f71d421SKyle Evans		if type(v) == "table" then
308ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
3095c1b5165SKyle Evans		else
310aedd6be5SKyle Evans			new_tbl[k] = v
3115c1b5165SKyle Evans		end
3125c1b5165SKyle Evans	end
313aedd6be5SKyle Evans	return new_tbl
3145c1b5165SKyle Evansend
3155c1b5165SKyle Evans
3166d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3176d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3186d4ed94dSKyle Evans-- for our uses
3196d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3206d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3219f71d421SKyle Evans	if #tbl == 0 then
322aedd6be5SKyle Evans		return nil, nil
3239f71d421SKyle Evans	elseif #tbl == 1 then
324aedd6be5SKyle Evans		return tbl[1], {}
3256d4ed94dSKyle Evans	end
3266d4ed94dSKyle Evans
327aedd6be5SKyle Evans	local first_value = tbl[1]
328aedd6be5SKyle Evans	local new_tbl = {}
3296d4ed94dSKyle Evans	-- This is not a cheap operation
3306d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3319f71d421SKyle Evans		if k > 1 then
332aedd6be5SKyle Evans			new_tbl[k - 1] = v
3336d4ed94dSKyle Evans		end
3346d4ed94dSKyle Evans	end
3356d4ed94dSKyle Evans
336aedd6be5SKyle Evans	return first_value, new_tbl
3376d4ed94dSKyle Evansend
3386d4ed94dSKyle Evans
3396f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3406f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3416f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3426f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3439f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
344aedd6be5SKyle Evans	core.setACPI(true)
3456f412147SKyle Evansend
346aedd6be5SKyle Evansreturn core
347