xref: /freebsd/stand/lua/core.lua (revision a500341916bd89c46a42166078decb5699f7d518)
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
4407faaf78SKyle Evans-- Globals
4507faaf78SKyle Evans-- try_include will return the loaded module on success, or nil on failure.
4607faaf78SKyle Evans-- A message will also be printed on failure, with one exception: non-verbose
4707faaf78SKyle Evans-- loading will suppress 'module not found' errors.
4807faaf78SKyle Evansfunction try_include(module)
4907faaf78SKyle Evans	local status, ret = pcall(require, module)
5007faaf78SKyle Evans	-- ret is the module if we succeeded.
5107faaf78SKyle Evans	if status then
5207faaf78SKyle Evans		return ret
5307faaf78SKyle Evans	end
5407faaf78SKyle Evans	-- Otherwise, ret is just a message; filter out ENOENT unless we're
5507faaf78SKyle Evans	-- doing a verbose load. As a consequence, try_include prior to loading
5607faaf78SKyle Evans	-- configuration will not display 'module not found'. All other errors
5707faaf78SKyle Evans	-- in loading will be printed.
5807faaf78SKyle Evans	if config.verbose or ret:match("^module .+ not found") == nil then
59892b3a52SKyle Evans		error(ret, 2)
6007faaf78SKyle Evans	end
6107faaf78SKyle Evans	return nil
6207faaf78SKyle Evansend
6307faaf78SKyle Evans
64b5746545SKyle Evans-- Module exports
65fe672a15SKyle Evans-- Commonly appearing constants
66aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
67aedd6be5SKyle Evanscore.KEY_ENTER		= 13
68aedd6be5SKyle Evanscore.KEY_DELETE		= 127
69fe672a15SKyle Evans
70230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in
71230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal'
72aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
732bb86aefSKyle Evanscore.KEYSTR_CSI		= core.KEYSTR_ESCAPE .. "["
7439006570SKyle Evans
75aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
76aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
77aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
78aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
79aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
80a7cf0562SKyle Evans
8104af4229SKyle Evansfunction core.setVerbose(verbose)
8204af4229SKyle Evans	if verbose == nil then
8304af4229SKyle Evans		verbose = not core.verbose
84088b4f5fSWarner Losh	end
85088b4f5fSWarner Losh
8604af4229SKyle Evans	if verbose then
87aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
88088b4f5fSWarner Losh	else
89aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
90088b4f5fSWarner Losh	end
9104af4229SKyle Evans	core.verbose = verbose
92088b4f5fSWarner Loshend
93088b4f5fSWarner Losh
9404af4229SKyle Evansfunction core.setSingleUser(single_user)
9504af4229SKyle Evans	if single_user == nil then
9604af4229SKyle Evans		single_user = not core.su
97088b4f5fSWarner Losh	end
98088b4f5fSWarner Losh
9904af4229SKyle Evans	if single_user then
100aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
101088b4f5fSWarner Losh	else
102aedd6be5SKyle Evans		loader.unsetenv("boot_single")
103088b4f5fSWarner Losh	end
10404af4229SKyle Evans	core.su = single_user
105088b4f5fSWarner Loshend
106088b4f5fSWarner Losh
10704af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults)
108aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
1096401094fSKyle Evans
1109f71d421SKyle Evans	if c ~= nil then
11104af4229SKyle Evans		if checking_system_defaults then
112aedd6be5SKyle Evans			return true
1136401094fSKyle Evans		end
1146401094fSKyle Evans		-- Otherwise, respect disabled if it's set
115aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
1169f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
1176401094fSKyle Evans	end
118aedd6be5SKyle Evans	return false
1196401094fSKyle Evansend
1206401094fSKyle Evans
12104af4229SKyle Evansfunction core.setACPI(acpi)
12204af4229SKyle Evans	if acpi == nil then
12304af4229SKyle Evans		acpi = not core.acpi
124088b4f5fSWarner Losh	end
125088b4f5fSWarner Losh
12604af4229SKyle Evans	if acpi then
127aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
128aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
129aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
130088b4f5fSWarner Losh	else
131aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
132aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
133aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
134088b4f5fSWarner Losh	end
13504af4229SKyle Evans	core.acpi = acpi
136088b4f5fSWarner Loshend
137088b4f5fSWarner Losh
13804af4229SKyle Evansfunction core.setSafeMode(safe_mode)
13904af4229SKyle Evans	if safe_mode == nil then
14004af4229SKyle Evans		safe_mode = not core.sm
141088b4f5fSWarner Losh	end
14204af4229SKyle Evans	if safe_mode then
143aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
144aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
145aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
146aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
147aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
148aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
149aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
150088b4f5fSWarner Losh	else
151aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
152aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
153aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
154aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
155aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
156aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
157aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
158088b4f5fSWarner Losh	end
15904af4229SKyle Evans	core.sm = safe_mode
160088b4f5fSWarner Loshend
161088b4f5fSWarner Losh
162aea262bfSKyle Evansfunction core.clearCachedKernels()
16335b0c718SKyle Evans	-- Clear the kernel cache on config changes, autodetect might have
16435b0c718SKyle Evans	-- changed or if we've switched boot environments then we could have
16535b0c718SKyle Evans	-- a new kernel set.
16635b0c718SKyle Evans	core.cached_kernels = nil
16735b0c718SKyle Evansend
16835b0c718SKyle Evans
169088b4f5fSWarner Loshfunction core.kernelList()
17035b0c718SKyle Evans	if core.cached_kernels ~= nil then
17135b0c718SKyle Evans		return core.cached_kernels
17235b0c718SKyle Evans	end
17335b0c718SKyle Evans
174aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1753f4eb56bSKyle Evans	local v = loader.getenv("kernels")
1763889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
177088b4f5fSWarner Losh
178aedd6be5SKyle Evans	local kernels = {}
179aedd6be5SKyle Evans	local unique = {}
180aedd6be5SKyle Evans	local i = 0
1819f71d421SKyle Evans	if k ~= nil then
182aedd6be5SKyle Evans		i = i + 1
183aedd6be5SKyle Evans		kernels[i] = k
184aedd6be5SKyle Evans		unique[k] = true
185088b4f5fSWarner Losh	end
186088b4f5fSWarner Losh
1873f4eb56bSKyle Evans	if v ~= nil then
188*a5003419SKyle Evans		for n in v:gmatch("([^;, ]+)[;, ]?") do
1899f71d421SKyle Evans			if unique[n] == nil then
190aedd6be5SKyle Evans				i = i + 1
191aedd6be5SKyle Evans				kernels[i] = n
192aedd6be5SKyle Evans				unique[n] = true
193088b4f5fSWarner Losh			end
194088b4f5fSWarner Losh		end
1953889e6cdSKyle Evans	end
196a108046fSConrad Meyer
1973889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
1983889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
1993889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
2003889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
20135b0c718SKyle Evans		core.cached_kernels = kernels
20235b0c718SKyle Evans		return core.cached_kernels
2033f4eb56bSKyle Evans	end
2043f4eb56bSKyle Evans
205a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
206a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
207a108046fSConrad Meyer	-- named "kernel" is considered eligible.
208a108046fSConrad Meyer	for file in lfs.dir("/boot") do
209aedd6be5SKyle Evans		local fname = "/boot/" .. file
210a108046fSConrad Meyer
2119f71d421SKyle Evans		if file == "." or file == ".." then
212aedd6be5SKyle Evans			goto continue
213a108046fSConrad Meyer		end
214a108046fSConrad Meyer
2159f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
216aedd6be5SKyle Evans			goto continue
217a108046fSConrad Meyer		end
218a108046fSConrad Meyer
2199f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
220aedd6be5SKyle Evans			goto continue
221a108046fSConrad Meyer		end
222a108046fSConrad Meyer
2239f71d421SKyle Evans		if unique[file] == nil then
224aedd6be5SKyle Evans			i = i + 1
225aedd6be5SKyle Evans			kernels[i] = file
226aedd6be5SKyle Evans			unique[file] = true
227a108046fSConrad Meyer		end
228a108046fSConrad Meyer
229a108046fSConrad Meyer		::continue::
230a108046fSConrad Meyer	end
23135b0c718SKyle Evans	core.cached_kernels = kernels
23235b0c718SKyle Evans	return core.cached_kernels
233088b4f5fSWarner Loshend
234088b4f5fSWarner Losh
2357efc058fSKyle Evansfunction core.bootenvDefault()
2367efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2377efc058fSKyle Evansend
2387efc058fSKyle Evans
2397efc058fSKyle Evansfunction core.bootenvList()
2407efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2417efc058fSKyle Evans	local bootenvs = {}
2427efc058fSKyle Evans	local curenv
2437efc058fSKyle Evans	local envcount = 0
2447efc058fSKyle Evans	local unique = {}
2457efc058fSKyle Evans
2467efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2477efc058fSKyle Evans		return bootenvs
2487efc058fSKyle Evans	end
2497efc058fSKyle Evans
2507efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2517efc058fSKyle Evans	curenv = core.bootenvDefault()
2527efc058fSKyle Evans	if curenv ~= nil then
2537efc058fSKyle Evans		envcount = envcount + 1
2547efc058fSKyle Evans		bootenvs[envcount] = curenv
2557efc058fSKyle Evans		unique[curenv] = true
2567efc058fSKyle Evans	end
2577efc058fSKyle Evans
2587efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2597efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2607efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2617efc058fSKyle Evans			envcount = envcount + 1
2627efc058fSKyle Evans			bootenvs[envcount] = curenv
2637efc058fSKyle Evans			unique[curenv] = true
2647efc058fSKyle Evans		end
2657efc058fSKyle Evans	end
2667efc058fSKyle Evans	return bootenvs
2677efc058fSKyle Evansend
2687efc058fSKyle Evans
269088b4f5fSWarner Loshfunction core.setDefaults()
270aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
271aedd6be5SKyle Evans	core.setSafeMode(false)
272aedd6be5SKyle Evans	core.setSingleUser(false)
273aedd6be5SKyle Evans	core.setVerbose(false)
274088b4f5fSWarner Loshend
275088b4f5fSWarner Losh
2766d4ed94dSKyle Evansfunction core.autoboot(argstr)
277a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
278a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
279aedd6be5SKyle Evans		config.loadelf()
280a2a7830eSKyle Evans	end
281322a2dddSKyle Evans	loader.perform(composeLoaderCmd("autoboot", argstr))
282088b4f5fSWarner Loshend
283088b4f5fSWarner Losh
2846d4ed94dSKyle Evansfunction core.boot(argstr)
285a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
286a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
287aedd6be5SKyle Evans		config.loadelf()
288a2a7830eSKyle Evans	end
289322a2dddSKyle Evans	loader.perform(composeLoaderCmd("boot", argstr))
290088b4f5fSWarner Loshend
291088b4f5fSWarner Losh
292e07fc39cSKyle Evansfunction core.isSingleUserBoot()
293aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
294aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
295e07fc39cSKyle Evansend
296e07fc39cSKyle Evans
2975f8cfbe1SKyle Evansfunction core.isUEFIBoot()
2985f8cfbe1SKyle Evans	local efiver = loader.getenv("efi-version")
2995f8cfbe1SKyle Evans
3005f8cfbe1SKyle Evans	return efiver ~= nil
3015f8cfbe1SKyle Evansend
3025f8cfbe1SKyle Evans
3037efc058fSKyle Evansfunction core.isZFSBoot()
3047efc058fSKyle Evans	local c = loader.getenv("currdev")
3057efc058fSKyle Evans
3067efc058fSKyle Evans	if c ~= nil then
3077efc058fSKyle Evans		return c:match("^zfs:") ~= nil
3087efc058fSKyle Evans	end
3097efc058fSKyle Evans	return false
3107efc058fSKyle Evansend
3117efc058fSKyle Evans
312b140d14bSKyle Evansfunction core.isSerialBoot()
313aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
3149f71d421SKyle Evans	if s ~= nil then
315aedd6be5SKyle Evans		return true
316088b4f5fSWarner Losh	end
317088b4f5fSWarner Losh
318aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
3199f71d421SKyle Evans	if m ~= nil then
320aedd6be5SKyle Evans		return true
321088b4f5fSWarner Losh	end
322aedd6be5SKyle Evans	return false
323088b4f5fSWarner Loshend
324088b4f5fSWarner Losh
325c1ab36f5SKyle Evansfunction core.isSystem386()
3269f71d421SKyle Evans	return loader.machine_arch == "i386"
327c1ab36f5SKyle Evansend
328c1ab36f5SKyle Evans
3299937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted?
3309937e979SKyle Evansfunction core.isMenuSkipped()
331b83a355dSKyle Evans	return string.lower(loader.getenv("beastie_disable") or "") == "yes"
3329937e979SKyle Evansend
3339937e979SKyle Evans
3345c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
335ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
336aedd6be5SKyle Evans	local new_tbl = {}
3375c1b5165SKyle Evans	for k, v in pairs(tbl) do
3389f71d421SKyle Evans		if type(v) == "table" then
339ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
3405c1b5165SKyle Evans		else
341aedd6be5SKyle Evans			new_tbl[k] = v
3425c1b5165SKyle Evans		end
3435c1b5165SKyle Evans	end
344aedd6be5SKyle Evans	return new_tbl
3455c1b5165SKyle Evansend
3465c1b5165SKyle Evans
3476d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3486d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3496d4ed94dSKyle Evans-- for our uses
3506d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3516d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3529f71d421SKyle Evans	if #tbl == 0 then
353aedd6be5SKyle Evans		return nil, nil
3549f71d421SKyle Evans	elseif #tbl == 1 then
355aedd6be5SKyle Evans		return tbl[1], {}
3566d4ed94dSKyle Evans	end
3576d4ed94dSKyle Evans
358aedd6be5SKyle Evans	local first_value = tbl[1]
359aedd6be5SKyle Evans	local new_tbl = {}
3606d4ed94dSKyle Evans	-- This is not a cheap operation
3616d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3629f71d421SKyle Evans		if k > 1 then
363aedd6be5SKyle Evans			new_tbl[k - 1] = v
3646d4ed94dSKyle Evans		end
3656d4ed94dSKyle Evans	end
3666d4ed94dSKyle Evans
367aedd6be5SKyle Evans	return first_value, new_tbl
3686d4ed94dSKyle Evansend
3696d4ed94dSKyle Evans
3706f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3716f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3726f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3736f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3749f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
375aedd6be5SKyle Evans	core.setACPI(true)
3766f412147SKyle Evansend
377aea262bfSKyle Evans
378aea262bfSKyle Evanshook.register("config.reloaded", core.clearCachedKernels)
379aedd6be5SKyle Evansreturn core
380