xref: /freebsd/stand/lua/core.lua (revision 3889e6cd04b6126c8380c0dff397b09d6ed45eae)
1088b4f5fSWarner Losh--
2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3088b4f5fSWarner Losh-- All rights reserved.
4088b4f5fSWarner Losh--
5088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
6088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
7088b4f5fSWarner Losh-- are met:
8088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
9088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
10088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
11088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
12088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
13088b4f5fSWarner Losh--
14088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24088b4f5fSWarner Losh-- SUCH DAMAGE.
25088b4f5fSWarner Losh--
26088b4f5fSWarner Losh-- $FreeBSD$
27088b4f5fSWarner Losh--
28088b4f5fSWarner Losh
29aedd6be5SKyle Evanslocal config = require('config')
30fa4a2394SKyle Evans
31aedd6be5SKyle Evanslocal core = {}
32088b4f5fSWarner Losh
336d4ed94dSKyle Evanslocal compose_loader_cmd = function(cmd_name, argstr)
349f71d421SKyle Evans	if argstr ~= nil then
35aedd6be5SKyle Evans		cmd_name = cmd_name .. " " .. argstr
366d4ed94dSKyle Evans	end
37aedd6be5SKyle Evans	return cmd_name
386d4ed94dSKyle Evansend
396d4ed94dSKyle Evans
406d3bcc06SKyle Evans-- Internal function
41f0cb3b6bSKyle Evans-- Parses arguments to boot and returns two values: kernel_name, argstr
42f0cb3b6bSKyle Evans-- Defaults to nil and "" respectively.
436d3bcc06SKyle Evans-- This will also parse arguments to autoboot, but the with_kernel argument
446d3bcc06SKyle Evans-- will need to be explicitly overwritten to false
456d3bcc06SKyle Evanslocal parse_boot_args = function(argv, with_kernel)
469f71d421SKyle Evans	if with_kernel == nil then
47aedd6be5SKyle Evans		with_kernel = true
486d3bcc06SKyle Evans	end
491d38e553SKyle Evans	if #argv == 0 then
501d38e553SKyle Evans		if with_kernel then
511d38e553SKyle Evans			return nil, ""
521d38e553SKyle Evans		else
531d38e553SKyle Evans			return ""
541d38e553SKyle Evans		end
551d38e553SKyle Evans	end
56aedd6be5SKyle Evans	local kernel_name
57aedd6be5SKyle Evans	local argstr = ""
58f0cb3b6bSKyle Evans
59f0cb3b6bSKyle Evans	for k, v in ipairs(argv) do
609f71d421SKyle Evans		if with_kernel and v:sub(1,1) ~= "-" then
61aedd6be5SKyle Evans			kernel_name = v
62f0cb3b6bSKyle Evans		else
63aedd6be5SKyle Evans			argstr = argstr .. " " .. v
64f0cb3b6bSKyle Evans		end
65f0cb3b6bSKyle Evans	end
669f71d421SKyle Evans	if with_kernel then
67aedd6be5SKyle Evans		return kernel_name, argstr
686d3bcc06SKyle Evans	else
69aedd6be5SKyle Evans		return argstr
706d3bcc06SKyle Evans	end
71f0cb3b6bSKyle Evansend
72f0cb3b6bSKyle Evans
73f0cb3b6bSKyle Evans-- Globals
74f0cb3b6bSKyle Evansfunction boot(...)
75aedd6be5SKyle Evans	local argv = {...}
76aedd6be5SKyle Evans	local cmd_name = ""
77aedd6be5SKyle Evans	cmd_name, argv = core.popFrontTable(argv)
78aedd6be5SKyle Evans	local kernel, argstr = parse_boot_args(argv)
799f71d421SKyle Evans	if kernel ~= nil then
80aedd6be5SKyle Evans		loader.perform("unload")
81aedd6be5SKyle Evans		config.selectkernel(kernel)
82f0cb3b6bSKyle Evans	end
83aedd6be5SKyle Evans	core.boot(argstr)
84f0cb3b6bSKyle Evansend
85f0cb3b6bSKyle Evans
866d3bcc06SKyle Evansfunction autoboot(...)
876d3bcc06SKyle Evans	local argv = {...}
88aedd6be5SKyle Evans	local cmd_name = ""
89aedd6be5SKyle Evans	cmd_name, argv = core.popFrontTable(argv)
90aedd6be5SKyle Evans	local argstr = parse_boot_args(argv, false)
91aedd6be5SKyle Evans	core.autoboot(argstr)
926d3bcc06SKyle Evansend
936d3bcc06SKyle Evans
94b5746545SKyle Evans-- Module exports
95fe672a15SKyle Evans-- Commonly appearing constants
96aedd6be5SKyle Evanscore.KEY_BACKSPACE	= 8
97aedd6be5SKyle Evanscore.KEY_ENTER		= 13
98aedd6be5SKyle Evanscore.KEY_DELETE		= 127
99fe672a15SKyle Evans
100aedd6be5SKyle Evanscore.KEYSTR_ESCAPE	= "\027"
10139006570SKyle Evans
102aedd6be5SKyle Evanscore.MENU_RETURN	= "return"
103aedd6be5SKyle Evanscore.MENU_ENTRY		= "entry"
104aedd6be5SKyle Evanscore.MENU_SEPARATOR	= "separator"
105aedd6be5SKyle Evanscore.MENU_SUBMENU	= "submenu"
106aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY	= "carousel_entry"
107a7cf0562SKyle Evans
108088b4f5fSWarner Loshfunction core.setVerbose(b)
1099f71d421SKyle Evans	if b == nil then
110aedd6be5SKyle Evans		b = not core.verbose
111088b4f5fSWarner Losh	end
112088b4f5fSWarner Losh
11349550489SKyle Evans	if b then
114aedd6be5SKyle Evans		loader.setenv("boot_verbose", "YES")
115088b4f5fSWarner Losh	else
116aedd6be5SKyle Evans		loader.unsetenv("boot_verbose")
117088b4f5fSWarner Losh	end
118aedd6be5SKyle Evans	core.verbose = b
119088b4f5fSWarner Loshend
120088b4f5fSWarner Losh
121088b4f5fSWarner Loshfunction core.setSingleUser(b)
1229f71d421SKyle Evans	if b == nil then
123aedd6be5SKyle Evans		b = not core.su
124088b4f5fSWarner Losh	end
125088b4f5fSWarner Losh
12649550489SKyle Evans	if b then
127aedd6be5SKyle Evans		loader.setenv("boot_single", "YES")
128088b4f5fSWarner Losh	else
129aedd6be5SKyle Evans		loader.unsetenv("boot_single")
130088b4f5fSWarner Losh	end
131aedd6be5SKyle Evans	core.su = b
132088b4f5fSWarner Loshend
133088b4f5fSWarner Losh
1346401094fSKyle Evansfunction core.getACPIPresent(checkingSystemDefaults)
135aedd6be5SKyle Evans	local c = loader.getenv("hint.acpi.0.rsdp")
1366401094fSKyle Evans
1379f71d421SKyle Evans	if c ~= nil then
13849550489SKyle Evans		if checkingSystemDefaults then
139aedd6be5SKyle Evans			return true
1406401094fSKyle Evans		end
1416401094fSKyle Evans		-- Otherwise, respect disabled if it's set
142aedd6be5SKyle Evans		c = loader.getenv("hint.acpi.0.disabled")
1439f71d421SKyle Evans		return c == nil or tonumber(c) ~= 1
1446401094fSKyle Evans	end
145aedd6be5SKyle Evans	return false
1466401094fSKyle Evansend
1476401094fSKyle Evans
148088b4f5fSWarner Loshfunction core.setACPI(b)
1499f71d421SKyle Evans	if b == nil then
150aedd6be5SKyle Evans		b = not core.acpi
151088b4f5fSWarner Losh	end
152088b4f5fSWarner Losh
15349550489SKyle Evans	if b then
154aedd6be5SKyle Evans		loader.setenv("acpi_load", "YES")
155aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "0")
156aedd6be5SKyle Evans		loader.unsetenv("loader.acpi_disabled_by_user")
157088b4f5fSWarner Losh	else
158aedd6be5SKyle Evans		loader.unsetenv("acpi_load")
159aedd6be5SKyle Evans		loader.setenv("hint.acpi.0.disabled", "1")
160aedd6be5SKyle Evans		loader.setenv("loader.acpi_disabled_by_user", "1")
161088b4f5fSWarner Losh	end
162aedd6be5SKyle Evans	core.acpi = b
163088b4f5fSWarner Loshend
164088b4f5fSWarner Losh
165088b4f5fSWarner Loshfunction core.setSafeMode(b)
1669f71d421SKyle Evans	if b == nil then
167aedd6be5SKyle Evans		b = not core.sm
168088b4f5fSWarner Losh	end
16949550489SKyle Evans	if b then
170aedd6be5SKyle Evans		loader.setenv("kern.smp.disabled", "1")
171aedd6be5SKyle Evans		loader.setenv("hw.ata.ata_dma", "0")
172aedd6be5SKyle Evans		loader.setenv("hw.ata.atapi_dma", "0")
173aedd6be5SKyle Evans		loader.setenv("hw.ata.wc", "0")
174aedd6be5SKyle Evans		loader.setenv("hw.eisa_slots", "0")
175aedd6be5SKyle Evans		loader.setenv("kern.eventtimer.periodic", "1")
176aedd6be5SKyle Evans		loader.setenv("kern.geom.part.check_integrity", "0")
177088b4f5fSWarner Losh	else
178aedd6be5SKyle Evans		loader.unsetenv("kern.smp.disabled")
179aedd6be5SKyle Evans		loader.unsetenv("hw.ata.ata_dma")
180aedd6be5SKyle Evans		loader.unsetenv("hw.ata.atapi_dma")
181aedd6be5SKyle Evans		loader.unsetenv("hw.ata.wc")
182aedd6be5SKyle Evans		loader.unsetenv("hw.eisa_slots")
183aedd6be5SKyle Evans		loader.unsetenv("kern.eventtimer.periodic")
184aedd6be5SKyle Evans		loader.unsetenv("kern.geom.part.check_integrity")
185088b4f5fSWarner Losh	end
186aedd6be5SKyle Evans	core.sm = b
187088b4f5fSWarner Loshend
188088b4f5fSWarner Losh
189088b4f5fSWarner Loshfunction core.kernelList()
190aedd6be5SKyle Evans	local k = loader.getenv("kernel")
1913f4eb56bSKyle Evans	local v = loader.getenv("kernels")
192*3889e6cdSKyle Evans	local autodetect = loader.getenv("kernels_autodetect") or ""
193088b4f5fSWarner Losh
194aedd6be5SKyle Evans	local kernels = {}
195aedd6be5SKyle Evans	local unique = {}
196aedd6be5SKyle Evans	local i = 0
1979f71d421SKyle Evans	if k ~= nil then
198aedd6be5SKyle Evans		i = i + 1
199aedd6be5SKyle Evans		kernels[i] = k
200aedd6be5SKyle Evans		unique[k] = true
201088b4f5fSWarner Losh	end
202088b4f5fSWarner Losh
2033f4eb56bSKyle Evans	if v ~= nil then
204088b4f5fSWarner Losh		for n in v:gmatch("([^; ]+)[; ]?") do
2059f71d421SKyle Evans			if unique[n] == nil then
206aedd6be5SKyle Evans				i = i + 1
207aedd6be5SKyle Evans				kernels[i] = n
208aedd6be5SKyle Evans				unique[n] = true
209088b4f5fSWarner Losh			end
210088b4f5fSWarner Losh		end
211*3889e6cdSKyle Evans	end
212a108046fSConrad Meyer
213*3889e6cdSKyle Evans	-- Base whether we autodetect kernels or not on a loader.conf(5)
214*3889e6cdSKyle Evans	-- setting, kernels_autodetect. If it's set to 'yes', we'll add
215*3889e6cdSKyle Evans	-- any kernels we detect based on the criteria described.
216*3889e6cdSKyle Evans	if autodetect:lower() ~= "yes" then
2173f4eb56bSKyle Evans		return kernels
2183f4eb56bSKyle Evans	end
2193f4eb56bSKyle Evans
220a108046fSConrad Meyer	-- Automatically detect other bootable kernel directories using a
221a108046fSConrad Meyer	-- heuristic.  Any directory in /boot that contains an ordinary file
222a108046fSConrad Meyer	-- named "kernel" is considered eligible.
223a108046fSConrad Meyer	for file in lfs.dir("/boot") do
224aedd6be5SKyle Evans		local fname = "/boot/" .. file
225a108046fSConrad Meyer
2269f71d421SKyle Evans		if file == "." or file == ".." then
227aedd6be5SKyle Evans			goto continue
228a108046fSConrad Meyer		end
229a108046fSConrad Meyer
2309f71d421SKyle Evans		if lfs.attributes(fname, "mode") ~= "directory" then
231aedd6be5SKyle Evans			goto continue
232a108046fSConrad Meyer		end
233a108046fSConrad Meyer
2349f71d421SKyle Evans		if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
235aedd6be5SKyle Evans			goto continue
236a108046fSConrad Meyer		end
237a108046fSConrad Meyer
2389f71d421SKyle Evans		if unique[file] == nil then
239aedd6be5SKyle Evans			i = i + 1
240aedd6be5SKyle Evans			kernels[i] = file
241aedd6be5SKyle Evans			unique[file] = true
242a108046fSConrad Meyer		end
243a108046fSConrad Meyer
244a108046fSConrad Meyer		::continue::
245a108046fSConrad Meyer	end
246aedd6be5SKyle Evans	return kernels
247088b4f5fSWarner Loshend
248088b4f5fSWarner Losh
2497efc058fSKyle Evansfunction core.bootenvDefault()
2507efc058fSKyle Evans	return loader.getenv("zfs_be_active")
2517efc058fSKyle Evansend
2527efc058fSKyle Evans
2537efc058fSKyle Evansfunction core.bootenvList()
2547efc058fSKyle Evans	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
2557efc058fSKyle Evans	local bootenvs = {}
2567efc058fSKyle Evans	local curenv
2577efc058fSKyle Evans	local curenv_idx = 0
2587efc058fSKyle Evans	local envcount = 0
2597efc058fSKyle Evans	local unique = {}
2607efc058fSKyle Evans
2617efc058fSKyle Evans	if bootenv_count == nil or bootenv_count <= 0 then
2627efc058fSKyle Evans		return bootenvs
2637efc058fSKyle Evans	end
2647efc058fSKyle Evans
2657efc058fSKyle Evans	-- Currently selected bootenv is always first/default
2667efc058fSKyle Evans	curenv = core.bootenvDefault()
2677efc058fSKyle Evans	if curenv ~= nil then
2687efc058fSKyle Evans		envcount = envcount + 1
2697efc058fSKyle Evans		bootenvs[envcount] = curenv
2707efc058fSKyle Evans		unique[curenv] = true
2717efc058fSKyle Evans	end
2727efc058fSKyle Evans
2737efc058fSKyle Evans	for curenv_idx = 0, bootenv_count - 1 do
2747efc058fSKyle Evans		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
2757efc058fSKyle Evans		if curenv ~= nil and unique[curenv] == nil then
2767efc058fSKyle Evans			envcount = envcount + 1
2777efc058fSKyle Evans			bootenvs[envcount] = curenv
2787efc058fSKyle Evans			unique[curenv] = true
2797efc058fSKyle Evans		end
2807efc058fSKyle Evans	end
2817efc058fSKyle Evans	return bootenvs
2827efc058fSKyle Evansend
2837efc058fSKyle Evans
284088b4f5fSWarner Loshfunction core.setDefaults()
285aedd6be5SKyle Evans	core.setACPI(core.getACPIPresent(true))
286aedd6be5SKyle Evans	core.setSafeMode(false)
287aedd6be5SKyle Evans	core.setSingleUser(false)
288aedd6be5SKyle Evans	core.setVerbose(false)
289088b4f5fSWarner Loshend
290088b4f5fSWarner Losh
2916d4ed94dSKyle Evansfunction core.autoboot(argstr)
292aedd6be5SKyle Evans	config.loadelf()
293aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("autoboot", argstr))
294088b4f5fSWarner Loshend
295088b4f5fSWarner Losh
2966d4ed94dSKyle Evansfunction core.boot(argstr)
297aedd6be5SKyle Evans	config.loadelf()
298aedd6be5SKyle Evans	loader.perform(compose_loader_cmd("boot", argstr))
299088b4f5fSWarner Loshend
300088b4f5fSWarner Losh
301e07fc39cSKyle Evansfunction core.isSingleUserBoot()
302aedd6be5SKyle Evans	local single_user = loader.getenv("boot_single")
303aedd6be5SKyle Evans	return single_user ~= nil and single_user:lower() == "yes"
304e07fc39cSKyle Evansend
305e07fc39cSKyle Evans
3067efc058fSKyle Evansfunction core.isZFSBoot()
3077efc058fSKyle Evans	local c = loader.getenv("currdev")
3087efc058fSKyle Evans
3097efc058fSKyle Evans	if c ~= nil then
3107efc058fSKyle Evans		return c:match("^zfs:") ~= nil
3117efc058fSKyle Evans	end
3127efc058fSKyle Evans	return false
3137efc058fSKyle Evansend
3147efc058fSKyle Evans
315b140d14bSKyle Evansfunction core.isSerialBoot()
316aedd6be5SKyle Evans	local c = loader.getenv("console")
317088b4f5fSWarner Losh
3189f71d421SKyle Evans	if c ~= nil then
3199f71d421SKyle Evans		if c:find("comconsole") ~= nil then
320aedd6be5SKyle Evans			return true
321088b4f5fSWarner Losh		end
322088b4f5fSWarner Losh	end
323088b4f5fSWarner Losh
324aedd6be5SKyle Evans	local s = loader.getenv("boot_serial")
3259f71d421SKyle Evans	if s ~= nil then
326aedd6be5SKyle Evans		return true
327088b4f5fSWarner Losh	end
328088b4f5fSWarner Losh
329aedd6be5SKyle Evans	local m = loader.getenv("boot_multicons")
3309f71d421SKyle Evans	if m ~= nil then
331aedd6be5SKyle Evans		return true
332088b4f5fSWarner Losh	end
333aedd6be5SKyle Evans	return false
334088b4f5fSWarner Loshend
335088b4f5fSWarner Losh
336c1ab36f5SKyle Evansfunction core.isSystem386()
3379f71d421SKyle Evans	return loader.machine_arch == "i386"
338c1ab36f5SKyle Evansend
339c1ab36f5SKyle Evans
3405c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module.
3415c1b5165SKyle Evansfunction core.shallowCopyTable(tbl)
342aedd6be5SKyle Evans	local new_tbl = {}
3435c1b5165SKyle Evans	for k, v in pairs(tbl) do
3449f71d421SKyle Evans		if type(v) == "table" then
345aedd6be5SKyle Evans			new_tbl[k] = core.shallowCopyTable(v)
3465c1b5165SKyle Evans		else
347aedd6be5SKyle Evans			new_tbl[k] = v
3485c1b5165SKyle Evans		end
3495c1b5165SKyle Evans	end
350aedd6be5SKyle Evans	return new_tbl
3515c1b5165SKyle Evansend
3525c1b5165SKyle Evans
3536d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing.
3546d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua
3556d4ed94dSKyle Evans-- for our uses
3566d4ed94dSKyle Evansfunction core.popFrontTable(tbl)
3576d4ed94dSKyle Evans	-- Shouldn't reasonably happen
3589f71d421SKyle Evans	if #tbl == 0 then
359aedd6be5SKyle Evans		return nil, nil
3609f71d421SKyle Evans	elseif #tbl == 1 then
361aedd6be5SKyle Evans		return tbl[1], {}
3626d4ed94dSKyle Evans	end
3636d4ed94dSKyle Evans
364aedd6be5SKyle Evans	local first_value = tbl[1]
365aedd6be5SKyle Evans	local new_tbl = {}
3666d4ed94dSKyle Evans	-- This is not a cheap operation
3676d4ed94dSKyle Evans	for k, v in ipairs(tbl) do
3689f71d421SKyle Evans		if k > 1 then
369aedd6be5SKyle Evans			new_tbl[k - 1] = v
3706d4ed94dSKyle Evans		end
3716d4ed94dSKyle Evans	end
3726d4ed94dSKyle Evans
373aedd6be5SKyle Evans	return first_value, new_tbl
3746d4ed94dSKyle Evansend
3756d4ed94dSKyle Evans
3766f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
3776f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or
3786f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
3796f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't.
3809f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then
381aedd6be5SKyle Evans	core.setACPI(true)
3826f412147SKyle Evansend
383aedd6be5SKyle Evansreturn core
384