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 141*35b0c718SKyle Evansfunction core.configReloaded() 142*35b0c718SKyle Evans -- Clear the kernel cache on config changes, autodetect might have 143*35b0c718SKyle Evans -- changed or if we've switched boot environments then we could have 144*35b0c718SKyle Evans -- a new kernel set. 145*35b0c718SKyle Evans core.cached_kernels = nil 146*35b0c718SKyle Evansend 147*35b0c718SKyle Evans 148088b4f5fSWarner Loshfunction core.kernelList() 149*35b0c718SKyle Evans if core.cached_kernels ~= nil then 150*35b0c718SKyle Evans return core.cached_kernels 151*35b0c718SKyle Evans end 152*35b0c718SKyle Evans 153aedd6be5SKyle Evans local k = loader.getenv("kernel") 1543f4eb56bSKyle Evans local v = loader.getenv("kernels") 1553889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 156088b4f5fSWarner Losh 157aedd6be5SKyle Evans local kernels = {} 158aedd6be5SKyle Evans local unique = {} 159aedd6be5SKyle Evans local i = 0 1609f71d421SKyle Evans if k ~= nil then 161aedd6be5SKyle Evans i = i + 1 162aedd6be5SKyle Evans kernels[i] = k 163aedd6be5SKyle Evans unique[k] = true 164088b4f5fSWarner Losh end 165088b4f5fSWarner Losh 1663f4eb56bSKyle Evans if v ~= nil then 167088b4f5fSWarner Losh for n in v:gmatch("([^; ]+)[; ]?") do 1689f71d421SKyle Evans if unique[n] == nil then 169aedd6be5SKyle Evans i = i + 1 170aedd6be5SKyle Evans kernels[i] = n 171aedd6be5SKyle Evans unique[n] = true 172088b4f5fSWarner Losh end 173088b4f5fSWarner Losh end 1743889e6cdSKyle Evans end 175a108046fSConrad Meyer 1763889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 1773889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 1783889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 1793889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 180*35b0c718SKyle Evans core.cached_kernels = kernels 181*35b0c718SKyle Evans return core.cached_kernels 1823f4eb56bSKyle Evans end 1833f4eb56bSKyle Evans 184a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 185a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 186a108046fSConrad Meyer -- named "kernel" is considered eligible. 187a108046fSConrad Meyer for file in lfs.dir("/boot") do 188aedd6be5SKyle Evans local fname = "/boot/" .. file 189a108046fSConrad Meyer 1909f71d421SKyle Evans if file == "." or file == ".." then 191aedd6be5SKyle Evans goto continue 192a108046fSConrad Meyer end 193a108046fSConrad Meyer 1949f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 195aedd6be5SKyle Evans goto continue 196a108046fSConrad Meyer end 197a108046fSConrad Meyer 1989f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 199aedd6be5SKyle Evans goto continue 200a108046fSConrad Meyer end 201a108046fSConrad Meyer 2029f71d421SKyle Evans if unique[file] == nil then 203aedd6be5SKyle Evans i = i + 1 204aedd6be5SKyle Evans kernels[i] = file 205aedd6be5SKyle Evans unique[file] = true 206a108046fSConrad Meyer end 207a108046fSConrad Meyer 208a108046fSConrad Meyer ::continue:: 209a108046fSConrad Meyer end 210*35b0c718SKyle Evans core.cached_kernels = kernels 211*35b0c718SKyle Evans return core.cached_kernels 212088b4f5fSWarner Loshend 213088b4f5fSWarner Losh 2147efc058fSKyle Evansfunction core.bootenvDefault() 2157efc058fSKyle Evans return loader.getenv("zfs_be_active") 2167efc058fSKyle Evansend 2177efc058fSKyle Evans 2187efc058fSKyle Evansfunction core.bootenvList() 2197efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2207efc058fSKyle Evans local bootenvs = {} 2217efc058fSKyle Evans local curenv 2227efc058fSKyle Evans local envcount = 0 2237efc058fSKyle Evans local unique = {} 2247efc058fSKyle Evans 2257efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2267efc058fSKyle Evans return bootenvs 2277efc058fSKyle Evans end 2287efc058fSKyle Evans 2297efc058fSKyle Evans -- Currently selected bootenv is always first/default 2307efc058fSKyle Evans curenv = core.bootenvDefault() 2317efc058fSKyle Evans if curenv ~= nil then 2327efc058fSKyle Evans envcount = envcount + 1 2337efc058fSKyle Evans bootenvs[envcount] = curenv 2347efc058fSKyle Evans unique[curenv] = true 2357efc058fSKyle Evans end 2367efc058fSKyle Evans 2377efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2387efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2397efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2407efc058fSKyle Evans envcount = envcount + 1 2417efc058fSKyle Evans bootenvs[envcount] = curenv 2427efc058fSKyle Evans unique[curenv] = true 2437efc058fSKyle Evans end 2447efc058fSKyle Evans end 2457efc058fSKyle Evans return bootenvs 2467efc058fSKyle Evansend 2477efc058fSKyle Evans 248088b4f5fSWarner Loshfunction core.setDefaults() 249aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 250aedd6be5SKyle Evans core.setSafeMode(false) 251aedd6be5SKyle Evans core.setSingleUser(false) 252aedd6be5SKyle Evans core.setVerbose(false) 253088b4f5fSWarner Loshend 254088b4f5fSWarner Losh 2556d4ed94dSKyle Evansfunction core.autoboot(argstr) 256a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 257a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 258aedd6be5SKyle Evans config.loadelf() 259a2a7830eSKyle Evans end 260322a2dddSKyle Evans loader.perform(composeLoaderCmd("autoboot", argstr)) 261088b4f5fSWarner Loshend 262088b4f5fSWarner Losh 2636d4ed94dSKyle Evansfunction core.boot(argstr) 264a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 265a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 266aedd6be5SKyle Evans config.loadelf() 267a2a7830eSKyle Evans end 268322a2dddSKyle Evans loader.perform(composeLoaderCmd("boot", argstr)) 269088b4f5fSWarner Loshend 270088b4f5fSWarner Losh 271e07fc39cSKyle Evansfunction core.isSingleUserBoot() 272aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 273aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 274e07fc39cSKyle Evansend 275e07fc39cSKyle Evans 2767efc058fSKyle Evansfunction core.isZFSBoot() 2777efc058fSKyle Evans local c = loader.getenv("currdev") 2787efc058fSKyle Evans 2797efc058fSKyle Evans if c ~= nil then 2807efc058fSKyle Evans return c:match("^zfs:") ~= nil 2817efc058fSKyle Evans end 2827efc058fSKyle Evans return false 2837efc058fSKyle Evansend 2847efc058fSKyle Evans 285b140d14bSKyle Evansfunction core.isSerialBoot() 286aedd6be5SKyle Evans local c = loader.getenv("console") 287088b4f5fSWarner Losh 2889f71d421SKyle Evans if c ~= nil then 2899f71d421SKyle Evans if c:find("comconsole") ~= nil then 290aedd6be5SKyle Evans return true 291088b4f5fSWarner Losh end 292088b4f5fSWarner Losh end 293088b4f5fSWarner Losh 294aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 2959f71d421SKyle Evans if s ~= nil then 296aedd6be5SKyle Evans return true 297088b4f5fSWarner Losh end 298088b4f5fSWarner Losh 299aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 3009f71d421SKyle Evans if m ~= nil then 301aedd6be5SKyle Evans return true 302088b4f5fSWarner Losh end 303aedd6be5SKyle Evans return false 304088b4f5fSWarner Loshend 305088b4f5fSWarner Losh 306c1ab36f5SKyle Evansfunction core.isSystem386() 3079f71d421SKyle Evans return loader.machine_arch == "i386" 308c1ab36f5SKyle Evansend 309c1ab36f5SKyle Evans 3109937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted? 3119937e979SKyle Evansfunction core.isMenuSkipped() 3129937e979SKyle Evans if core.isSerialBoot() then 3139937e979SKyle Evans return true 3149937e979SKyle Evans end 3159937e979SKyle Evans local c = string.lower(loader.getenv("console") or "") 3169937e979SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 3179937e979SKyle Evans return true 3189937e979SKyle Evans end 3199937e979SKyle Evans 3209937e979SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 3219937e979SKyle Evans return c == "yes" 3229937e979SKyle Evansend 3239937e979SKyle Evans 3245c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 325ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl) 326aedd6be5SKyle Evans local new_tbl = {} 3275c1b5165SKyle Evans for k, v in pairs(tbl) do 3289f71d421SKyle Evans if type(v) == "table" then 329ee4e69f1SKyle Evans new_tbl[k] = core.deepCopyTable(v) 3305c1b5165SKyle Evans else 331aedd6be5SKyle Evans new_tbl[k] = v 3325c1b5165SKyle Evans end 3335c1b5165SKyle Evans end 334aedd6be5SKyle Evans return new_tbl 3355c1b5165SKyle Evansend 3365c1b5165SKyle Evans 3376d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3386d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3396d4ed94dSKyle Evans-- for our uses 3406d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3416d4ed94dSKyle Evans -- Shouldn't reasonably happen 3429f71d421SKyle Evans if #tbl == 0 then 343aedd6be5SKyle Evans return nil, nil 3449f71d421SKyle Evans elseif #tbl == 1 then 345aedd6be5SKyle Evans return tbl[1], {} 3466d4ed94dSKyle Evans end 3476d4ed94dSKyle Evans 348aedd6be5SKyle Evans local first_value = tbl[1] 349aedd6be5SKyle Evans local new_tbl = {} 3506d4ed94dSKyle Evans -- This is not a cheap operation 3516d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3529f71d421SKyle Evans if k > 1 then 353aedd6be5SKyle Evans new_tbl[k - 1] = v 3546d4ed94dSKyle Evans end 3556d4ed94dSKyle Evans end 3566d4ed94dSKyle Evans 357aedd6be5SKyle Evans return first_value, new_tbl 3586d4ed94dSKyle Evansend 3596d4ed94dSKyle Evans 3606f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3616f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3626f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3636f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3649f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 365aedd6be5SKyle Evans core.setACPI(true) 3666f412147SKyle Evansend 367aedd6be5SKyle Evansreturn core 368