xref: /freebsd/stand/lua/core.lua (revision ee4e69f1b9b3fd5774d00c87e1a0bbf11859e24d)
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
366d4ed94dSKyle Evanslocal compose_loader_cmd = function(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"
5039006570SKyle Evans
51aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
52aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
53aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
54aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
55aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
56a7cf0562SKyle Evans
57088b4f5fSWarner Loshfunction core.setVerbose(b)
589f71d421SKyle Evans	if b == nil then
59aedd6be5SKyle Evans		b = not core.verbose
60088b4f5fSWarner Losh	end
61088b4f5fSWarner Losh
6249550489SKyle Evans	if b then
63aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
64088b4f5fSWarner Losh	else
65aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
66088b4f5fSWarner Losh	end
67aedd6be5SKyle Evans	core.verbose = b
68088b4f5fSWarner Loshend
69088b4f5fSWarner Losh
70088b4f5fSWarner Loshfunction core.setSingleUser(b)
719f71d421SKyle Evans	if b == nil then
72aedd6be5SKyle Evans		b = not core.su
73088b4f5fSWarner Losh	end
74088b4f5fSWarner Losh
7549550489SKyle Evans	if b then
76aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
77088b4f5fSWarner Losh	else
78aedd6be5SKyle Evans		loader.unsetenv("boot_single")
79088b4f5fSWarner Losh	end
80aedd6be5SKyle Evans	core.su = b
81088b4f5fSWarner Loshend
82088b4f5fSWarner Losh
836401094fSKyle Evansfunction core.getACPIPresent(checkingSystemDefaults)
84aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
856401094fSKyle Evans
869f71d421SKyle Evans	if c ~= nil then
8749550489SKyle Evans		if checkingSystemDefaults then
88aedd6be5SKyle Evans			return true
896401094fSKyle Evans		end
906401094fSKyle Evans		-- Otherwise, respect disabled if it's set
91aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
929f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
936401094fSKyle Evans	end
94aedd6be5SKyle Evans	return false
956401094fSKyle Evansend
966401094fSKyle Evans
97088b4f5fSWarner Loshfunction core.setACPI(b)
989f71d421SKyle Evans	if b == nil then
99aedd6be5SKyle Evans		b = not core.acpi
100088b4f5fSWarner Losh	end
101088b4f5fSWarner Losh
10249550489SKyle Evans	if b then
103aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
104aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
105aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
106088b4f5fSWarner Losh	else
107aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
108aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
109aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
110088b4f5fSWarner Losh	end
111aedd6be5SKyle Evans	core.acpi = b
112088b4f5fSWarner Loshend
113088b4f5fSWarner Losh
114088b4f5fSWarner Loshfunction core.setSafeMode(b)
1159f71d421SKyle Evans	if b == nil then
116aedd6be5SKyle Evans		b = not core.sm
117088b4f5fSWarner Losh	end
11849550489SKyle Evans	if b then
119aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
120aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
121aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
122aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
123aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
124aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
125aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
126088b4f5fSWarner Losh	else
127aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
128aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
129aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
130aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
131aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
132aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
133aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
134088b4f5fSWarner Losh	end
135aedd6be5SKyle Evans	core.sm = b
136088b4f5fSWarner Loshend
137088b4f5fSWarner Losh
138088b4f5fSWarner Loshfunction core.kernelList()
139aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1403f4eb56bSKyle Evans	local v = loader.getenv("kernels")
1413889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
142088b4f5fSWarner Losh
143aedd6be5SKyle Evans	local kernels = {}
144aedd6be5SKyle Evans	local unique = {}
145aedd6be5SKyle Evans	local i = 0
1469f71d421SKyle Evans	if k ~= nil then
147aedd6be5SKyle Evans		i = i + 1
148aedd6be5SKyle Evans		kernels[i] = k
149aedd6be5SKyle Evans		unique[k] = true
150088b4f5fSWarner Losh	end
151088b4f5fSWarner Losh
1523f4eb56bSKyle Evans	if v ~= nil then
153088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
1549f71d421SKyle Evans			if unique[n] == nil then
155aedd6be5SKyle Evans				i = i + 1
156aedd6be5SKyle Evans				kernels[i] = n
157aedd6be5SKyle Evans				unique[n] = true
158088b4f5fSWarner Losh			end
159088b4f5fSWarner Losh		end
1603889e6cdSKyle Evans	end
161a108046fSConrad Meyer
1623889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
1633889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
1643889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
1653889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
1663f4eb56bSKyle Evans		return kernels
1673f4eb56bSKyle Evans	end
1683f4eb56bSKyle Evans
169a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
170a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
171a108046fSConrad Meyer	-- named "kernel" is considered eligible.
172a108046fSConrad Meyer	for file in lfs.dir("/boot") do
173aedd6be5SKyle Evans		local fname = "/boot/" .. file
174a108046fSConrad Meyer
1759f71d421SKyle Evans		if file == "." or file == ".." then
176aedd6be5SKyle Evans			goto continue
177a108046fSConrad Meyer		end
178a108046fSConrad Meyer
1799f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
180aedd6be5SKyle Evans			goto continue
181a108046fSConrad Meyer		end
182a108046fSConrad Meyer
1839f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
184aedd6be5SKyle Evans			goto continue
185a108046fSConrad Meyer		end
186a108046fSConrad Meyer
1879f71d421SKyle Evans		if unique[file] == nil then
188aedd6be5SKyle Evans			i = i + 1
189aedd6be5SKyle Evans			kernels[i] = file
190aedd6be5SKyle Evans			unique[file] = true
191a108046fSConrad Meyer		end
192a108046fSConrad Meyer
193a108046fSConrad Meyer		::continue::
194a108046fSConrad Meyer	end
195aedd6be5SKyle Evans	return kernels
196088b4f5fSWarner Loshend
197088b4f5fSWarner Losh
1987efc058fSKyle Evansfunction core.bootenvDefault()
1997efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2007efc058fSKyle Evansend
2017efc058fSKyle Evans
2027efc058fSKyle Evansfunction core.bootenvList()
2037efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2047efc058fSKyle Evans	local bootenvs = {}
2057efc058fSKyle Evans	local curenv
2067efc058fSKyle Evans	local envcount = 0
2077efc058fSKyle Evans	local unique = {}
2087efc058fSKyle Evans
2097efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2107efc058fSKyle Evans		return bootenvs
2117efc058fSKyle Evans	end
2127efc058fSKyle Evans
2137efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2147efc058fSKyle Evans	curenv = core.bootenvDefault()
2157efc058fSKyle Evans	if curenv ~= nil then
2167efc058fSKyle Evans		envcount = envcount + 1
2177efc058fSKyle Evans		bootenvs[envcount] = curenv
2187efc058fSKyle Evans		unique[curenv] = true
2197efc058fSKyle Evans	end
2207efc058fSKyle Evans
2217efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2227efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2237efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2247efc058fSKyle Evans			envcount = envcount + 1
2257efc058fSKyle Evans			bootenvs[envcount] = curenv
2267efc058fSKyle Evans			unique[curenv] = true
2277efc058fSKyle Evans		end
2287efc058fSKyle Evans	end
2297efc058fSKyle Evans	return bootenvs
2307efc058fSKyle Evansend
2317efc058fSKyle Evans
232088b4f5fSWarner Loshfunction core.setDefaults()
233aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
234aedd6be5SKyle Evans	core.setSafeMode(false)
235aedd6be5SKyle Evans	core.setSingleUser(false)
236aedd6be5SKyle Evans	core.setVerbose(false)
237088b4f5fSWarner Loshend
238088b4f5fSWarner Losh
2396d4ed94dSKyle Evansfunction core.autoboot(argstr)
240aedd6be5SKyle Evans	config.loadelf()
241aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("autoboot", argstr))
242088b4f5fSWarner Loshend
243088b4f5fSWarner Losh
2446d4ed94dSKyle Evansfunction core.boot(argstr)
245aedd6be5SKyle Evans	config.loadelf()
246aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("boot", argstr))
247088b4f5fSWarner Loshend
248088b4f5fSWarner Losh
249e07fc39cSKyle Evansfunction core.isSingleUserBoot()
250aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
251aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
252e07fc39cSKyle Evansend
253e07fc39cSKyle Evans
2547efc058fSKyle Evansfunction core.isZFSBoot()
2557efc058fSKyle Evans	local c = loader.getenv("currdev")
2567efc058fSKyle Evans
2577efc058fSKyle Evans	if c ~= nil then
2587efc058fSKyle Evans		return c:match("^zfs:") ~= nil
2597efc058fSKyle Evans	end
2607efc058fSKyle Evans	return false
2617efc058fSKyle Evansend
2627efc058fSKyle Evans
263b140d14bSKyle Evansfunction core.isSerialBoot()
264aedd6be5SKyle Evans	local c = loader.getenv("console")
265088b4f5fSWarner Losh
2669f71d421SKyle Evans	if c ~= nil then
2679f71d421SKyle Evans		if c:find("comconsole") ~= nil then
268aedd6be5SKyle Evans			return true
269088b4f5fSWarner Losh		end
270088b4f5fSWarner Losh	end
271088b4f5fSWarner Losh
272aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
2739f71d421SKyle Evans	if s ~= nil then
274aedd6be5SKyle Evans		return true
275088b4f5fSWarner Losh	end
276088b4f5fSWarner Losh
277aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
2789f71d421SKyle Evans	if m ~= nil then
279aedd6be5SKyle Evans		return true
280088b4f5fSWarner Losh	end
281aedd6be5SKyle Evans	return false
282088b4f5fSWarner Loshend
283088b4f5fSWarner Losh
284c1ab36f5SKyle Evansfunction core.isSystem386()
2859f71d421SKyle Evans	return loader.machine_arch == "i386"
286c1ab36f5SKyle Evansend
287c1ab36f5SKyle Evans
2885c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
289*ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl)
290aedd6be5SKyle Evans	local new_tbl = {}
2915c1b5165SKyle Evans	for k, v in pairs(tbl) do
2929f71d421SKyle Evans		if type(v) == "table" then
293*ee4e69f1SKyle Evans			new_tbl[k] = core.deepCopyTable(v)
2945c1b5165SKyle Evans		else
295aedd6be5SKyle Evans			new_tbl[k] = v
2965c1b5165SKyle Evans		end
2975c1b5165SKyle Evans	end
298aedd6be5SKyle Evans	return new_tbl
2995c1b5165SKyle Evansend
3005c1b5165SKyle Evans
3016d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3026d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3036d4ed94dSKyle Evans-- for our uses
3046d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3056d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3069f71d421SKyle Evans	if #tbl == 0 then
307aedd6be5SKyle Evans		return nil, nil
3089f71d421SKyle Evans	elseif #tbl == 1 then
309aedd6be5SKyle Evans		return tbl[1], {}
3106d4ed94dSKyle Evans	end
3116d4ed94dSKyle Evans
312aedd6be5SKyle Evans	local first_value = tbl[1]
313aedd6be5SKyle Evans	local new_tbl = {}
3146d4ed94dSKyle Evans	-- This is not a cheap operation
3156d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3169f71d421SKyle Evans		if k > 1 then
317aedd6be5SKyle Evans			new_tbl[k - 1] = v
3186d4ed94dSKyle Evans		end
3196d4ed94dSKyle Evans	end
3206d4ed94dSKyle Evans
321aedd6be5SKyle Evans	return first_value, new_tbl
3226d4ed94dSKyle Evansend
3236d4ed94dSKyle Evans
3246f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3256f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3266f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3276f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3289f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
329aedd6be5SKyle Evans	core.setACPI(true)
3306f412147SKyle Evansend
331aedd6be5SKyle Evansreturn core
332