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 49aedd6be5SKyle Evanscore.KEYSTR_ESCAPE = "\027" 50*2bb86aefSKyle Evanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 5139006570SKyle Evans 52aedd6be5SKyle Evanscore.MENU_RETURN = "return" 53aedd6be5SKyle Evanscore.MENU_ENTRY = "entry" 54aedd6be5SKyle Evanscore.MENU_SEPARATOR = "separator" 55aedd6be5SKyle Evanscore.MENU_SUBMENU = "submenu" 56aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 57a7cf0562SKyle Evans 5804af4229SKyle Evansfunction core.setVerbose(verbose) 5904af4229SKyle Evans if verbose == nil then 6004af4229SKyle Evans verbose = not core.verbose 61088b4f5fSWarner Losh end 62088b4f5fSWarner Losh 6304af4229SKyle Evans if verbose then 64aedd6be5SKyle Evans loader.setenv("boot_verbose", "YES") 65088b4f5fSWarner Losh else 66aedd6be5SKyle Evans loader.unsetenv("boot_verbose") 67088b4f5fSWarner Losh end 6804af4229SKyle Evans core.verbose = verbose 69088b4f5fSWarner Loshend 70088b4f5fSWarner Losh 7104af4229SKyle Evansfunction core.setSingleUser(single_user) 7204af4229SKyle Evans if single_user == nil then 7304af4229SKyle Evans single_user = not core.su 74088b4f5fSWarner Losh end 75088b4f5fSWarner Losh 7604af4229SKyle Evans if single_user then 77aedd6be5SKyle Evans loader.setenv("boot_single", "YES") 78088b4f5fSWarner Losh else 79aedd6be5SKyle Evans loader.unsetenv("boot_single") 80088b4f5fSWarner Losh end 8104af4229SKyle Evans core.su = single_user 82088b4f5fSWarner Loshend 83088b4f5fSWarner Losh 8404af4229SKyle Evansfunction core.getACPIPresent(checking_system_defaults) 85aedd6be5SKyle Evans local c = loader.getenv("hint.acpi.0.rsdp") 866401094fSKyle Evans 879f71d421SKyle Evans if c ~= nil then 8804af4229SKyle Evans if checking_system_defaults then 89aedd6be5SKyle Evans return true 906401094fSKyle Evans end 916401094fSKyle Evans -- Otherwise, respect disabled if it's set 92aedd6be5SKyle Evans c = loader.getenv("hint.acpi.0.disabled") 939f71d421SKyle Evans return c == nil or tonumber(c) ~= 1 946401094fSKyle Evans end 95aedd6be5SKyle Evans return false 966401094fSKyle Evansend 976401094fSKyle Evans 9804af4229SKyle Evansfunction core.setACPI(acpi) 9904af4229SKyle Evans if acpi == nil then 10004af4229SKyle Evans acpi = not core.acpi 101088b4f5fSWarner Losh end 102088b4f5fSWarner Losh 10304af4229SKyle Evans if acpi then 104aedd6be5SKyle Evans loader.setenv("acpi_load", "YES") 105aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "0") 106aedd6be5SKyle Evans loader.unsetenv("loader.acpi_disabled_by_user") 107088b4f5fSWarner Losh else 108aedd6be5SKyle Evans loader.unsetenv("acpi_load") 109aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "1") 110aedd6be5SKyle Evans loader.setenv("loader.acpi_disabled_by_user", "1") 111088b4f5fSWarner Losh end 11204af4229SKyle Evans core.acpi = acpi 113088b4f5fSWarner Loshend 114088b4f5fSWarner Losh 11504af4229SKyle Evansfunction core.setSafeMode(safe_mode) 11604af4229SKyle Evans if safe_mode == nil then 11704af4229SKyle Evans safe_mode = not core.sm 118088b4f5fSWarner Losh end 11904af4229SKyle Evans if safe_mode then 120aedd6be5SKyle Evans loader.setenv("kern.smp.disabled", "1") 121aedd6be5SKyle Evans loader.setenv("hw.ata.ata_dma", "0") 122aedd6be5SKyle Evans loader.setenv("hw.ata.atapi_dma", "0") 123aedd6be5SKyle Evans loader.setenv("hw.ata.wc", "0") 124aedd6be5SKyle Evans loader.setenv("hw.eisa_slots", "0") 125aedd6be5SKyle Evans loader.setenv("kern.eventtimer.periodic", "1") 126aedd6be5SKyle Evans loader.setenv("kern.geom.part.check_integrity", "0") 127088b4f5fSWarner Losh else 128aedd6be5SKyle Evans loader.unsetenv("kern.smp.disabled") 129aedd6be5SKyle Evans loader.unsetenv("hw.ata.ata_dma") 130aedd6be5SKyle Evans loader.unsetenv("hw.ata.atapi_dma") 131aedd6be5SKyle Evans loader.unsetenv("hw.ata.wc") 132aedd6be5SKyle Evans loader.unsetenv("hw.eisa_slots") 133aedd6be5SKyle Evans loader.unsetenv("kern.eventtimer.periodic") 134aedd6be5SKyle Evans loader.unsetenv("kern.geom.part.check_integrity") 135088b4f5fSWarner Losh end 13604af4229SKyle Evans core.sm = safe_mode 137088b4f5fSWarner Loshend 138088b4f5fSWarner Losh 139088b4f5fSWarner Loshfunction core.kernelList() 140aedd6be5SKyle Evans local k = loader.getenv("kernel") 1413f4eb56bSKyle Evans local v = loader.getenv("kernels") 1423889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 143088b4f5fSWarner Losh 144aedd6be5SKyle Evans local kernels = {} 145aedd6be5SKyle Evans local unique = {} 146aedd6be5SKyle Evans local i = 0 1479f71d421SKyle Evans if k ~= nil then 148aedd6be5SKyle Evans i = i + 1 149aedd6be5SKyle Evans kernels[i] = k 150aedd6be5SKyle Evans unique[k] = true 151088b4f5fSWarner Losh end 152088b4f5fSWarner Losh 1533f4eb56bSKyle Evans if v ~= nil then 154088b4f5fSWarner Losh for n in v:gmatch("([^; ]+)[; ]?") do 1559f71d421SKyle Evans if unique[n] == nil then 156aedd6be5SKyle Evans i = i + 1 157aedd6be5SKyle Evans kernels[i] = n 158aedd6be5SKyle Evans unique[n] = true 159088b4f5fSWarner Losh end 160088b4f5fSWarner Losh end 1613889e6cdSKyle Evans end 162a108046fSConrad Meyer 1633889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 1643889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 1653889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 1663889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 1673f4eb56bSKyle Evans return kernels 1683f4eb56bSKyle Evans end 1693f4eb56bSKyle Evans 170a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 171a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 172a108046fSConrad Meyer -- named "kernel" is considered eligible. 173a108046fSConrad Meyer for file in lfs.dir("/boot") do 174aedd6be5SKyle Evans local fname = "/boot/" .. file 175a108046fSConrad Meyer 1769f71d421SKyle Evans if file == "." or file == ".." then 177aedd6be5SKyle Evans goto continue 178a108046fSConrad Meyer end 179a108046fSConrad Meyer 1809f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 181aedd6be5SKyle Evans goto continue 182a108046fSConrad Meyer end 183a108046fSConrad Meyer 1849f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 185aedd6be5SKyle Evans goto continue 186a108046fSConrad Meyer end 187a108046fSConrad Meyer 1889f71d421SKyle Evans if unique[file] == nil then 189aedd6be5SKyle Evans i = i + 1 190aedd6be5SKyle Evans kernels[i] = file 191aedd6be5SKyle Evans unique[file] = true 192a108046fSConrad Meyer end 193a108046fSConrad Meyer 194a108046fSConrad Meyer ::continue:: 195a108046fSConrad Meyer end 196aedd6be5SKyle Evans return kernels 197088b4f5fSWarner Loshend 198088b4f5fSWarner Losh 1997efc058fSKyle Evansfunction core.bootenvDefault() 2007efc058fSKyle Evans return loader.getenv("zfs_be_active") 2017efc058fSKyle Evansend 2027efc058fSKyle Evans 2037efc058fSKyle Evansfunction core.bootenvList() 2047efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2057efc058fSKyle Evans local bootenvs = {} 2067efc058fSKyle Evans local curenv 2077efc058fSKyle Evans local envcount = 0 2087efc058fSKyle Evans local unique = {} 2097efc058fSKyle Evans 2107efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2117efc058fSKyle Evans return bootenvs 2127efc058fSKyle Evans end 2137efc058fSKyle Evans 2147efc058fSKyle Evans -- Currently selected bootenv is always first/default 2157efc058fSKyle Evans curenv = core.bootenvDefault() 2167efc058fSKyle Evans if curenv ~= nil then 2177efc058fSKyle Evans envcount = envcount + 1 2187efc058fSKyle Evans bootenvs[envcount] = curenv 2197efc058fSKyle Evans unique[curenv] = true 2207efc058fSKyle Evans end 2217efc058fSKyle Evans 2227efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2237efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2247efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2257efc058fSKyle Evans envcount = envcount + 1 2267efc058fSKyle Evans bootenvs[envcount] = curenv 2277efc058fSKyle Evans unique[curenv] = true 2287efc058fSKyle Evans end 2297efc058fSKyle Evans end 2307efc058fSKyle Evans return bootenvs 2317efc058fSKyle Evansend 2327efc058fSKyle Evans 233088b4f5fSWarner Loshfunction core.setDefaults() 234aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 235aedd6be5SKyle Evans core.setSafeMode(false) 236aedd6be5SKyle Evans core.setSingleUser(false) 237aedd6be5SKyle Evans core.setVerbose(false) 238088b4f5fSWarner Loshend 239088b4f5fSWarner Losh 2406d4ed94dSKyle Evansfunction core.autoboot(argstr) 241aedd6be5SKyle Evans config.loadelf() 242322a2dddSKyle Evans loader.perform(composeLoaderCmd("autoboot", argstr)) 243088b4f5fSWarner Loshend 244088b4f5fSWarner Losh 2456d4ed94dSKyle Evansfunction core.boot(argstr) 246aedd6be5SKyle Evans config.loadelf() 247322a2dddSKyle Evans loader.perform(composeLoaderCmd("boot", argstr)) 248088b4f5fSWarner Loshend 249088b4f5fSWarner Losh 250e07fc39cSKyle Evansfunction core.isSingleUserBoot() 251aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 252aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 253e07fc39cSKyle Evansend 254e07fc39cSKyle Evans 2557efc058fSKyle Evansfunction core.isZFSBoot() 2567efc058fSKyle Evans local c = loader.getenv("currdev") 2577efc058fSKyle Evans 2587efc058fSKyle Evans if c ~= nil then 2597efc058fSKyle Evans return c:match("^zfs:") ~= nil 2607efc058fSKyle Evans end 2617efc058fSKyle Evans return false 2627efc058fSKyle Evansend 2637efc058fSKyle Evans 264b140d14bSKyle Evansfunction core.isSerialBoot() 265aedd6be5SKyle Evans local c = loader.getenv("console") 266088b4f5fSWarner Losh 2679f71d421SKyle Evans if c ~= nil then 2689f71d421SKyle Evans if c:find("comconsole") ~= nil then 269aedd6be5SKyle Evans return true 270088b4f5fSWarner Losh end 271088b4f5fSWarner Losh end 272088b4f5fSWarner Losh 273aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 2749f71d421SKyle Evans if s ~= nil then 275aedd6be5SKyle Evans return true 276088b4f5fSWarner Losh end 277088b4f5fSWarner Losh 278aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 2799f71d421SKyle Evans if m ~= nil then 280aedd6be5SKyle Evans return true 281088b4f5fSWarner Losh end 282aedd6be5SKyle Evans return false 283088b4f5fSWarner Loshend 284088b4f5fSWarner Losh 285c1ab36f5SKyle Evansfunction core.isSystem386() 2869f71d421SKyle Evans return loader.machine_arch == "i386" 287c1ab36f5SKyle Evansend 288c1ab36f5SKyle Evans 2899937e979SKyle Evans-- Is the menu skipped in the environment in which we've booted? 2909937e979SKyle Evansfunction core.isMenuSkipped() 2919937e979SKyle Evans if core.isSerialBoot() then 2929937e979SKyle Evans return true 2939937e979SKyle Evans end 2949937e979SKyle Evans local c = string.lower(loader.getenv("console") or "") 2959937e979SKyle Evans if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then 2969937e979SKyle Evans return true 2979937e979SKyle Evans end 2989937e979SKyle Evans 2999937e979SKyle Evans c = string.lower(loader.getenv("beastie_disable") or "") 3009937e979SKyle Evans return c == "yes" 3019937e979SKyle Evansend 3029937e979SKyle Evans 3035c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 304ee4e69f1SKyle Evansfunction core.deepCopyTable(tbl) 305aedd6be5SKyle Evans local new_tbl = {} 3065c1b5165SKyle Evans for k, v in pairs(tbl) do 3079f71d421SKyle Evans if type(v) == "table" then 308ee4e69f1SKyle Evans new_tbl[k] = core.deepCopyTable(v) 3095c1b5165SKyle Evans else 310aedd6be5SKyle Evans new_tbl[k] = v 3115c1b5165SKyle Evans end 3125c1b5165SKyle Evans end 313aedd6be5SKyle Evans return new_tbl 3145c1b5165SKyle Evansend 3155c1b5165SKyle Evans 3166d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3176d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3186d4ed94dSKyle Evans-- for our uses 3196d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3206d4ed94dSKyle Evans -- Shouldn't reasonably happen 3219f71d421SKyle Evans if #tbl == 0 then 322aedd6be5SKyle Evans return nil, nil 3239f71d421SKyle Evans elseif #tbl == 1 then 324aedd6be5SKyle Evans return tbl[1], {} 3256d4ed94dSKyle Evans end 3266d4ed94dSKyle Evans 327aedd6be5SKyle Evans local first_value = tbl[1] 328aedd6be5SKyle Evans local new_tbl = {} 3296d4ed94dSKyle Evans -- This is not a cheap operation 3306d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3319f71d421SKyle Evans if k > 1 then 332aedd6be5SKyle Evans new_tbl[k - 1] = v 3336d4ed94dSKyle Evans end 3346d4ed94dSKyle Evans end 3356d4ed94dSKyle Evans 336aedd6be5SKyle Evans return first_value, new_tbl 3376d4ed94dSKyle Evansend 3386d4ed94dSKyle Evans 3396f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3406f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3416f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3426f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3439f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 344aedd6be5SKyle Evans core.setACPI(true) 3456f412147SKyle Evansend 346aedd6be5SKyle Evansreturn core 347