xref: /freebsd/stand/lua/core.lua (revision c2ba66d4d01f23303352bfe3cbd50ff5d9a05947)
1088b4f5fSWarner Losh--
24d846d26SWarner Losh-- SPDX-License-Identifier: BSD-2-Clause
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
30011eae6cSKyle Evanslocal config = require("config")
31aea262bfSKyle Evanslocal hook = require("hook")
32fa4a2394SKyle Evans
33aedd6be5SKyle Evanslocal core = {}
34088b4f5fSWarner Losh
35e0f3dc82SR. Christian McDonaldlocal default_acpi = false
361613f091SKyle Evanslocal default_safe_mode = false
371613f091SKyle Evanslocal default_single_user = false
381613f091SKyle Evanslocal default_verbose = false
391613f091SKyle Evans
40277f38abSMariusz Zaborskilocal bootenv_list = "bootenvs"
41277f38abSMariusz Zaborski
42322a2dddSKyle Evanslocal function composeLoaderCmd(cmd_name, argstr)
439f71d421SKyle Evans	if argstr ~= nil then
44aedd6be5SKyle Evans		cmd_name = cmd_name .. " " .. argstr
456d4ed94dSKyle Evans	end
46aedd6be5SKyle Evans	return cmd_name
476d4ed94dSKyle Evansend
486d4ed94dSKyle Evans
491613f091SKyle Evanslocal function recordDefaults()
501613f091SKyle Evans	local boot_single = loader.getenv("boot_single") or "no"
511613f091SKyle Evans	local boot_verbose = loader.getenv("boot_verbose") or "no"
52e0f3dc82SR. Christian McDonald
53e0f3dc82SR. Christian McDonald	default_acpi = core.getACPI()
541613f091SKyle Evans	default_single_user = boot_single:lower() ~= "no"
551613f091SKyle Evans	default_verbose = boot_verbose:lower() ~= "no"
561613f091SKyle Evans
57e0f3dc82SR. Christian McDonald	core.setACPI(default_acpi)
581613f091SKyle Evans	core.setSingleUser(default_single_user)
591613f091SKyle Evans	core.setVerbose(default_verbose)
601613f091SKyle Evansend
611613f091SKyle Evans
621613f091SKyle Evans
6307faaf78SKyle Evans-- Globals
645beb5507SKyle Evans-- try_include will return the loaded module on success, or false and the error
655beb5507SKyle Evans-- message on failure.
6607faaf78SKyle Evansfunction try_include(module)
67bac5966eSKyle Evans	if module:sub(1, 1) ~= "/" then
68522a65a8SWarner Losh		local lua_path = loader.lua_path
69522a65a8SWarner Losh		-- XXX Temporary compat shim; this should be removed once the
70522a65a8SWarner Losh		-- loader.lua_path export has sufficiently spread.
71522a65a8SWarner Losh		if lua_path == nil then
72522a65a8SWarner Losh			lua_path = "/boot/lua"
73522a65a8SWarner Losh		end
74522a65a8SWarner Losh		module = lua_path .. "/" .. module
75bac5966eSKyle Evans		-- We only attempt to append an extension if an absolute path
76bac5966eSKyle Evans		-- wasn't specified.  This assumes that the caller either wants
77bac5966eSKyle Evans		-- to treat this like it would require() and specify just the
78bac5966eSKyle Evans		-- base filename, or they know what they're doing as they've
79bac5966eSKyle Evans		-- specified an absolute path and we shouldn't impede.
80bac5966eSKyle Evans		if module:match(".lua$") == nil then
81bac5966eSKyle Evans			module = module .. ".lua"
82bac5966eSKyle Evans		end
83bac5966eSKyle Evans	end
84bac5966eSKyle Evans	if lfs.attributes(module, "mode") ~= "file" then
85bac5966eSKyle Evans		return
86bac5966eSKyle Evans	end
87bac5966eSKyle Evans
88bac5966eSKyle Evans	return dofile(module)
8907faaf78SKyle Evansend
9007faaf78SKyle Evans
91b5746545SKyle Evans-- Module exports
92fe672a15SKyle Evans-- Commonly appearing constants
93aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
94aedd6be5SKyle Evanscore.KEY_ENTER		= 13
95aedd6be5SKyle Evanscore.KEY_DELETE		= 127
96fe672a15SKyle Evans
97230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in
98230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal'
99aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
1002bb86aefSKyle Evanscore.KEYSTR_CSI		= core.KEYSTR_ESCAPE .. "["
10173531a2aSRyan Moellercore.KEYSTR_RESET	= core.KEYSTR_ESCAPE .. "c"
10239006570SKyle Evans
103aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
104aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
105aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
106aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
107aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
108a7cf0562SKyle Evans
10904af4229SKyle Evansfunction core.setVerbose(verbose)
11004af4229SKyle Evans	if verbose == nil then
11104af4229SKyle Evans		verbose = not core.verbose
112088b4f5fSWarner Losh	end
113088b4f5fSWarner Losh
11404af4229SKyle Evans	if verbose then
115aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
116088b4f5fSWarner Losh	else
117aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
118088b4f5fSWarner Losh	end
11904af4229SKyle Evans	core.verbose = verbose
120088b4f5fSWarner Loshend
121088b4f5fSWarner Losh
12204af4229SKyle Evansfunction core.setSingleUser(single_user)
12304af4229SKyle Evans	if single_user == nil then
12404af4229SKyle Evans		single_user = not core.su
125088b4f5fSWarner Losh	end
126088b4f5fSWarner Losh
12704af4229SKyle Evans	if single_user then
128aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
129088b4f5fSWarner Losh	else
130aedd6be5SKyle Evans		loader.unsetenv("boot_single")
131088b4f5fSWarner Losh	end
13204af4229SKyle Evans	core.su = single_user
133088b4f5fSWarner Loshend
134088b4f5fSWarner Losh
135e0f3dc82SR. Christian McDonaldfunction core.hasACPI()
136e183039fSKyle Evans	-- We can't trust acpi.rsdp to be set if the loader binary doesn't do
137e183039fSKyle Evans	-- ACPI detection early enough.  UEFI loader historically didn't, so
138e183039fSKyle Evans	-- we'll fallback to assuming ACPI is enabled if this binary does not
139e183039fSKyle Evans	-- declare that it probes for ACPI early enough
140e183039fSKyle Evans	if loader.getenv("acpi.rsdp") ~= nil then
141e183039fSKyle Evans		return true
142e0f3dc82SR. Christian McDonald	end
1436401094fSKyle Evans
144e183039fSKyle Evans	return not core.hasFeature("EARLY_ACPI")
1450abe05aeSWarner Loshend
1460abe05aeSWarner Losh
147e0f3dc82SR. Christian McDonaldfunction core.getACPI()
148e0f3dc82SR. Christian McDonald	if not core.hasACPI() then
149e183039fSKyle Evans		return false
1506401094fSKyle Evans	end
1516401094fSKyle Evans
152e0f3dc82SR. Christian McDonald	-- Otherwise, respect disabled if it's set
153e0f3dc82SR. Christian McDonald	local c = loader.getenv("hint.acpi.0.disabled")
154e0f3dc82SR. Christian McDonald	return c == nil or tonumber(c) ~= 1
155e0f3dc82SR. Christian McDonaldend
156e0f3dc82SR. Christian McDonald
15704af4229SKyle Evansfunction core.setACPI(acpi)
15804af4229SKyle Evans	if acpi == nil then
15904af4229SKyle Evans		acpi = not core.acpi
160088b4f5fSWarner Losh	end
161088b4f5fSWarner Losh
16204af4229SKyle Evans	if acpi then
163e183039fSKyle Evans		config.enableModule("acpi")
164aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
165088b4f5fSWarner Losh	else
166e183039fSKyle Evans		config.disableModule("acpi")
167aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
168088b4f5fSWarner Losh	end
16904af4229SKyle Evans	core.acpi = acpi
170088b4f5fSWarner Loshend
171088b4f5fSWarner Losh
17204af4229SKyle Evansfunction core.setSafeMode(safe_mode)
17304af4229SKyle Evans	if safe_mode == nil then
17404af4229SKyle Evans		safe_mode = not core.sm
175088b4f5fSWarner Losh	end
17604af4229SKyle Evans	if safe_mode then
177aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
178aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
179aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
180aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
181aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
182088b4f5fSWarner Losh	else
183aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
184aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
185aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
186aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
187aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
188088b4f5fSWarner Losh	end
18904af4229SKyle Evans	core.sm = safe_mode
190088b4f5fSWarner Loshend
191088b4f5fSWarner Losh
192aea262bfSKyle Evansfunction core.clearCachedKernels()
19335b0c718SKyle Evans	-- Clear the kernel cache on config changes, autodetect might have
19435b0c718SKyle Evans	-- changed or if we've switched boot environments then we could have
19535b0c718SKyle Evans	-- a new kernel set.
19635b0c718SKyle Evans	core.cached_kernels = nil
19735b0c718SKyle Evansend
19835b0c718SKyle Evans
199088b4f5fSWarner Loshfunction core.kernelList()
20035b0c718SKyle Evans	if core.cached_kernels ~= nil then
20135b0c718SKyle Evans		return core.cached_kernels
20235b0c718SKyle Evans	end
20335b0c718SKyle Evans
204d04415c5SKyle Evans	local default_kernel = loader.getenv("kernel")
2053f4eb56bSKyle Evans	local v = loader.getenv("kernels")
2063889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
207088b4f5fSWarner Losh
208aedd6be5SKyle Evans	local kernels = {}
209aedd6be5SKyle Evans	local unique = {}
210aedd6be5SKyle Evans	local i = 0
211d04415c5SKyle Evans
212d04415c5SKyle Evans	if default_kernel then
213aedd6be5SKyle Evans		i = i + 1
214d04415c5SKyle Evans		kernels[i] = default_kernel
215d04415c5SKyle Evans		unique[default_kernel] = true
216088b4f5fSWarner Losh	end
217088b4f5fSWarner Losh
2183f4eb56bSKyle Evans	if v ~= nil then
219a5003419SKyle Evans		for n in v:gmatch("([^;, ]+)[;, ]?") do
2209f71d421SKyle Evans			if unique[n] == nil then
221aedd6be5SKyle Evans				i = i + 1
222aedd6be5SKyle Evans				kernels[i] = n
223aedd6be5SKyle Evans				unique[n] = true
224088b4f5fSWarner Losh			end
225088b4f5fSWarner Losh		end
2263889e6cdSKyle Evans	end
227a108046fSConrad Meyer
228c4dc9072SEmmanuel Vadot	-- Do not attempt to autodetect if underlying filesystem
229c4dc9072SEmmanuel Vadot	-- do not support directory listing (e.g. tftp, http)
230c4dc9072SEmmanuel Vadot	if not lfs.attributes("/boot", "mode") then
231c4dc9072SEmmanuel Vadot		autodetect = "no"
232c4dc9072SEmmanuel Vadot		loader.setenv("kernels_autodetect", "NO")
233c4dc9072SEmmanuel Vadot	end
234c4dc9072SEmmanuel Vadot
2353889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
2363889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
2373889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
2383889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
23935b0c718SKyle Evans		core.cached_kernels = kernels
24035b0c718SKyle Evans		return core.cached_kernels
2413f4eb56bSKyle Evans	end
2423f4eb56bSKyle Evans
243d04415c5SKyle Evans	local present = {}
244d04415c5SKyle Evans
245a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
246a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
247a108046fSConrad Meyer	-- named "kernel" is considered eligible.
248e25ee296SKyle Evans	for file, ftype in lfs.dir("/boot") do
249aedd6be5SKyle Evans		local fname = "/boot/" .. file
250a108046fSConrad Meyer
2519f71d421SKyle Evans		if file == "." or file == ".." then
252aedd6be5SKyle Evans			goto continue
253a108046fSConrad Meyer		end
254a108046fSConrad Meyer
255e25ee296SKyle Evans		if ftype then
256e25ee296SKyle Evans			if ftype ~= lfs.DT_DIR then
257e25ee296SKyle Evans				goto continue
258e25ee296SKyle Evans			end
259e25ee296SKyle Evans		elseif lfs.attributes(fname, "mode") ~= "directory" then
260aedd6be5SKyle Evans			goto continue
261a108046fSConrad Meyer		end
262a108046fSConrad Meyer
2639f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
264aedd6be5SKyle Evans			goto continue
265a108046fSConrad Meyer		end
266a108046fSConrad Meyer
2679f71d421SKyle Evans		if unique[file] == nil then
268aedd6be5SKyle Evans			i = i + 1
269aedd6be5SKyle Evans			kernels[i] = file
270aedd6be5SKyle Evans			unique[file] = true
271a108046fSConrad Meyer		end
272a108046fSConrad Meyer
273d04415c5SKyle Evans		present[file] = true
274d04415c5SKyle Evans
275a108046fSConrad Meyer		::continue::
276a108046fSConrad Meyer	end
277d04415c5SKyle Evans
278d04415c5SKyle Evans	-- If we found more than one kernel, prune the "kernel" specified kernel
279d04415c5SKyle Evans	-- off of the list if it wasn't found during traversal.  If we didn't
280d04415c5SKyle Evans	-- actually find any kernels, we just assume that they know what they're
281d04415c5SKyle Evans	-- doing and leave it alone.
282d04415c5SKyle Evans	if default_kernel and not present[default_kernel] and #kernels > 1 then
283d04415c5SKyle Evans		for i = 1, #kernels do
284d04415c5SKyle Evans			if i == #kernels then
285d04415c5SKyle Evans				kernels[i] = nil
286d04415c5SKyle Evans			else
287d04415c5SKyle Evans				kernels[i] = kernels[i + 1]
288d04415c5SKyle Evans			end
289d04415c5SKyle Evans		end
290d04415c5SKyle Evans	end
291d04415c5SKyle Evans
29235b0c718SKyle Evans	core.cached_kernels = kernels
29335b0c718SKyle Evans	return core.cached_kernels
294088b4f5fSWarner Loshend
295088b4f5fSWarner Losh
2967efc058fSKyle Evansfunction core.bootenvDefault()
2977efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2987efc058fSKyle Evansend
2997efc058fSKyle Evans
3007efc058fSKyle Evansfunction core.bootenvList()
301277f38abSMariusz Zaborski	local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count"))
3027efc058fSKyle Evans	local bootenvs = {}
3037efc058fSKyle Evans	local curenv
3047efc058fSKyle Evans	local envcount = 0
3057efc058fSKyle Evans	local unique = {}
3067efc058fSKyle Evans
3077efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
3087efc058fSKyle Evans		return bootenvs
3097efc058fSKyle Evans	end
3107efc058fSKyle Evans
3117efc058fSKyle Evans	-- Currently selected bootenv is always first/default
312277f38abSMariusz Zaborski	-- On the rewinded list the bootenv may not exists
313277f38abSMariusz Zaborski	if core.isRewinded() then
314277f38abSMariusz Zaborski		curenv = core.bootenvDefaultRewinded()
315277f38abSMariusz Zaborski	else
3167efc058fSKyle Evans		curenv = core.bootenvDefault()
317277f38abSMariusz Zaborski	end
3187efc058fSKyle Evans	if curenv ~= nil then
3197efc058fSKyle Evans		envcount = envcount + 1
3207efc058fSKyle Evans		bootenvs[envcount] = curenv
3217efc058fSKyle Evans		unique[curenv] = true
3227efc058fSKyle Evans	end
3237efc058fSKyle Evans
3247efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
325277f38abSMariusz Zaborski		curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]")
3267efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
3277efc058fSKyle Evans			envcount = envcount + 1
3287efc058fSKyle Evans			bootenvs[envcount] = curenv
3297efc058fSKyle Evans			unique[curenv] = true
3307efc058fSKyle Evans		end
3317efc058fSKyle Evans	end
3327efc058fSKyle Evans	return bootenvs
3337efc058fSKyle Evansend
3347efc058fSKyle Evans
335277f38abSMariusz Zaborskifunction core.isCheckpointed()
336277f38abSMariusz Zaborski	return loader.getenv("zpool_checkpoint") ~= nil
337277f38abSMariusz Zaborskiend
338277f38abSMariusz Zaborski
339277f38abSMariusz Zaborskifunction core.bootenvDefaultRewinded()
340277f38abSMariusz Zaborski	local defname =  "zfs:!" .. string.sub(core.bootenvDefault(), 5)
341277f38abSMariusz Zaborski	local bootenv_count = tonumber("bootenvs_check_count")
342277f38abSMariusz Zaborski
343277f38abSMariusz Zaborski	if bootenv_count == nil or bootenv_count <= 0 then
344277f38abSMariusz Zaborski		return defname
345277f38abSMariusz Zaborski	end
346277f38abSMariusz Zaborski
347277f38abSMariusz Zaborski	for curenv_idx = 0, bootenv_count - 1 do
34894510c29SKyle Evans		local curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]")
349277f38abSMariusz Zaborski		if curenv == defname then
350277f38abSMariusz Zaborski			return defname
351277f38abSMariusz Zaborski		end
352277f38abSMariusz Zaborski	end
353277f38abSMariusz Zaborski
354277f38abSMariusz Zaborski	return loader.getenv("bootenvs_check[0]")
355277f38abSMariusz Zaborskiend
356277f38abSMariusz Zaborski
357277f38abSMariusz Zaborskifunction core.isRewinded()
358277f38abSMariusz Zaborski	return bootenv_list == "bootenvs_check"
359277f38abSMariusz Zaborskiend
360277f38abSMariusz Zaborski
361277f38abSMariusz Zaborskifunction core.changeRewindCheckpoint()
362277f38abSMariusz Zaborski	if core.isRewinded() then
363277f38abSMariusz Zaborski		bootenv_list = "bootenvs"
364277f38abSMariusz Zaborski	else
365277f38abSMariusz Zaborski		bootenv_list = "bootenvs_check"
366277f38abSMariusz Zaborski	end
367277f38abSMariusz Zaborskiend
368277f38abSMariusz Zaborski
3695c73b3e0SColin Percivalfunction core.loadEntropy()
3705c73b3e0SColin Percival	if core.isUEFIBoot() then
3715c73b3e0SColin Percival		if (loader.getenv("entropy_efi_seed") or "no"):lower() == "yes" then
372f4a69a93SColin Percival			local seedsize = loader.getenv("entropy_efi_seed_size") or "2048"
373f4a69a93SColin Percival			loader.perform("efi-seed-entropy " .. seedsize)
3745c73b3e0SColin Percival		end
3755c73b3e0SColin Percival	end
3765c73b3e0SColin Percivalend
3775c73b3e0SColin Percival
378088b4f5fSWarner Loshfunction core.setDefaults()
379e0f3dc82SR. Christian McDonald	core.setACPI(default_acpi)
3801613f091SKyle Evans	core.setSafeMode(default_safe_mode)
3811613f091SKyle Evans	core.setSingleUser(default_single_user)
3821613f091SKyle Evans	core.setVerbose(default_verbose)
383088b4f5fSWarner Loshend
384088b4f5fSWarner Losh
3856d4ed94dSKyle Evansfunction core.autoboot(argstr)
386a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
387a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
388aedd6be5SKyle Evans		config.loadelf()
389a2a7830eSKyle Evans	end
3905c73b3e0SColin Percival	core.loadEntropy()
391322a2dddSKyle Evans	loader.perform(composeLoaderCmd("autoboot", argstr))
392088b4f5fSWarner Loshend
393088b4f5fSWarner Losh
3946d4ed94dSKyle Evansfunction core.boot(argstr)
395a2a7830eSKyle Evans	-- loadelf() only if we've not already loaded a kernel
396a2a7830eSKyle Evans	if loader.getenv("kernelname") == nil then
397aedd6be5SKyle Evans		config.loadelf()
398a2a7830eSKyle Evans	end
3995c73b3e0SColin Percival	core.loadEntropy()
400322a2dddSKyle Evans	loader.perform(composeLoaderCmd("boot", argstr))
401088b4f5fSWarner Loshend
402088b4f5fSWarner Losh
4031631382cSKyle Evansfunction core.hasFeature(name)
4041631382cSKyle Evans	if not loader.has_feature then
4051631382cSKyle Evans		-- Loader too old, no feature support
4061631382cSKyle Evans		return nil, "No feature support in loaded loader"
4071631382cSKyle Evans	end
4081631382cSKyle Evans
4091631382cSKyle Evans	return loader.has_feature(name)
4101631382cSKyle Evansend
4111631382cSKyle Evans
412e07fc39cSKyle Evansfunction core.isSingleUserBoot()
413aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
414aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
415e07fc39cSKyle Evansend
416e07fc39cSKyle Evans
4175f8cfbe1SKyle Evansfunction core.isUEFIBoot()
4185f8cfbe1SKyle Evans	local efiver = loader.getenv("efi-version")
4195f8cfbe1SKyle Evans
4205f8cfbe1SKyle Evans	return efiver ~= nil
4215f8cfbe1SKyle Evansend
4225f8cfbe1SKyle Evans
4237efc058fSKyle Evansfunction core.isZFSBoot()
4247efc058fSKyle Evans	local c = loader.getenv("currdev")
4257efc058fSKyle Evans
4267efc058fSKyle Evans	if c ~= nil then
4277efc058fSKyle Evans		return c:match("^zfs:") ~= nil
4287efc058fSKyle Evans	end
4297efc058fSKyle Evans	return false
4307efc058fSKyle Evansend
4317efc058fSKyle Evans
4323630506bSToomas Soomefunction core.isFramebufferConsole()
4333630506bSToomas Soome	local c = loader.getenv("console")
4343630506bSToomas Soome	if c ~= nil then
4353630506bSToomas Soome		if c:find("efi") == nil and c:find("vidconsole") == nil then
4363630506bSToomas Soome			return false
4373630506bSToomas Soome		end
4383630506bSToomas Soome		if loader.getenv("screen.depth") ~= nil then
4393630506bSToomas Soome			return true
4403630506bSToomas Soome		end
4413630506bSToomas Soome	end
4423630506bSToomas Soome	return false
4433630506bSToomas Soomeend
4443630506bSToomas Soome
44590a25417SKyle Evansfunction core.isSerialConsole()
44690a25417SKyle Evans	local c = loader.getenv("console")
44790a25417SKyle Evans	if c ~= nil then
4485d8c062fSToomas Soome		-- serial console is comconsole, but also userboot.
4495d8c062fSToomas Soome		-- userboot is there, because we have no way to know
4505d8c062fSToomas Soome		-- if the user terminal can draw unicode box chars or not.
4515d8c062fSToomas Soome		if c:find("comconsole") ~= nil or c:find("userboot") ~= nil then
45290a25417SKyle Evans			return true
45390a25417SKyle Evans		end
45490a25417SKyle Evans	end
45590a25417SKyle Evans	return false
45690a25417SKyle Evansend
45790a25417SKyle Evans
458b140d14bSKyle Evansfunction core.isSerialBoot()
459aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
4609f71d421SKyle Evans	if s ~= nil then
461aedd6be5SKyle Evans		return true
462088b4f5fSWarner Losh	end
463088b4f5fSWarner Losh
464aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
4659f71d421SKyle Evans	if m ~= nil then
466aedd6be5SKyle Evans		return true
467088b4f5fSWarner Losh	end
468aedd6be5SKyle Evans	return false
469088b4f5fSWarner Loshend
470088b4f5fSWarner Losh
4719937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted?
4729937e979SKyle Evansfunction core.isMenuSkipped()
473b83a355dSKyle Evans	return string.lower(loader.getenv("beastie_disable") or "") == "yes"
4749937e979SKyle Evansend
4759937e979SKyle Evans
4765c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
477ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
478aedd6be5SKyle Evans	local new_tbl = {}
4795c1b5165SKyle Evans	for k, v in pairs(tbl) do
4809f71d421SKyle Evans		if type(v) == "table" then
481ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
4825c1b5165SKyle Evans		else
483aedd6be5SKyle Evans			new_tbl[k] = v
4845c1b5165SKyle Evans		end
4855c1b5165SKyle Evans	end
486aedd6be5SKyle Evans	return new_tbl
4875c1b5165SKyle Evansend
4885c1b5165SKyle Evans
4896d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
4906d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
4916d4ed94dSKyle Evans-- for our uses
4926d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
4936d4ed94dSKyle Evans	-- Shouldn't reasonably happen
4949f71d421SKyle Evans	if #tbl == 0 then
495aedd6be5SKyle Evans		return nil, nil
4969f71d421SKyle Evans	elseif #tbl == 1 then
497aedd6be5SKyle Evans		return tbl[1], {}
4986d4ed94dSKyle Evans	end
4996d4ed94dSKyle Evans
500aedd6be5SKyle Evans	local first_value = tbl[1]
501aedd6be5SKyle Evans	local new_tbl = {}
5026d4ed94dSKyle Evans	-- This is not a cheap operation
5036d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
5049f71d421SKyle Evans		if k > 1 then
505aedd6be5SKyle Evans			new_tbl[k - 1] = v
5066d4ed94dSKyle Evans		end
5076d4ed94dSKyle Evans	end
5086d4ed94dSKyle Evans
509aedd6be5SKyle Evans	return first_value, new_tbl
5106d4ed94dSKyle Evansend
5116d4ed94dSKyle Evans
5128f3b3610SWarner Loshfunction core.getConsoleName()
5138f3b3610SWarner Losh	if loader.getenv("boot_multicons") ~= nil then
5148f3b3610SWarner Losh		if loader.getenv("boot_serial") ~= nil then
5158f3b3610SWarner Losh			return "Dual (Serial primary)"
5168f3b3610SWarner Losh		else
5178f3b3610SWarner Losh			return "Dual (Video primary)"
5188f3b3610SWarner Losh		end
5198f3b3610SWarner Losh	else
5208f3b3610SWarner Losh		if loader.getenv("boot_serial") ~= nil then
5218f3b3610SWarner Losh			return "Serial"
5228f3b3610SWarner Losh		else
5238f3b3610SWarner Losh			return "Video"
5248f3b3610SWarner Losh		end
5258f3b3610SWarner Losh	end
5268f3b3610SWarner Loshend
5278f3b3610SWarner Losh
5288f3b3610SWarner Loshfunction core.nextConsoleChoice()
5298f3b3610SWarner Losh	if loader.getenv("boot_multicons") ~= nil then
5308f3b3610SWarner Losh		if loader.getenv("boot_serial") ~= nil then
5318f3b3610SWarner Losh			loader.unsetenv("boot_serial")
5328f3b3610SWarner Losh		else
5338f3b3610SWarner Losh			loader.unsetenv("boot_multicons")
5348f3b3610SWarner Losh			loader.setenv("boot_serial", "YES")
5358f3b3610SWarner Losh		end
5368f3b3610SWarner Losh	else
5378f3b3610SWarner Losh		if loader.getenv("boot_serial") ~= nil then
5388f3b3610SWarner Losh			loader.unsetenv("boot_serial")
5398f3b3610SWarner Losh		else
5408f3b3610SWarner Losh			loader.setenv("boot_multicons", "YES")
5418f3b3610SWarner Losh			loader.setenv("boot_serial", "YES")
5428f3b3610SWarner Losh		end
5438f3b3610SWarner Losh	end
5448f3b3610SWarner Loshend
5458f3b3610SWarner Losh
546*c2ba66d4SWarner Losh-- The graphical-enabled loaders have unicode drawing character support. The
547*c2ba66d4SWarner Losh-- text-only ones do not. We check the old and new bindings for term_drawrect as
548*c2ba66d4SWarner Losh-- a proxy for unicode support, which will work on older boot loaders as well
549*c2ba66d4SWarner Losh-- as be future proof for when we remove the old binding. This also abstracts
550*c2ba66d4SWarner Losh-- out the test to one spot in case we start to export this notion more directly.
551*c2ba66d4SWarner Loshfunction core.hasUnicode()
552*c2ba66d4SWarner Losh	 return gfx.term_drawrect ~= nil or loader.term_drawrect ~= nil
553*c2ba66d4SWarner Loshend
554*c2ba66d4SWarner Losh
555a2fd7ae8SWarner Losh-- Sanity check the boot loader revision
556a2fd7ae8SWarner Losh-- Loaders with version 3.0 have everything that we need without backwards
557a2fd7ae8SWarner Losh-- compatible hacks. Warn users that still have old versions to upgrade so
558a2fd7ae8SWarner Losh-- that we can remove the backwards compatible hacks in the future since
559a2fd7ae8SWarner Losh-- they have been there a long time.
560a2fd7ae8SWarner Loshlocal loader_major = 3
561a2fd7ae8SWarner Losh
562a2fd7ae8SWarner Loshfunction core.loaderTooOld()
563a2fd7ae8SWarner Losh	return loader.version == nil or loader.version < loader_major * 1000
564a2fd7ae8SWarner Loshend
565a2fd7ae8SWarner Losh
566a2fd7ae8SWarner Loshif core.loaderTooOld() then
567a2fd7ae8SWarner Losh	print("**********************************************************************")
568a2fd7ae8SWarner Losh	print("**********************************************************************")
569a2fd7ae8SWarner Losh	print("*****                                                            *****")
570a2fd7ae8SWarner Losh	print("*****           BOOT LOADER IS TOO OLD. PLEASE UPGRADE.          *****")
571a2fd7ae8SWarner Losh	print("*****                                                            *****")
572a2fd7ae8SWarner Losh	print("**********************************************************************")
573a2fd7ae8SWarner Losh	print("**********************************************************************")
574a2fd7ae8SWarner Loshend
575a2fd7ae8SWarner Losh
5761613f091SKyle EvansrecordDefaults()
577aea262bfSKyle Evanshook.register("config.reloaded", core.clearCachedKernels)
578aedd6be5SKyle Evansreturn core
579