xref: /freebsd/stand/lua/core.lua (revision 35b0c718d312ae8067c598910886553c5b4da02a)
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
49230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in
50230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal'
51aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
522bb86aefSKyle Evanscore.KEYSTR_CSI		= core.KEYSTR_ESCAPE .. "["
5339006570SKyle Evans
54aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
55aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
56aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
57aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
58aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
59a7cf0562SKyle Evans
6004af4229SKyle Evansfunction core.setVerbose(verbose)
6104af4229SKyle Evans	if verbose == nil then
6204af4229SKyle Evans		verbose = not core.verbose
63088b4f5fSWarner Losh	end
64088b4f5fSWarner Losh
6504af4229SKyle Evans	if verbose then
66aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
67088b4f5fSWarner Losh	else
68aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
69088b4f5fSWarner Losh	end
7004af4229SKyle Evans	core.verbose = verbose
71088b4f5fSWarner Loshend
72088b4f5fSWarner Losh
7304af4229SKyle Evansfunction core.setSingleUser(single_user)
7404af4229SKyle Evans	if single_user == nil then
7504af4229SKyle Evans		single_user = not core.su
76088b4f5fSWarner Losh	end
77088b4f5fSWarner Losh
7804af4229SKyle Evans	if single_user then
79aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
80088b4f5fSWarner Losh	else
81aedd6be5SKyle Evans		loader.unsetenv("boot_single")
82088b4f5fSWarner Losh	end
8304af4229SKyle Evans	core.su = single_user
84088b4f5fSWarner Loshend
85088b4f5fSWarner Losh
8604af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults)
87aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
886401094fSKyle Evans
899f71d421SKyle Evans	if c ~= nil then
9004af4229SKyle Evans		if checking_system_defaults then
91aedd6be5SKyle Evans			return true
926401094fSKyle Evans		end
936401094fSKyle Evans		-- Otherwise, respect disabled if it's set
94aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
959f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
966401094fSKyle Evans	end
97aedd6be5SKyle Evans	return false
986401094fSKyle Evansend
996401094fSKyle Evans
10004af4229SKyle Evansfunction core.setACPI(acpi)
10104af4229SKyle Evans	if acpi == nil then
10204af4229SKyle Evans		acpi = not core.acpi
103088b4f5fSWarner Losh	end
104088b4f5fSWarner Losh
10504af4229SKyle Evans	if acpi then
106aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
107aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
108aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
109088b4f5fSWarner Losh	else
110aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
111aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
112aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
113088b4f5fSWarner Losh	end
11404af4229SKyle Evans	core.acpi = acpi
115088b4f5fSWarner Loshend
116088b4f5fSWarner Losh
11704af4229SKyle Evansfunction core.setSafeMode(safe_mode)
11804af4229SKyle Evans	if safe_mode == nil then
11904af4229SKyle Evans		safe_mode = not core.sm
120088b4f5fSWarner Losh	end
12104af4229SKyle Evans	if safe_mode then
122aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
123aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
124aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
125aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
126aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
127aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
128aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
129088b4f5fSWarner Losh	else
130aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
131aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
132aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
133aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
134aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
135aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
136aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
137088b4f5fSWarner Losh	end
13804af4229SKyle Evans	core.sm = safe_mode
139088b4f5fSWarner Loshend
140088b4f5fSWarner Losh
141*35b0c718SKyle Evansfunction core.configReloaded()
142*35b0c718SKyle Evans	-- Clear the kernel cache on config changes, autodetect might have
143*35b0c718SKyle Evans	-- changed or if we've switched boot environments then we could have
144*35b0c718SKyle Evans	-- a new kernel set.
145*35b0c718SKyle Evans	core.cached_kernels = nil
146*35b0c718SKyle Evansend
147*35b0c718SKyle Evans
148088b4f5fSWarner Loshfunction core.kernelList()
149*35b0c718SKyle Evans	if core.cached_kernels ~= nil then
150*35b0c718SKyle Evans		return core.cached_kernels
151*35b0c718SKyle Evans	end
152*35b0c718SKyle Evans
153aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1543f4eb56bSKyle Evans	local v = loader.getenv("kernels")
1553889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
156088b4f5fSWarner Losh
157aedd6be5SKyle Evans	local kernels = {}
158aedd6be5SKyle Evans	local unique = {}
159aedd6be5SKyle Evans	local i = 0
1609f71d421SKyle Evans	if k ~= nil then
161aedd6be5SKyle Evans		i = i + 1
162aedd6be5SKyle Evans		kernels[i] = k
163aedd6be5SKyle Evans		unique[k] = true
164088b4f5fSWarner Losh	end
165088b4f5fSWarner Losh
1663f4eb56bSKyle Evans	if v ~= nil then
167088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
1689f71d421SKyle Evans			if unique[n] == nil then
169aedd6be5SKyle Evans				i = i + 1
170aedd6be5SKyle Evans				kernels[i] = n
171aedd6be5SKyle Evans				unique[n] = true
172088b4f5fSWarner Losh			end
173088b4f5fSWarner Losh		end
1743889e6cdSKyle Evans	end
175a108046fSConrad Meyer
1763889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
1773889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
1783889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
1793889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
180*35b0c718SKyle Evans		core.cached_kernels = kernels
181*35b0c718SKyle Evans		return core.cached_kernels
1823f4eb56bSKyle Evans	end
1833f4eb56bSKyle Evans
184a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
185a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
186a108046fSConrad Meyer	-- named "kernel" is considered eligible.
187a108046fSConrad Meyer	for file in lfs.dir("/boot") do
188aedd6be5SKyle Evans		local fname = "/boot/" .. file
189a108046fSConrad Meyer
1909f71d421SKyle Evans		if file == "." or file == ".." then
191aedd6be5SKyle Evans			goto continue
192a108046fSConrad Meyer		end
193a108046fSConrad Meyer
1949f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
195aedd6be5SKyle Evans			goto continue
196a108046fSConrad Meyer		end
197a108046fSConrad Meyer
1989f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
199aedd6be5SKyle Evans			goto continue
200a108046fSConrad Meyer		end
201a108046fSConrad Meyer
2029f71d421SKyle Evans		if unique[file] == nil then
203aedd6be5SKyle Evans			i = i + 1
204aedd6be5SKyle Evans			kernels[i] = file
205aedd6be5SKyle Evans			unique[file] = true
206a108046fSConrad Meyer		end
207a108046fSConrad Meyer
208a108046fSConrad Meyer		::continue::
209a108046fSConrad Meyer	end
210*35b0c718SKyle Evans	core.cached_kernels = kernels
211*35b0c718SKyle Evans	return core.cached_kernels
212088b4f5fSWarner Loshend
213088b4f5fSWarner Losh
2147efc058fSKyle Evansfunction core.bootenvDefault()
2157efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2167efc058fSKyle Evansend
2177efc058fSKyle Evans
2187efc058fSKyle Evansfunction core.bootenvList()
2197efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2207efc058fSKyle Evans	local bootenvs = {}
2217efc058fSKyle Evans	local curenv
2227efc058fSKyle Evans	local envcount = 0
2237efc058fSKyle Evans	local unique = {}
2247efc058fSKyle Evans
2257efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2267efc058fSKyle Evans		return bootenvs
2277efc058fSKyle Evans	end
2287efc058fSKyle Evans
2297efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2307efc058fSKyle Evans	curenv = core.bootenvDefault()
2317efc058fSKyle Evans	if curenv ~= nil then
2327efc058fSKyle Evans		envcount = envcount + 1
2337efc058fSKyle Evans		bootenvs[envcount] = curenv
2347efc058fSKyle Evans		unique[curenv] = true
2357efc058fSKyle Evans	end
2367efc058fSKyle Evans
2377efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2387efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2397efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2407efc058fSKyle Evans			envcount = envcount + 1
2417efc058fSKyle Evans			bootenvs[envcount] = curenv
2427efc058fSKyle Evans			unique[curenv] = true
2437efc058fSKyle Evans		end
2447efc058fSKyle Evans	end
2457efc058fSKyle Evans	return bootenvs
2467efc058fSKyle Evansend
2477efc058fSKyle Evans
248088b4f5fSWarner Loshfunction core.setDefaults()
249aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
250aedd6be5SKyle Evans	core.setSafeMode(false)
251aedd6be5SKyle Evans	core.setSingleUser(false)
252aedd6be5SKyle Evans	core.setVerbose(false)
253088b4f5fSWarner Loshend
254088b4f5fSWarner Losh
2556d4ed94dSKyle Evansfunction core.autoboot(argstr)
256a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
257a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
258aedd6be5SKyle Evans		config.loadelf()
259a2a7830eSKyle Evans	end
260322a2dddSKyle Evans	loader.perform(composeLoaderCmd("autoboot", argstr))
261088b4f5fSWarner Loshend
262088b4f5fSWarner Losh
2636d4ed94dSKyle Evansfunction core.boot(argstr)
264a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
265a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
266aedd6be5SKyle Evans		config.loadelf()
267a2a7830eSKyle Evans	end
268322a2dddSKyle Evans	loader.perform(composeLoaderCmd("boot", argstr))
269088b4f5fSWarner Loshend
270088b4f5fSWarner Losh
271e07fc39cSKyle Evansfunction core.isSingleUserBoot()
272aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
273aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
274e07fc39cSKyle Evansend
275e07fc39cSKyle Evans
2767efc058fSKyle Evansfunction core.isZFSBoot()
2777efc058fSKyle Evans	local c = loader.getenv("currdev")
2787efc058fSKyle Evans
2797efc058fSKyle Evans	if c ~= nil then
2807efc058fSKyle Evans		return c:match("^zfs:") ~= nil
2817efc058fSKyle Evans	end
2827efc058fSKyle Evans	return false
2837efc058fSKyle Evansend
2847efc058fSKyle Evans
285b140d14bSKyle Evansfunction core.isSerialBoot()
286aedd6be5SKyle Evans	local c = loader.getenv("console")
287088b4f5fSWarner Losh
2889f71d421SKyle Evans	if c ~= nil then
2899f71d421SKyle Evans		if c:find("comconsole") ~= nil then
290aedd6be5SKyle Evans			return true
291088b4f5fSWarner Losh		end
292088b4f5fSWarner Losh	end
293088b4f5fSWarner Losh
294aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
2959f71d421SKyle Evans	if s ~= nil then
296aedd6be5SKyle Evans		return true
297088b4f5fSWarner Losh	end
298088b4f5fSWarner Losh
299aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
3009f71d421SKyle Evans	if m ~= nil then
301aedd6be5SKyle Evans		return true
302088b4f5fSWarner Losh	end
303aedd6be5SKyle Evans	return false
304088b4f5fSWarner Loshend
305088b4f5fSWarner Losh
306c1ab36f5SKyle Evansfunction core.isSystem386()
3079f71d421SKyle Evans	return loader.machine_arch == "i386"
308c1ab36f5SKyle Evansend
309c1ab36f5SKyle Evans
3109937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted?
3119937e979SKyle Evansfunction core.isMenuSkipped()
3129937e979SKyle Evans	if core.isSerialBoot() then
3139937e979SKyle Evans		return true
3149937e979SKyle Evans	end
3159937e979SKyle Evans	local c = string.lower(loader.getenv("console") or "")
3169937e979SKyle Evans	if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then
3179937e979SKyle Evans		return true
3189937e979SKyle Evans	end
3199937e979SKyle Evans
3209937e979SKyle Evans	c = string.lower(loader.getenv("beastie_disable") or "")
3219937e979SKyle Evans	return c == "yes"
3229937e979SKyle Evansend
3239937e979SKyle Evans
3245c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
325ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
326aedd6be5SKyle Evans	local new_tbl = {}
3275c1b5165SKyle Evans	for k, v in pairs(tbl) do
3289f71d421SKyle Evans		if type(v) == "table" then
329ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
3305c1b5165SKyle Evans		else
331aedd6be5SKyle Evans			new_tbl[k] = v
3325c1b5165SKyle Evans		end
3335c1b5165SKyle Evans	end
334aedd6be5SKyle Evans	return new_tbl
3355c1b5165SKyle Evansend
3365c1b5165SKyle Evans
3376d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3386d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3396d4ed94dSKyle Evans-- for our uses
3406d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3416d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3429f71d421SKyle Evans	if #tbl == 0 then
343aedd6be5SKyle Evans		return nil, nil
3449f71d421SKyle Evans	elseif #tbl == 1 then
345aedd6be5SKyle Evans		return tbl[1], {}
3466d4ed94dSKyle Evans	end
3476d4ed94dSKyle Evans
348aedd6be5SKyle Evans	local first_value = tbl[1]
349aedd6be5SKyle Evans	local new_tbl = {}
3506d4ed94dSKyle Evans	-- This is not a cheap operation
3516d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3529f71d421SKyle Evans		if k > 1 then
353aedd6be5SKyle Evans			new_tbl[k - 1] = v
3546d4ed94dSKyle Evans		end
3556d4ed94dSKyle Evans	end
3566d4ed94dSKyle Evans
357aedd6be5SKyle Evans	return first_value, new_tbl
3586d4ed94dSKyle Evansend
3596d4ed94dSKyle Evans
3606f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3616f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3626f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3636f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3649f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
365aedd6be5SKyle Evans	core.setACPI(true)
3666f412147SKyle Evansend
367aedd6be5SKyle Evansreturn core
368