xref: /freebsd/stand/lua/core.lua (revision 5f8cfbe1348dac0abce73c344af2eab0ccaae732)
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")
33aea262bfSKyle Evanslocal hook = require("hook")
34fa4a2394SKyle Evans
35aedd6be5SKyle Evanslocal core = {}
36088b4f5fSWarner Losh
37322a2dddSKyle Evanslocal function composeLoaderCmd(cmd_name, argstr)
389f71d421SKyle Evans	if argstr ~= nil then
39aedd6be5SKyle Evans		cmd_name = cmd_name .. " " .. argstr
406d4ed94dSKyle Evans	end
41aedd6be5SKyle Evans	return cmd_name
426d4ed94dSKyle Evansend
436d4ed94dSKyle Evans
44b5746545SKyle Evans-- Module exports
45fe672a15SKyle Evans-- Commonly appearing constants
46aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
47aedd6be5SKyle Evanscore.KEY_ENTER		= 13
48aedd6be5SKyle Evanscore.KEY_DELETE		= 127
49fe672a15SKyle Evans
50230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in
51230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal'
52aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
532bb86aefSKyle Evanscore.KEYSTR_CSI		= core.KEYSTR_ESCAPE .. "["
5439006570SKyle Evans
55aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
56aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
57aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
58aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
59aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
60a7cf0562SKyle Evans
6104af4229SKyle Evansfunction core.setVerbose(verbose)
6204af4229SKyle Evans	if verbose == nil then
6304af4229SKyle Evans		verbose = not core.verbose
64088b4f5fSWarner Losh	end
65088b4f5fSWarner Losh
6604af4229SKyle Evans	if verbose then
67aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
68088b4f5fSWarner Losh	else
69aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
70088b4f5fSWarner Losh	end
7104af4229SKyle Evans	core.verbose = verbose
72088b4f5fSWarner Loshend
73088b4f5fSWarner Losh
7404af4229SKyle Evansfunction core.setSingleUser(single_user)
7504af4229SKyle Evans	if single_user == nil then
7604af4229SKyle Evans		single_user = not core.su
77088b4f5fSWarner Losh	end
78088b4f5fSWarner Losh
7904af4229SKyle Evans	if single_user then
80aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
81088b4f5fSWarner Losh	else
82aedd6be5SKyle Evans		loader.unsetenv("boot_single")
83088b4f5fSWarner Losh	end
8404af4229SKyle Evans	core.su = single_user
85088b4f5fSWarner Loshend
86088b4f5fSWarner Losh
8704af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults)
88aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
896401094fSKyle Evans
909f71d421SKyle Evans	if c ~= nil then
9104af4229SKyle Evans		if checking_system_defaults then
92aedd6be5SKyle Evans			return true
936401094fSKyle Evans		end
946401094fSKyle Evans		-- Otherwise, respect disabled if it's set
95aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
969f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
976401094fSKyle Evans	end
98aedd6be5SKyle Evans	return false
996401094fSKyle Evansend
1006401094fSKyle Evans
10104af4229SKyle Evansfunction core.setACPI(acpi)
10204af4229SKyle Evans	if acpi == nil then
10304af4229SKyle Evans		acpi = not core.acpi
104088b4f5fSWarner Losh	end
105088b4f5fSWarner Losh
10604af4229SKyle Evans	if acpi then
107aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
108aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
109aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
110088b4f5fSWarner Losh	else
111aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
112aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
113aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
114088b4f5fSWarner Losh	end
11504af4229SKyle Evans	core.acpi = acpi
116088b4f5fSWarner Loshend
117088b4f5fSWarner Losh
11804af4229SKyle Evansfunction core.setSafeMode(safe_mode)
11904af4229SKyle Evans	if safe_mode == nil then
12004af4229SKyle Evans		safe_mode = not core.sm
121088b4f5fSWarner Losh	end
12204af4229SKyle Evans	if safe_mode then
123aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
124aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
125aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
126aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
127aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
128aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
129aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
130088b4f5fSWarner Losh	else
131aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
132aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
133aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
134aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
135aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
136aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
137aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
138088b4f5fSWarner Losh	end
13904af4229SKyle Evans	core.sm = safe_mode
140088b4f5fSWarner Loshend
141088b4f5fSWarner Losh
142aea262bfSKyle Evansfunction core.clearCachedKernels()
14335b0c718SKyle Evans	-- Clear the kernel cache on config changes, autodetect might have
14435b0c718SKyle Evans	-- changed or if we've switched boot environments then we could have
14535b0c718SKyle Evans	-- a new kernel set.
14635b0c718SKyle Evans	core.cached_kernels = nil
14735b0c718SKyle Evansend
14835b0c718SKyle Evans
149088b4f5fSWarner Loshfunction core.kernelList()
15035b0c718SKyle Evans	if core.cached_kernels ~= nil then
15135b0c718SKyle Evans		return core.cached_kernels
15235b0c718SKyle Evans	end
15335b0c718SKyle Evans
154aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1553f4eb56bSKyle Evans	local v = loader.getenv("kernels")
1563889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
157088b4f5fSWarner Losh
158aedd6be5SKyle Evans	local kernels = {}
159aedd6be5SKyle Evans	local unique = {}
160aedd6be5SKyle Evans	local i = 0
1619f71d421SKyle Evans	if k ~= nil then
162aedd6be5SKyle Evans		i = i + 1
163aedd6be5SKyle Evans		kernels[i] = k
164aedd6be5SKyle Evans		unique[k] = true
165088b4f5fSWarner Losh	end
166088b4f5fSWarner Losh
1673f4eb56bSKyle Evans	if v ~= nil then
168088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
1699f71d421SKyle Evans			if unique[n] == nil then
170aedd6be5SKyle Evans				i = i + 1
171aedd6be5SKyle Evans				kernels[i] = n
172aedd6be5SKyle Evans				unique[n] = true
173088b4f5fSWarner Losh			end
174088b4f5fSWarner Losh		end
1753889e6cdSKyle Evans	end
176a108046fSConrad Meyer
1773889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
1783889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
1793889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
1803889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
18135b0c718SKyle Evans		core.cached_kernels = kernels
18235b0c718SKyle Evans		return core.cached_kernels
1833f4eb56bSKyle Evans	end
1843f4eb56bSKyle Evans
185a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
186a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
187a108046fSConrad Meyer	-- named "kernel" is considered eligible.
188a108046fSConrad Meyer	for file in lfs.dir("/boot") do
189aedd6be5SKyle Evans		local fname = "/boot/" .. file
190a108046fSConrad Meyer
1919f71d421SKyle Evans		if file == "." or file == ".." then
192aedd6be5SKyle Evans			goto continue
193a108046fSConrad Meyer		end
194a108046fSConrad Meyer
1959f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
196aedd6be5SKyle Evans			goto continue
197a108046fSConrad Meyer		end
198a108046fSConrad Meyer
1999f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
200aedd6be5SKyle Evans			goto continue
201a108046fSConrad Meyer		end
202a108046fSConrad Meyer
2039f71d421SKyle Evans		if unique[file] == nil then
204aedd6be5SKyle Evans			i = i + 1
205aedd6be5SKyle Evans			kernels[i] = file
206aedd6be5SKyle Evans			unique[file] = true
207a108046fSConrad Meyer		end
208a108046fSConrad Meyer
209a108046fSConrad Meyer		::continue::
210a108046fSConrad Meyer	end
21135b0c718SKyle Evans	core.cached_kernels = kernels
21235b0c718SKyle Evans	return core.cached_kernels
213088b4f5fSWarner Loshend
214088b4f5fSWarner Losh
2157efc058fSKyle Evansfunction core.bootenvDefault()
2167efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2177efc058fSKyle Evansend
2187efc058fSKyle Evans
2197efc058fSKyle Evansfunction core.bootenvList()
2207efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2217efc058fSKyle Evans	local bootenvs = {}
2227efc058fSKyle Evans	local curenv
2237efc058fSKyle Evans	local envcount = 0
2247efc058fSKyle Evans	local unique = {}
2257efc058fSKyle Evans
2267efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2277efc058fSKyle Evans		return bootenvs
2287efc058fSKyle Evans	end
2297efc058fSKyle Evans
2307efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2317efc058fSKyle Evans	curenv = core.bootenvDefault()
2327efc058fSKyle Evans	if curenv ~= nil then
2337efc058fSKyle Evans		envcount = envcount + 1
2347efc058fSKyle Evans		bootenvs[envcount] = curenv
2357efc058fSKyle Evans		unique[curenv] = true
2367efc058fSKyle Evans	end
2377efc058fSKyle Evans
2387efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2397efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2407efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2417efc058fSKyle Evans			envcount = envcount + 1
2427efc058fSKyle Evans			bootenvs[envcount] = curenv
2437efc058fSKyle Evans			unique[curenv] = true
2447efc058fSKyle Evans		end
2457efc058fSKyle Evans	end
2467efc058fSKyle Evans	return bootenvs
2477efc058fSKyle Evansend
2487efc058fSKyle Evans
249088b4f5fSWarner Loshfunction core.setDefaults()
250aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
251aedd6be5SKyle Evans	core.setSafeMode(false)
252aedd6be5SKyle Evans	core.setSingleUser(false)
253aedd6be5SKyle Evans	core.setVerbose(false)
254088b4f5fSWarner Loshend
255088b4f5fSWarner Losh
2566d4ed94dSKyle Evansfunction core.autoboot(argstr)
257a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
258a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
259aedd6be5SKyle Evans		config.loadelf()
260a2a7830eSKyle Evans	end
261322a2dddSKyle Evans	loader.perform(composeLoaderCmd("autoboot", argstr))
262088b4f5fSWarner Loshend
263088b4f5fSWarner Losh
2646d4ed94dSKyle Evansfunction core.boot(argstr)
265a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
266a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
267aedd6be5SKyle Evans		config.loadelf()
268a2a7830eSKyle Evans	end
269322a2dddSKyle Evans	loader.perform(composeLoaderCmd("boot", argstr))
270088b4f5fSWarner Loshend
271088b4f5fSWarner Losh
272e07fc39cSKyle Evansfunction core.isSingleUserBoot()
273aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
274aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
275e07fc39cSKyle Evansend
276e07fc39cSKyle Evans
277*5f8cfbe1SKyle Evansfunction core.isUEFIBoot()
278*5f8cfbe1SKyle Evans	local efiver = loader.getenv("efi-version")
279*5f8cfbe1SKyle Evans
280*5f8cfbe1SKyle Evans	return efiver ~= nil
281*5f8cfbe1SKyle Evansend
282*5f8cfbe1SKyle Evans
2837efc058fSKyle Evansfunction core.isZFSBoot()
2847efc058fSKyle Evans	local c = loader.getenv("currdev")
2857efc058fSKyle Evans
2867efc058fSKyle Evans	if c ~= nil then
2877efc058fSKyle Evans		return c:match("^zfs:") ~= nil
2887efc058fSKyle Evans	end
2897efc058fSKyle Evans	return false
2907efc058fSKyle Evansend
2917efc058fSKyle Evans
292b140d14bSKyle Evansfunction core.isSerialBoot()
293aedd6be5SKyle Evans	local c = loader.getenv("console")
294088b4f5fSWarner Losh
2959f71d421SKyle Evans	if c ~= nil then
2969f71d421SKyle Evans		if c:find("comconsole") ~= nil then
297aedd6be5SKyle Evans			return true
298088b4f5fSWarner Losh		end
299088b4f5fSWarner Losh	end
300088b4f5fSWarner Losh
301aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
3029f71d421SKyle Evans	if s ~= nil then
303aedd6be5SKyle Evans		return true
304088b4f5fSWarner Losh	end
305088b4f5fSWarner Losh
306aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
3079f71d421SKyle Evans	if m ~= nil then
308aedd6be5SKyle Evans		return true
309088b4f5fSWarner Losh	end
310aedd6be5SKyle Evans	return false
311088b4f5fSWarner Loshend
312088b4f5fSWarner Losh
313c1ab36f5SKyle Evansfunction core.isSystem386()
3149f71d421SKyle Evans	return loader.machine_arch == "i386"
315c1ab36f5SKyle Evansend
316c1ab36f5SKyle Evans
3179937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted?
3189937e979SKyle Evansfunction core.isMenuSkipped()
3199937e979SKyle Evans	if core.isSerialBoot() then
3209937e979SKyle Evans		return true
3219937e979SKyle Evans	end
3229937e979SKyle Evans	local c = string.lower(loader.getenv("console") or "")
3239937e979SKyle Evans	if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then
3249937e979SKyle Evans		return true
3259937e979SKyle Evans	end
3269937e979SKyle Evans
3279937e979SKyle Evans	c = string.lower(loader.getenv("beastie_disable") or "")
3289937e979SKyle Evans	return c == "yes"
3299937e979SKyle Evansend
3309937e979SKyle Evans
3315c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
332ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
333aedd6be5SKyle Evans	local new_tbl = {}
3345c1b5165SKyle Evans	for k, v in pairs(tbl) do
3359f71d421SKyle Evans		if type(v) == "table" then
336ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
3375c1b5165SKyle Evans		else
338aedd6be5SKyle Evans			new_tbl[k] = v
3395c1b5165SKyle Evans		end
3405c1b5165SKyle Evans	end
341aedd6be5SKyle Evans	return new_tbl
3425c1b5165SKyle Evansend
3435c1b5165SKyle Evans
3446d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3456d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3466d4ed94dSKyle Evans-- for our uses
3476d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3486d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3499f71d421SKyle Evans	if #tbl == 0 then
350aedd6be5SKyle Evans		return nil, nil
3519f71d421SKyle Evans	elseif #tbl == 1 then
352aedd6be5SKyle Evans		return tbl[1], {}
3536d4ed94dSKyle Evans	end
3546d4ed94dSKyle Evans
355aedd6be5SKyle Evans	local first_value = tbl[1]
356aedd6be5SKyle Evans	local new_tbl = {}
3576d4ed94dSKyle Evans	-- This is not a cheap operation
3586d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3599f71d421SKyle Evans		if k > 1 then
360aedd6be5SKyle Evans			new_tbl[k - 1] = v
3616d4ed94dSKyle Evans		end
3626d4ed94dSKyle Evans	end
3636d4ed94dSKyle Evans
364aedd6be5SKyle Evans	return first_value, new_tbl
3656d4ed94dSKyle Evansend
3666d4ed94dSKyle Evans
3676f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3686f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3696f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3706f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3719f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
372aedd6be5SKyle Evans	core.setACPI(true)
3736f412147SKyle Evansend
374aea262bfSKyle Evans
375aea262bfSKyle Evanshook.register("config.reloaded", core.clearCachedKernels)
376aedd6be5SKyle Evansreturn core
377