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 44b5746545SKyle Evans-- Module exports 45fe672a15SKyle Evans-- Commonly appearing constants 46aedd6be5SKyle Evanscore.KEY_BACKSPACE = 8 47aedd6be5SKyle Evanscore.KEY_ENTER = 13 48aedd6be5SKyle Evanscore.KEY_DELETE = 127 49fe672a15SKyle Evans 50230061c5SKyle Evans-- Note that this is a decimal representation, despite the leading 0 that in 51230061c5SKyle Evans-- other contexts (outside of Lua) may mean 'octal' 52aedd6be5SKyle Evanscore.KEYSTR_ESCAPE = "\027" 532bb86aefSKyle Evanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 5439006570SKyle Evans 55aedd6be5SKyle Evanscore.MENU_RETURN = "return" 56aedd6be5SKyle Evanscore.MENU_ENTRY = "entry" 57aedd6be5SKyle Evanscore.MENU_SEPARATOR = "separator" 58aedd6be5SKyle Evanscore.MENU_SUBMENU = "submenu" 59aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 60a7cf0562SKyle Evans 6104af4229SKyle Evansfunction core.setVerbose(verbose) 6204af4229SKyle Evans if verbose == nil then 6304af4229SKyle Evans verbose = not core.verbose 64088b4f5fSWarner Losh end 65088b4f5fSWarner Losh 6604af4229SKyle Evans if verbose then 67aedd6be5SKyle Evans loader.setenv("boot_verbose", "YES") 68088b4f5fSWarner Losh else 69aedd6be5SKyle Evans loader.unsetenv("boot_verbose") 70088b4f5fSWarner Losh end 7104af4229SKyle Evans core.verbose = verbose 72088b4f5fSWarner Loshend 73088b4f5fSWarner Losh 7404af4229SKyle Evansfunction core.setSingleUser(single_user) 7504af4229SKyle Evans if single_user == nil then 7604af4229SKyle Evans single_user = not core.su 77088b4f5fSWarner Losh end 78088b4f5fSWarner Losh 7904af4229SKyle Evans if single_user then 80aedd6be5SKyle Evans loader.setenv("boot_single", "YES") 81088b4f5fSWarner Losh else 82aedd6be5SKyle Evans loader.unsetenv("boot_single") 83088b4f5fSWarner Losh end 8404af4229SKyle Evans core.su = single_user 85088b4f5fSWarner Loshend 86088b4f5fSWarner Losh 8704af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults) 88aedd6be5SKyle Evans local c = loader.getenv("hint.acpi.0.rsdp") 896401094fSKyle Evans 909f71d421SKyle Evans if c ~= nil then 9104af4229SKyle Evans if checking_system_defaults then 92aedd6be5SKyle Evans return true 936401094fSKyle Evans end 946401094fSKyle Evans -- Otherwise, respect disabled if it's set 95aedd6be5SKyle Evans c = loader.getenv("hint.acpi.0.disabled") 969f71d421SKyle Evans return c == nil or tonumber(c) ~= 1 976401094fSKyle Evans end 98aedd6be5SKyle Evans return false 996401094fSKyle Evansend 1006401094fSKyle Evans 10104af4229SKyle Evansfunction core.setACPI(acpi) 10204af4229SKyle Evans if acpi == nil then 10304af4229SKyle Evans acpi = not core.acpi 104088b4f5fSWarner Losh end 105088b4f5fSWarner Losh 10604af4229SKyle Evans if acpi then 107aedd6be5SKyle Evans loader.setenv("acpi_load", "YES") 108aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "0") 109aedd6be5SKyle Evans loader.unsetenv("loader.acpi_disabled_by_user") 110088b4f5fSWarner Losh else 111aedd6be5SKyle Evans loader.unsetenv("acpi_load") 112aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "1") 113aedd6be5SKyle Evans loader.setenv("loader.acpi_disabled_by_user", "1") 114088b4f5fSWarner Losh end 11504af4229SKyle Evans core.acpi = acpi 116088b4f5fSWarner Loshend 117088b4f5fSWarner Losh 11804af4229SKyle Evansfunction core.setSafeMode(safe_mode) 11904af4229SKyle Evans if safe_mode == nil then 12004af4229SKyle Evans safe_mode = not core.sm 121088b4f5fSWarner Losh end 12204af4229SKyle Evans if safe_mode then 123aedd6be5SKyle Evans loader.setenv("kern.smp.disabled", "1") 124aedd6be5SKyle Evans loader.setenv("hw.ata.ata_dma", "0") 125aedd6be5SKyle Evans loader.setenv("hw.ata.atapi_dma", "0") 126aedd6be5SKyle Evans loader.setenv("hw.ata.wc", "0") 127aedd6be5SKyle Evans loader.setenv("hw.eisa_slots", "0") 128aedd6be5SKyle Evans loader.setenv("kern.eventtimer.periodic", "1") 129aedd6be5SKyle Evans loader.setenv("kern.geom.part.check_integrity", "0") 130088b4f5fSWarner Losh else 131aedd6be5SKyle Evans loader.unsetenv("kern.smp.disabled") 132aedd6be5SKyle Evans loader.unsetenv("hw.ata.ata_dma") 133aedd6be5SKyle Evans loader.unsetenv("hw.ata.atapi_dma") 134aedd6be5SKyle Evans loader.unsetenv("hw.ata.wc") 135aedd6be5SKyle Evans loader.unsetenv("hw.eisa_slots") 136aedd6be5SKyle Evans loader.unsetenv("kern.eventtimer.periodic") 137aedd6be5SKyle Evans loader.unsetenv("kern.geom.part.check_integrity") 138088b4f5fSWarner Losh end 13904af4229SKyle Evans core.sm = safe_mode 140088b4f5fSWarner Loshend 141088b4f5fSWarner Losh 142aea262bfSKyle Evansfunction core.clearCachedKernels() 14335b0c718SKyle Evans -- Clear the kernel cache on config changes, autodetect might have 14435b0c718SKyle Evans -- changed or if we've switched boot environments then we could have 14535b0c718SKyle Evans -- a new kernel set. 14635b0c718SKyle Evans core.cached_kernels = nil 14735b0c718SKyle Evansend 14835b0c718SKyle Evans 149088b4f5fSWarner Loshfunction core.kernelList() 15035b0c718SKyle Evans if core.cached_kernels ~= nil then 15135b0c718SKyle Evans return core.cached_kernels 15235b0c718SKyle Evans end 15335b0c718SKyle Evans 154aedd6be5SKyle Evans local k = loader.getenv("kernel") 1553f4eb56bSKyle Evans local v = loader.getenv("kernels") 1563889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 157088b4f5fSWarner Losh 158aedd6be5SKyle Evans local kernels = {} 159aedd6be5SKyle Evans local unique = {} 160aedd6be5SKyle Evans local i = 0 1619f71d421SKyle Evans if k ~= nil then 162aedd6be5SKyle Evans i = i + 1 163aedd6be5SKyle Evans kernels[i] = k 164aedd6be5SKyle Evans unique[k] = true 165088b4f5fSWarner Losh end 166088b4f5fSWarner Losh 1673f4eb56bSKyle Evans if v ~= nil then 168088b4f5fSWarner Losh for n in v:gmatch("([^; ]+)[; ]?") do 1699f71d421SKyle Evans if unique[n] == nil then 170aedd6be5SKyle Evans i = i + 1 171aedd6be5SKyle Evans kernels[i] = n 172aedd6be5SKyle Evans unique[n] = true 173088b4f5fSWarner Losh end 174088b4f5fSWarner Losh end 1753889e6cdSKyle Evans end 176a108046fSConrad Meyer 1773889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 1783889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 1793889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 1803889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 18135b0c718SKyle Evans core.cached_kernels = kernels 18235b0c718SKyle Evans return core.cached_kernels 1833f4eb56bSKyle Evans end 1843f4eb56bSKyle Evans 185a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 186a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 187a108046fSConrad Meyer -- named "kernel" is considered eligible. 188a108046fSConrad Meyer for file in lfs.dir("/boot") do 189aedd6be5SKyle Evans local fname = "/boot/" .. file 190a108046fSConrad Meyer 1919f71d421SKyle Evans if file == "." or file == ".." then 192aedd6be5SKyle Evans goto continue 193a108046fSConrad Meyer end 194a108046fSConrad Meyer 1959f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 196aedd6be5SKyle Evans goto continue 197a108046fSConrad Meyer end 198a108046fSConrad Meyer 1999f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 200aedd6be5SKyle Evans goto continue 201a108046fSConrad Meyer end 202a108046fSConrad Meyer 2039f71d421SKyle Evans if unique[file] == nil then 204aedd6be5SKyle Evans i = i + 1 205aedd6be5SKyle Evans kernels[i] = file 206aedd6be5SKyle Evans unique[file] = true 207a108046fSConrad Meyer end 208a108046fSConrad Meyer 209a108046fSConrad Meyer ::continue:: 210a108046fSConrad Meyer end 21135b0c718SKyle Evans core.cached_kernels = kernels 21235b0c718SKyle Evans return core.cached_kernels 213088b4f5fSWarner Loshend 214088b4f5fSWarner Losh 2157efc058fSKyle Evansfunction core.bootenvDefault() 2167efc058fSKyle Evans return loader.getenv("zfs_be_active") 2177efc058fSKyle Evansend 2187efc058fSKyle Evans 2197efc058fSKyle Evansfunction core.bootenvList() 2207efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2217efc058fSKyle Evans local bootenvs = {} 2227efc058fSKyle Evans local curenv 2237efc058fSKyle Evans local envcount = 0 2247efc058fSKyle Evans local unique = {} 2257efc058fSKyle Evans 2267efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2277efc058fSKyle Evans return bootenvs 2287efc058fSKyle Evans end 2297efc058fSKyle Evans 2307efc058fSKyle Evans -- Currently selected bootenv is always first/default 2317efc058fSKyle Evans curenv = core.bootenvDefault() 2327efc058fSKyle Evans if curenv ~= nil then 2337efc058fSKyle Evans envcount = envcount + 1 2347efc058fSKyle Evans bootenvs[envcount] = curenv 2357efc058fSKyle Evans unique[curenv] = true 2367efc058fSKyle Evans end 2377efc058fSKyle Evans 2387efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2397efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2407efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2417efc058fSKyle Evans envcount = envcount + 1 2427efc058fSKyle Evans bootenvs[envcount] = curenv 2437efc058fSKyle Evans unique[curenv] = true 2447efc058fSKyle Evans end 2457efc058fSKyle Evans end 2467efc058fSKyle Evans return bootenvs 2477efc058fSKyle Evansend 2487efc058fSKyle Evans 249088b4f5fSWarner Loshfunction core.setDefaults() 250aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 251aedd6be5SKyle Evans core.setSafeMode(false) 252aedd6be5SKyle Evans core.setSingleUser(false) 253aedd6be5SKyle Evans core.setVerbose(false) 254088b4f5fSWarner Loshend 255088b4f5fSWarner Losh 2566d4ed94dSKyle Evansfunction core.autoboot(argstr) 257a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 258a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 259aedd6be5SKyle Evans config.loadelf() 260a2a7830eSKyle Evans end 261322a2dddSKyle Evans loader.perform(composeLoaderCmd("autoboot", argstr)) 262088b4f5fSWarner Loshend 263088b4f5fSWarner Losh 2646d4ed94dSKyle Evansfunction core.boot(argstr) 265a2a7830eSKyle Evans -- loadelf() only if we've not already loaded a kernel 266a2a7830eSKyle Evans if loader.getenv("kernelname") == nil then 267aedd6be5SKyle Evans config.loadelf() 268a2a7830eSKyle Evans end 269322a2dddSKyle Evans loader.perform(composeLoaderCmd("boot", argstr)) 270088b4f5fSWarner Loshend 271088b4f5fSWarner Losh 272e07fc39cSKyle Evansfunction core.isSingleUserBoot() 273aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 274aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 275e07fc39cSKyle Evansend 276e07fc39cSKyle Evans 277*5f8cfbe1SKyle Evansfunction core.isUEFIBoot() 278*5f8cfbe1SKyle Evans local efiver = loader.getenv("efi-version") 279*5f8cfbe1SKyle Evans 280*5f8cfbe1SKyle Evans return efiver ~= nil 281*5f8cfbe1SKyle Evansend 282*5f8cfbe1SKyle Evans 2837efc058fSKyle Evansfunction core.isZFSBoot() 2847efc058fSKyle Evans local c = loader.getenv("currdev") 2857efc058fSKyle Evans 2867efc058fSKyle Evans if c ~= nil then 2877efc058fSKyle Evans return c:match("^zfs:") ~= nil 2887efc058fSKyle Evans end 2897efc058fSKyle Evans return false 2907efc058fSKyle Evansend 2917efc058fSKyle Evans 292b140d14bSKyle Evansfunction core.isSerialBoot() 293aedd6be5SKyle Evans local c = loader.getenv("console") 294088b4f5fSWarner Losh 2959f71d421SKyle Evans if c ~= nil then 2969f71d421SKyle Evans if c:find("comconsole") ~= nil then 297aedd6be5SKyle Evans return true 298088b4f5fSWarner Losh end 299088b4f5fSWarner Losh end 300088b4f5fSWarner Losh 301aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 3029f71d421SKyle Evans if s ~= nil then 303aedd6be5SKyle Evans return true 304088b4f5fSWarner Losh end 305088b4f5fSWarner Losh 306aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 3079f71d421SKyle Evans if m ~= nil then 308aedd6be5SKyle Evans return true 309088b4f5fSWarner Losh end 310aedd6be5SKyle Evans return false 311088b4f5fSWarner Loshend 312088b4f5fSWarner Losh 313c1ab36f5SKyle Evansfunction core.isSystem386() 3149f71d421SKyle Evans return loader.machine_arch == "i386" 315c1ab36f5SKyle Evansend 316c1ab36f5SKyle Evans 3179937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted? 3189937e979SKyle Evansfunction core.isMenuSkipped() 3199937e979SKyle Evans if core.isSerialBoot() then 3209937e979SKyle Evans return true 3219937e979SKyle Evans end 3229937e979SKyle Evans local c = string.lower(loader.getenv("console") or "") 3239937e979SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 3249937e979SKyle Evans return true 3259937e979SKyle Evans end 3269937e979SKyle Evans 3279937e979SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 3289937e979SKyle Evans return c == "yes" 3299937e979SKyle Evansend 3309937e979SKyle Evans 3315c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 332ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl) 333aedd6be5SKyle Evans local new_tbl = {} 3345c1b5165SKyle Evans for k, v in pairs(tbl) do 3359f71d421SKyle Evans if type(v) == "table" then 336ee4e69f1SKyle Evans new_tbl[k] = core.deepCopyTable(v) 3375c1b5165SKyle Evans else 338aedd6be5SKyle Evans new_tbl[k] = v 3395c1b5165SKyle Evans end 3405c1b5165SKyle Evans end 341aedd6be5SKyle Evans return new_tbl 3425c1b5165SKyle Evansend 3435c1b5165SKyle Evans 3446d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3456d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3466d4ed94dSKyle Evans-- for our uses 3476d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3486d4ed94dSKyle Evans -- Shouldn't reasonably happen 3499f71d421SKyle Evans if #tbl == 0 then 350aedd6be5SKyle Evans return nil, nil 3519f71d421SKyle Evans elseif #tbl == 1 then 352aedd6be5SKyle Evans return tbl[1], {} 3536d4ed94dSKyle Evans end 3546d4ed94dSKyle Evans 355aedd6be5SKyle Evans local first_value = tbl[1] 356aedd6be5SKyle Evans local new_tbl = {} 3576d4ed94dSKyle Evans -- This is not a cheap operation 3586d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3599f71d421SKyle Evans if k > 1 then 360aedd6be5SKyle Evans new_tbl[k - 1] = v 3616d4ed94dSKyle Evans end 3626d4ed94dSKyle Evans end 3636d4ed94dSKyle Evans 364aedd6be5SKyle Evans return first_value, new_tbl 3656d4ed94dSKyle Evansend 3666d4ed94dSKyle Evans 3676f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3686f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3696f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3706f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3719f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 372aedd6be5SKyle Evans core.setACPI(true) 3736f412147SKyle Evansend 374aea262bfSKyle Evans 375aea262bfSKyle Evanshook.register("config.reloaded", core.clearCachedKernels) 376aedd6be5SKyle Evansreturn core 377