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 36322a2dddSKyle Evanslocal function composeLoaderCmd(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 49230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in 50230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal' 51aedd6be5SKyle Evanscore.KEYSTR_ESCAPE = "\027" 522bb86aefSKyle Evanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 5339006570SKyle Evans 54aedd6be5SKyle Evanscore.MENU_RETURN = "return" 55aedd6be5SKyle Evanscore.MENU_ENTRY = "entry" 56aedd6be5SKyle Evanscore.MENU_SEPARATOR = "separator" 57aedd6be5SKyle Evanscore.MENU_SUBMENU = "submenu" 58aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 59a7cf0562SKyle Evans 6004af4229SKyle Evansfunction core.setVerbose(verbose) 6104af4229SKyle Evans if verbose == nil then 6204af4229SKyle Evans verbose = not core.verbose 63088b4f5fSWarner Losh end 64088b4f5fSWarner Losh 6504af4229SKyle Evans if verbose then 66aedd6be5SKyle Evans loader.setenv("boot_verbose", "YES") 67088b4f5fSWarner Losh else 68aedd6be5SKyle Evans loader.unsetenv("boot_verbose") 69088b4f5fSWarner Losh end 7004af4229SKyle Evans core.verbose = verbose 71088b4f5fSWarner Loshend 72088b4f5fSWarner Losh 7304af4229SKyle Evansfunction core.setSingleUser(single_user) 7404af4229SKyle Evans if single_user == nil then 7504af4229SKyle Evans single_user = not core.su 76088b4f5fSWarner Losh end 77088b4f5fSWarner Losh 7804af4229SKyle Evans if single_user then 79aedd6be5SKyle Evans loader.setenv("boot_single", "YES") 80088b4f5fSWarner Losh else 81aedd6be5SKyle Evans loader.unsetenv("boot_single") 82088b4f5fSWarner Losh end 8304af4229SKyle Evans core.su = single_user 84088b4f5fSWarner Loshend 85088b4f5fSWarner Losh 8604af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults) 87aedd6be5SKyle Evans local c = loader.getenv("hint.acpi.0.rsdp") 886401094fSKyle Evans 899f71d421SKyle Evans if c ~= nil then 9004af4229SKyle Evans if checking_system_defaults then 91aedd6be5SKyle Evans return true 926401094fSKyle Evans end 936401094fSKyle Evans -- Otherwise, respect disabled if it's set 94aedd6be5SKyle Evans c = loader.getenv("hint.acpi.0.disabled") 959f71d421SKyle Evans return c == nil or tonumber(c) ~= 1 966401094fSKyle Evans end 97aedd6be5SKyle Evans return false 986401094fSKyle Evansend 996401094fSKyle Evans 10004af4229SKyle Evansfunction core.setACPI(acpi) 10104af4229SKyle Evans if acpi == nil then 10204af4229SKyle Evans acpi = not core.acpi 103088b4f5fSWarner Losh end 104088b4f5fSWarner Losh 10504af4229SKyle Evans if acpi then 106aedd6be5SKyle Evans loader.setenv("acpi_load", "YES") 107aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "0") 108aedd6be5SKyle Evans loader.unsetenv("loader.acpi_disabled_by_user") 109088b4f5fSWarner Losh else 110aedd6be5SKyle Evans loader.unsetenv("acpi_load") 111aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "1") 112aedd6be5SKyle Evans loader.setenv("loader.acpi_disabled_by_user", "1") 113088b4f5fSWarner Losh end 11404af4229SKyle Evans core.acpi = acpi 115088b4f5fSWarner Loshend 116088b4f5fSWarner Losh 11704af4229SKyle Evansfunction core.setSafeMode(safe_mode) 11804af4229SKyle Evans if safe_mode == nil then 11904af4229SKyle Evans safe_mode = not core.sm 120088b4f5fSWarner Losh end 12104af4229SKyle Evans if safe_mode then 122aedd6be5SKyle Evans loader.setenv("kern.smp.disabled", "1") 123aedd6be5SKyle Evans loader.setenv("hw.ata.ata_dma", "0") 124aedd6be5SKyle Evans loader.setenv("hw.ata.atapi_dma", "0") 125aedd6be5SKyle Evans loader.setenv("hw.ata.wc", "0") 126aedd6be5SKyle Evans loader.setenv("hw.eisa_slots", "0") 127aedd6be5SKyle Evans loader.setenv("kern.eventtimer.periodic", "1") 128aedd6be5SKyle Evans loader.setenv("kern.geom.part.check_integrity", "0") 129088b4f5fSWarner Losh else 130aedd6be5SKyle Evans loader.unsetenv("kern.smp.disabled") 131aedd6be5SKyle Evans loader.unsetenv("hw.ata.ata_dma") 132aedd6be5SKyle Evans loader.unsetenv("hw.ata.atapi_dma") 133aedd6be5SKyle Evans loader.unsetenv("hw.ata.wc") 134aedd6be5SKyle Evans loader.unsetenv("hw.eisa_slots") 135aedd6be5SKyle Evans loader.unsetenv("kern.eventtimer.periodic") 136aedd6be5SKyle Evans loader.unsetenv("kern.geom.part.check_integrity") 137088b4f5fSWarner Losh end 13804af4229SKyle Evans core.sm = safe_mode 139088b4f5fSWarner Loshend 140088b4f5fSWarner Losh 141088b4f5fSWarner Loshfunction core.kernelList() 142aedd6be5SKyle Evans local k = loader.getenv("kernel") 1433f4eb56bSKyle Evans local v = loader.getenv("kernels") 1443889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 145088b4f5fSWarner Losh 146aedd6be5SKyle Evans local kernels = {} 147aedd6be5SKyle Evans local unique = {} 148aedd6be5SKyle Evans local i = 0 1499f71d421SKyle Evans if k ~= nil then 150aedd6be5SKyle Evans i = i + 1 151aedd6be5SKyle Evans kernels[i] = k 152aedd6be5SKyle Evans unique[k] = true 153088b4f5fSWarner Losh end 154088b4f5fSWarner Losh 1553f4eb56bSKyle Evans if v ~= nil then 156088b4f5fSWarner Losh for n in v:gmatch("([^; ]+)[; ]?") do 1579f71d421SKyle Evans if unique[n] == nil then 158aedd6be5SKyle Evans i = i + 1 159aedd6be5SKyle Evans kernels[i] = n 160aedd6be5SKyle Evans unique[n] = true 161088b4f5fSWarner Losh end 162088b4f5fSWarner Losh end 1633889e6cdSKyle Evans end 164a108046fSConrad Meyer 1653889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 1663889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 1673889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 1683889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 1693f4eb56bSKyle Evans return kernels 1703f4eb56bSKyle Evans end 1713f4eb56bSKyle Evans 172a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 173a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 174a108046fSConrad Meyer -- named "kernel" is considered eligible. 175a108046fSConrad Meyer for file in lfs.dir("/boot") do 176aedd6be5SKyle Evans local fname = "/boot/" .. file 177a108046fSConrad Meyer 1789f71d421SKyle Evans if file == "." or file == ".." then 179aedd6be5SKyle Evans goto continue 180a108046fSConrad Meyer end 181a108046fSConrad Meyer 1829f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 183aedd6be5SKyle Evans goto continue 184a108046fSConrad Meyer end 185a108046fSConrad Meyer 1869f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 187aedd6be5SKyle Evans goto continue 188a108046fSConrad Meyer end 189a108046fSConrad Meyer 1909f71d421SKyle Evans if unique[file] == nil then 191aedd6be5SKyle Evans i = i + 1 192aedd6be5SKyle Evans kernels[i] = file 193aedd6be5SKyle Evans unique[file] = true 194a108046fSConrad Meyer end 195a108046fSConrad Meyer 196a108046fSConrad Meyer ::continue:: 197a108046fSConrad Meyer end 198aedd6be5SKyle Evans return kernels 199088b4f5fSWarner Loshend 200088b4f5fSWarner Losh 2017efc058fSKyle Evansfunction core.bootenvDefault() 2027efc058fSKyle Evans return loader.getenv("zfs_be_active") 2037efc058fSKyle Evansend 2047efc058fSKyle Evans 2057efc058fSKyle Evansfunction core.bootenvList() 2067efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2077efc058fSKyle Evans local bootenvs = {} 2087efc058fSKyle Evans local curenv 2097efc058fSKyle Evans local envcount = 0 2107efc058fSKyle Evans local unique = {} 2117efc058fSKyle Evans 2127efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2137efc058fSKyle Evans return bootenvs 2147efc058fSKyle Evans end 2157efc058fSKyle Evans 2167efc058fSKyle Evans -- Currently selected bootenv is always first/default 2177efc058fSKyle Evans curenv = core.bootenvDefault() 2187efc058fSKyle Evans if curenv ~= nil then 2197efc058fSKyle Evans envcount = envcount + 1 2207efc058fSKyle Evans bootenvs[envcount] = curenv 2217efc058fSKyle Evans unique[curenv] = true 2227efc058fSKyle Evans end 2237efc058fSKyle Evans 2247efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2257efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2267efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2277efc058fSKyle Evans envcount = envcount + 1 2287efc058fSKyle Evans bootenvs[envcount] = curenv 2297efc058fSKyle Evans unique[curenv] = true 2307efc058fSKyle Evans end 2317efc058fSKyle Evans end 2327efc058fSKyle Evans return bootenvs 2337efc058fSKyle Evansend 2347efc058fSKyle Evans 235088b4f5fSWarner Loshfunction core.setDefaults() 236aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 237aedd6be5SKyle Evans core.setSafeMode(false) 238aedd6be5SKyle Evans core.setSingleUser(false) 239aedd6be5SKyle Evans core.setVerbose(false) 240088b4f5fSWarner Loshend 241088b4f5fSWarner Losh 2426d4ed94dSKyle Evansfunction core.autoboot(argstr) 243*a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 244*a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 245aedd6be5SKyle Evans config.loadelf() 246*a2a7830eSKyle Evans end 247322a2dddSKyle Evans loader.perform(composeLoaderCmd("autoboot", argstr)) 248088b4f5fSWarner Loshend 249088b4f5fSWarner Losh 2506d4ed94dSKyle Evansfunction core.boot(argstr) 251*a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 252*a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 253aedd6be5SKyle Evans config.loadelf() 254*a2a7830eSKyle Evans end 255322a2dddSKyle Evans loader.perform(composeLoaderCmd("boot", argstr)) 256088b4f5fSWarner Loshend 257088b4f5fSWarner Losh 258e07fc39cSKyle Evansfunction core.isSingleUserBoot() 259aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 260aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 261e07fc39cSKyle Evansend 262e07fc39cSKyle Evans 2637efc058fSKyle Evansfunction core.isZFSBoot() 2647efc058fSKyle Evans local c = loader.getenv("currdev") 2657efc058fSKyle Evans 2667efc058fSKyle Evans if c ~= nil then 2677efc058fSKyle Evans return c:match("^zfs:") ~= nil 2687efc058fSKyle Evans end 2697efc058fSKyle Evans return false 2707efc058fSKyle Evansend 2717efc058fSKyle Evans 272b140d14bSKyle Evansfunction core.isSerialBoot() 273aedd6be5SKyle Evans local c = loader.getenv("console") 274088b4f5fSWarner Losh 2759f71d421SKyle Evans if c ~= nil then 2769f71d421SKyle Evans if c:find("comconsole") ~= nil then 277aedd6be5SKyle Evans return true 278088b4f5fSWarner Losh end 279088b4f5fSWarner Losh end 280088b4f5fSWarner Losh 281aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 2829f71d421SKyle Evans if s ~= nil then 283aedd6be5SKyle Evans return true 284088b4f5fSWarner Losh end 285088b4f5fSWarner Losh 286aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 2879f71d421SKyle Evans if m ~= nil then 288aedd6be5SKyle Evans return true 289088b4f5fSWarner Losh end 290aedd6be5SKyle Evans return false 291088b4f5fSWarner Loshend 292088b4f5fSWarner Losh 293c1ab36f5SKyle Evansfunction core.isSystem386() 2949f71d421SKyle Evans return loader.machine_arch == "i386" 295c1ab36f5SKyle Evansend 296c1ab36f5SKyle Evans 2979937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted? 2989937e979SKyle Evansfunction core.isMenuSkipped() 2999937e979SKyle Evans if core.isSerialBoot() then 3009937e979SKyle Evans return true 3019937e979SKyle Evans end 3029937e979SKyle Evans local c = string.lower(loader.getenv("console") or "") 3039937e979SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 3049937e979SKyle Evans return true 3059937e979SKyle Evans end 3069937e979SKyle Evans 3079937e979SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 3089937e979SKyle Evans return c == "yes" 3099937e979SKyle Evansend 3109937e979SKyle Evans 3115c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 312ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl) 313aedd6be5SKyle Evans local new_tbl = {} 3145c1b5165SKyle Evans for k, v in pairs(tbl) do 3159f71d421SKyle Evans if type(v) == "table" then 316ee4e69f1SKyle Evans new_tbl[k] = core.deepCopyTable(v) 3175c1b5165SKyle Evans else 318aedd6be5SKyle Evans new_tbl[k] = v 3195c1b5165SKyle Evans end 3205c1b5165SKyle Evans end 321aedd6be5SKyle Evans return new_tbl 3225c1b5165SKyle Evansend 3235c1b5165SKyle Evans 3246d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3256d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3266d4ed94dSKyle Evans-- for our uses 3276d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3286d4ed94dSKyle Evans -- Shouldn't reasonably happen 3299f71d421SKyle Evans if #tbl == 0 then 330aedd6be5SKyle Evans return nil, nil 3319f71d421SKyle Evans elseif #tbl == 1 then 332aedd6be5SKyle Evans return tbl[1], {} 3336d4ed94dSKyle Evans end 3346d4ed94dSKyle Evans 335aedd6be5SKyle Evans local first_value = tbl[1] 336aedd6be5SKyle Evans local new_tbl = {} 3376d4ed94dSKyle Evans -- This is not a cheap operation 3386d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3399f71d421SKyle Evans if k > 1 then 340aedd6be5SKyle Evans new_tbl[k - 1] = v 3416d4ed94dSKyle Evans end 3426d4ed94dSKyle Evans end 3436d4ed94dSKyle Evans 344aedd6be5SKyle Evans return first_value, new_tbl 3456d4ed94dSKyle Evansend 3466d4ed94dSKyle Evans 3476f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3486f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3496f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3506f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3519f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 352aedd6be5SKyle Evans core.setACPI(true) 3536f412147SKyle Evansend 354aedd6be5SKyle Evansreturn core 355