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") 33aea262bfSKyle Evanslocal hook = require("hook") 34fa4a2394SKyle Evans 35aedd6be5SKyle Evanslocal core = {} 36088b4f5fSWarner Losh 37322a2dddSKyle Evanslocal function composeLoaderCmd(cmd_name, argstr) 389f71d421SKyle Evans if argstr ~= nil then 39aedd6be5SKyle Evans cmd_name = cmd_name .. " " .. argstr 406d4ed94dSKyle Evans end 41aedd6be5SKyle Evans return cmd_name 426d4ed94dSKyle Evansend 436d4ed94dSKyle Evans 4407faaf78SKyle Evans-- Globals 4507faaf78SKyle Evans-- try_include will return the loaded module on success, or nil on failure. 4607faaf78SKyle Evans-- A message will also be printed on failure, with one exception: non-verbose 4707faaf78SKyle Evans-- loading will suppress 'module not found' errors. 4807faaf78SKyle Evansfunction try_include(module) 4907faaf78SKyle Evans local status, ret = pcall(require, module) 5007faaf78SKyle Evans -- ret is the module if we succeeded. 5107faaf78SKyle Evans if status then 5207faaf78SKyle Evans return ret 5307faaf78SKyle Evans end 5407faaf78SKyle Evans -- Otherwise, ret is just a message; filter out ENOENT unless we're 5507faaf78SKyle Evans -- doing a verbose load. As a consequence, try_include prior to loading 5607faaf78SKyle Evans -- configuration will not display 'module not found'. All other errors 5707faaf78SKyle Evans -- in loading will be printed. 5807faaf78SKyle Evans if config.verbose or ret:match("^module .+ not found") == nil then 59892b3a52SKyle Evans error(ret, 2) 6007faaf78SKyle Evans end 6107faaf78SKyle Evans return nil 6207faaf78SKyle Evansend 6307faaf78SKyle Evans 64b5746545SKyle Evans-- Module exports 65fe672a15SKyle Evans-- Commonly appearing constants 66aedd6be5SKyle Evanscore.KEY_BACKSPACE = 8 67aedd6be5SKyle Evanscore.KEY_ENTER = 13 68aedd6be5SKyle Evanscore.KEY_DELETE = 127 69fe672a15SKyle Evans 70230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in 71230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal' 72aedd6be5SKyle Evanscore.KEYSTR_ESCAPE = "\027" 732bb86aefSKyle Evanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 7439006570SKyle Evans 75aedd6be5SKyle Evanscore.MENU_RETURN = "return" 76aedd6be5SKyle Evanscore.MENU_ENTRY = "entry" 77aedd6be5SKyle Evanscore.MENU_SEPARATOR = "separator" 78aedd6be5SKyle Evanscore.MENU_SUBMENU = "submenu" 79aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 80a7cf0562SKyle Evans 8104af4229SKyle Evansfunction core.setVerbose(verbose) 8204af4229SKyle Evans if verbose == nil then 8304af4229SKyle Evans verbose = not core.verbose 84088b4f5fSWarner Losh end 85088b4f5fSWarner Losh 8604af4229SKyle Evans if verbose then 87aedd6be5SKyle Evans loader.setenv("boot_verbose", "YES") 88088b4f5fSWarner Losh else 89aedd6be5SKyle Evans loader.unsetenv("boot_verbose") 90088b4f5fSWarner Losh end 9104af4229SKyle Evans core.verbose = verbose 92088b4f5fSWarner Loshend 93088b4f5fSWarner Losh 9404af4229SKyle Evansfunction core.setSingleUser(single_user) 9504af4229SKyle Evans if single_user == nil then 9604af4229SKyle Evans single_user = not core.su 97088b4f5fSWarner Losh end 98088b4f5fSWarner Losh 9904af4229SKyle Evans if single_user then 100aedd6be5SKyle Evans loader.setenv("boot_single", "YES") 101088b4f5fSWarner Losh else 102aedd6be5SKyle Evans loader.unsetenv("boot_single") 103088b4f5fSWarner Losh end 10404af4229SKyle Evans core.su = single_user 105088b4f5fSWarner Loshend 106088b4f5fSWarner Losh 10704af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults) 108aedd6be5SKyle Evans local c = loader.getenv("hint.acpi.0.rsdp") 1096401094fSKyle Evans 1109f71d421SKyle Evans if c ~= nil then 11104af4229SKyle Evans if checking_system_defaults then 112aedd6be5SKyle Evans return true 1136401094fSKyle Evans end 1146401094fSKyle Evans -- Otherwise, respect disabled if it's set 115aedd6be5SKyle Evans c = loader.getenv("hint.acpi.0.disabled") 1169f71d421SKyle Evans return c == nil or tonumber(c) ~= 1 1176401094fSKyle Evans end 118aedd6be5SKyle Evans return false 1196401094fSKyle Evansend 1206401094fSKyle Evans 12104af4229SKyle Evansfunction core.setACPI(acpi) 12204af4229SKyle Evans if acpi == nil then 12304af4229SKyle Evans acpi = not core.acpi 124088b4f5fSWarner Losh end 125088b4f5fSWarner Losh 12604af4229SKyle Evans if acpi then 127aedd6be5SKyle Evans loader.setenv("acpi_load", "YES") 128aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "0") 129aedd6be5SKyle Evans loader.unsetenv("loader.acpi_disabled_by_user") 130088b4f5fSWarner Losh else 131aedd6be5SKyle Evans loader.unsetenv("acpi_load") 132aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "1") 133aedd6be5SKyle Evans loader.setenv("loader.acpi_disabled_by_user", "1") 134088b4f5fSWarner Losh end 13504af4229SKyle Evans core.acpi = acpi 136088b4f5fSWarner Loshend 137088b4f5fSWarner Losh 13804af4229SKyle Evansfunction core.setSafeMode(safe_mode) 13904af4229SKyle Evans if safe_mode == nil then 14004af4229SKyle Evans safe_mode = not core.sm 141088b4f5fSWarner Losh end 14204af4229SKyle Evans if safe_mode then 143aedd6be5SKyle Evans loader.setenv("kern.smp.disabled", "1") 144aedd6be5SKyle Evans loader.setenv("hw.ata.ata_dma", "0") 145aedd6be5SKyle Evans loader.setenv("hw.ata.atapi_dma", "0") 146aedd6be5SKyle Evans loader.setenv("hw.ata.wc", "0") 147aedd6be5SKyle Evans loader.setenv("hw.eisa_slots", "0") 148aedd6be5SKyle Evans loader.setenv("kern.eventtimer.periodic", "1") 149aedd6be5SKyle Evans loader.setenv("kern.geom.part.check_integrity", "0") 150088b4f5fSWarner Losh else 151aedd6be5SKyle Evans loader.unsetenv("kern.smp.disabled") 152aedd6be5SKyle Evans loader.unsetenv("hw.ata.ata_dma") 153aedd6be5SKyle Evans loader.unsetenv("hw.ata.atapi_dma") 154aedd6be5SKyle Evans loader.unsetenv("hw.ata.wc") 155aedd6be5SKyle Evans loader.unsetenv("hw.eisa_slots") 156aedd6be5SKyle Evans loader.unsetenv("kern.eventtimer.periodic") 157aedd6be5SKyle Evans loader.unsetenv("kern.geom.part.check_integrity") 158088b4f5fSWarner Losh end 15904af4229SKyle Evans core.sm = safe_mode 160088b4f5fSWarner Loshend 161088b4f5fSWarner Losh 162aea262bfSKyle Evansfunction core.clearCachedKernels() 16335b0c718SKyle Evans -- Clear the kernel cache on config changes, autodetect might have 16435b0c718SKyle Evans -- changed or if we've switched boot environments then we could have 16535b0c718SKyle Evans -- a new kernel set. 16635b0c718SKyle Evans core.cached_kernels = nil 16735b0c718SKyle Evansend 16835b0c718SKyle Evans 169088b4f5fSWarner Loshfunction core.kernelList() 17035b0c718SKyle Evans if core.cached_kernels ~= nil then 17135b0c718SKyle Evans return core.cached_kernels 17235b0c718SKyle Evans end 17335b0c718SKyle Evans 174aedd6be5SKyle Evans local k = loader.getenv("kernel") 1753f4eb56bSKyle Evans local v = loader.getenv("kernels") 1763889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 177088b4f5fSWarner Losh 178aedd6be5SKyle Evans local kernels = {} 179aedd6be5SKyle Evans local unique = {} 180aedd6be5SKyle Evans local i = 0 1819f71d421SKyle Evans if k ~= nil then 182aedd6be5SKyle Evans i = i + 1 183aedd6be5SKyle Evans kernels[i] = k 184aedd6be5SKyle Evans unique[k] = true 185088b4f5fSWarner Losh end 186088b4f5fSWarner Losh 1873f4eb56bSKyle Evans if v ~= nil then 188*a5003419SKyle Evans for n in v:gmatch("([^;, ]+)[;, ]?") do 1899f71d421SKyle Evans if unique[n] == nil then 190aedd6be5SKyle Evans i = i + 1 191aedd6be5SKyle Evans kernels[i] = n 192aedd6be5SKyle Evans unique[n] = true 193088b4f5fSWarner Losh end 194088b4f5fSWarner Losh end 1953889e6cdSKyle Evans end 196a108046fSConrad Meyer 1973889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 1983889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 1993889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 2003889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 20135b0c718SKyle Evans core.cached_kernels = kernels 20235b0c718SKyle Evans return core.cached_kernels 2033f4eb56bSKyle Evans end 2043f4eb56bSKyle Evans 205a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 206a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 207a108046fSConrad Meyer -- named "kernel" is considered eligible. 208a108046fSConrad Meyer for file in lfs.dir("/boot") do 209aedd6be5SKyle Evans local fname = "/boot/" .. file 210a108046fSConrad Meyer 2119f71d421SKyle Evans if file == "." or file == ".." then 212aedd6be5SKyle Evans goto continue 213a108046fSConrad Meyer end 214a108046fSConrad Meyer 2159f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 216aedd6be5SKyle Evans goto continue 217a108046fSConrad Meyer end 218a108046fSConrad Meyer 2199f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 220aedd6be5SKyle Evans goto continue 221a108046fSConrad Meyer end 222a108046fSConrad Meyer 2239f71d421SKyle Evans if unique[file] == nil then 224aedd6be5SKyle Evans i = i + 1 225aedd6be5SKyle Evans kernels[i] = file 226aedd6be5SKyle Evans unique[file] = true 227a108046fSConrad Meyer end 228a108046fSConrad Meyer 229a108046fSConrad Meyer ::continue:: 230a108046fSConrad Meyer end 23135b0c718SKyle Evans core.cached_kernels = kernels 23235b0c718SKyle Evans return core.cached_kernels 233088b4f5fSWarner Loshend 234088b4f5fSWarner Losh 2357efc058fSKyle Evansfunction core.bootenvDefault() 2367efc058fSKyle Evans return loader.getenv("zfs_be_active") 2377efc058fSKyle Evansend 2387efc058fSKyle Evans 2397efc058fSKyle Evansfunction core.bootenvList() 2407efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2417efc058fSKyle Evans local bootenvs = {} 2427efc058fSKyle Evans local curenv 2437efc058fSKyle Evans local envcount = 0 2447efc058fSKyle Evans local unique = {} 2457efc058fSKyle Evans 2467efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2477efc058fSKyle Evans return bootenvs 2487efc058fSKyle Evans end 2497efc058fSKyle Evans 2507efc058fSKyle Evans -- Currently selected bootenv is always first/default 2517efc058fSKyle Evans curenv = core.bootenvDefault() 2527efc058fSKyle Evans if curenv ~= nil then 2537efc058fSKyle Evans envcount = envcount + 1 2547efc058fSKyle Evans bootenvs[envcount] = curenv 2557efc058fSKyle Evans unique[curenv] = true 2567efc058fSKyle Evans end 2577efc058fSKyle Evans 2587efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2597efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2607efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2617efc058fSKyle Evans envcount = envcount + 1 2627efc058fSKyle Evans bootenvs[envcount] = curenv 2637efc058fSKyle Evans unique[curenv] = true 2647efc058fSKyle Evans end 2657efc058fSKyle Evans end 2667efc058fSKyle Evans return bootenvs 2677efc058fSKyle Evansend 2687efc058fSKyle Evans 269088b4f5fSWarner Loshfunction core.setDefaults() 270aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 271aedd6be5SKyle Evans core.setSafeMode(false) 272aedd6be5SKyle Evans core.setSingleUser(false) 273aedd6be5SKyle Evans core.setVerbose(false) 274088b4f5fSWarner Loshend 275088b4f5fSWarner Losh 2766d4ed94dSKyle Evansfunction core.autoboot(argstr) 277a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 278a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 279aedd6be5SKyle Evans config.loadelf() 280a2a7830eSKyle Evans end 281322a2dddSKyle Evans loader.perform(composeLoaderCmd("autoboot", argstr)) 282088b4f5fSWarner Loshend 283088b4f5fSWarner Losh 2846d4ed94dSKyle Evansfunction core.boot(argstr) 285a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 286a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 287aedd6be5SKyle Evans config.loadelf() 288a2a7830eSKyle Evans end 289322a2dddSKyle Evans loader.perform(composeLoaderCmd("boot", argstr)) 290088b4f5fSWarner Loshend 291088b4f5fSWarner Losh 292e07fc39cSKyle Evansfunction core.isSingleUserBoot() 293aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 294aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 295e07fc39cSKyle Evansend 296e07fc39cSKyle Evans 2975f8cfbe1SKyle Evansfunction core.isUEFIBoot() 2985f8cfbe1SKyle Evans local efiver = loader.getenv("efi-version") 2995f8cfbe1SKyle Evans 3005f8cfbe1SKyle Evans return efiver ~= nil 3015f8cfbe1SKyle Evansend 3025f8cfbe1SKyle Evans 3037efc058fSKyle Evansfunction core.isZFSBoot() 3047efc058fSKyle Evans local c = loader.getenv("currdev") 3057efc058fSKyle Evans 3067efc058fSKyle Evans if c ~= nil then 3077efc058fSKyle Evans return c:match("^zfs:") ~= nil 3087efc058fSKyle Evans end 3097efc058fSKyle Evans return false 3107efc058fSKyle Evansend 3117efc058fSKyle Evans 312b140d14bSKyle Evansfunction core.isSerialBoot() 313aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 3149f71d421SKyle Evans if s ~= nil then 315aedd6be5SKyle Evans return true 316088b4f5fSWarner Losh end 317088b4f5fSWarner Losh 318aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 3199f71d421SKyle Evans if m ~= nil then 320aedd6be5SKyle Evans return true 321088b4f5fSWarner Losh end 322aedd6be5SKyle Evans return false 323088b4f5fSWarner Loshend 324088b4f5fSWarner Losh 325c1ab36f5SKyle Evansfunction core.isSystem386() 3269f71d421SKyle Evans return loader.machine_arch == "i386" 327c1ab36f5SKyle Evansend 328c1ab36f5SKyle Evans 3299937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted? 3309937e979SKyle Evansfunction core.isMenuSkipped() 331b83a355dSKyle Evans return string.lower(loader.getenv("beastie_disable") or "") == "yes" 3329937e979SKyle Evansend 3339937e979SKyle Evans 3345c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 335ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl) 336aedd6be5SKyle Evans local new_tbl = {} 3375c1b5165SKyle Evans for k, v in pairs(tbl) do 3389f71d421SKyle Evans if type(v) == "table" then 339ee4e69f1SKyle Evans new_tbl[k] = core.deepCopyTable(v) 3405c1b5165SKyle Evans else 341aedd6be5SKyle Evans new_tbl[k] = v 3425c1b5165SKyle Evans end 3435c1b5165SKyle Evans end 344aedd6be5SKyle Evans return new_tbl 3455c1b5165SKyle Evansend 3465c1b5165SKyle Evans 3476d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3486d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3496d4ed94dSKyle Evans-- for our uses 3506d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3516d4ed94dSKyle Evans -- Shouldn't reasonably happen 3529f71d421SKyle Evans if #tbl == 0 then 353aedd6be5SKyle Evans return nil, nil 3549f71d421SKyle Evans elseif #tbl == 1 then 355aedd6be5SKyle Evans return tbl[1], {} 3566d4ed94dSKyle Evans end 3576d4ed94dSKyle Evans 358aedd6be5SKyle Evans local first_value = tbl[1] 359aedd6be5SKyle Evans local new_tbl = {} 3606d4ed94dSKyle Evans -- This is not a cheap operation 3616d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3629f71d421SKyle Evans if k > 1 then 363aedd6be5SKyle Evans new_tbl[k - 1] = v 3646d4ed94dSKyle Evans end 3656d4ed94dSKyle Evans end 3666d4ed94dSKyle Evans 367aedd6be5SKyle Evans return first_value, new_tbl 3686d4ed94dSKyle Evansend 3696d4ed94dSKyle Evans 3706f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3716f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3726f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3736f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3749f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 375aedd6be5SKyle Evans core.setACPI(true) 3766f412147SKyle Evansend 377aea262bfSKyle Evans 378aea262bfSKyle Evanshook.register("config.reloaded", core.clearCachedKernels) 379aedd6be5SKyle Evansreturn core 380