1329167Simp-- 2344220Skevans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3344220Skevans-- 4329167Simp-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 5344220Skevans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6329167Simp-- All rights reserved. 7329167Simp-- 8329167Simp-- Redistribution and use in source and binary forms, with or without 9329167Simp-- modification, are permitted provided that the following conditions 10329167Simp-- are met: 11329167Simp-- 1. Redistributions of source code must retain the above copyright 12329167Simp-- notice, this list of conditions and the following disclaimer. 13329167Simp-- 2. Redistributions in binary form must reproduce the above copyright 14329167Simp-- notice, this list of conditions and the following disclaimer in the 15329167Simp-- documentation and/or other materials provided with the distribution. 16329167Simp-- 17329167Simp-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18329167Simp-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19329167Simp-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20329167Simp-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21329167Simp-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22329167Simp-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23329167Simp-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24329167Simp-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25329167Simp-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26329167Simp-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27329167Simp-- SUCH DAMAGE. 28329167Simp-- 29329167Simp-- $FreeBSD: stable/11/stand/lua/core.lua 360599 2020-05-03 04:03:05Z kevans $ 30329167Simp-- 31329167Simp 32344220Skevanslocal config = require("config") 33344220Skevanslocal hook = require("hook") 34329167Simp 35344220Skevanslocal core = {} 36344220Skevans 37344220Skevanslocal default_safe_mode = false 38344220Skevanslocal default_single_user = false 39344220Skevanslocal default_verbose = false 40344220Skevans 41344220Skevanslocal function composeLoaderCmd(cmd_name, argstr) 42344220Skevans if argstr ~= nil then 43344220Skevans cmd_name = cmd_name .. " " .. argstr 44329167Simp end 45344220Skevans return cmd_name 46344220Skevansend 47329167Simp 48344220Skevanslocal function recordDefaults() 49344220Skevans -- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, 50344220Skevans -- it will generally be set upon execution of the kernel. Because of 51344220Skevans -- this, we can't (or don't really want to) detect/disable ACPI on !i386 52344220Skevans -- reliably. Just set it enabled if we detect it and leave well enough 53344220Skevans -- alone if we don't. 54344220Skevans local boot_acpi = core.isSystem386() and core.getACPIPresent(false) 55344220Skevans local boot_single = loader.getenv("boot_single") or "no" 56344220Skevans local boot_verbose = loader.getenv("boot_verbose") or "no" 57344220Skevans default_single_user = boot_single:lower() ~= "no" 58344220Skevans default_verbose = boot_verbose:lower() ~= "no" 59344220Skevans 60344220Skevans if boot_acpi then 61344220Skevans core.setACPI(true) 62344220Skevans end 63344220Skevans core.setSingleUser(default_single_user) 64344220Skevans core.setVerbose(default_verbose) 65344220Skevansend 66344220Skevans 67344220Skevans 68344220Skevans-- Globals 69344220Skevans-- try_include will return the loaded module on success, or false and the error 70344220Skevans-- message on failure. 71344220Skevansfunction try_include(module) 72355661Skevans if module:sub(1, 1) ~= "/" then 73355661Skevans local lua_path = loader.lua_path 74355661Skevans -- XXX Temporary compat shim; this should be removed once the 75355661Skevans -- loader.lua_path export has sufficiently spread. 76355661Skevans if lua_path == nil then 77355661Skevans lua_path = "/boot/lua" 78355661Skevans end 79355661Skevans module = lua_path .. "/" .. module 80355661Skevans -- We only attempt to append an extension if an absolute path 81355661Skevans -- wasn't specified. This assumes that the caller either wants 82355661Skevans -- to treat this like it would require() and specify just the 83355661Skevans -- base filename, or they know what they're doing as they've 84355661Skevans -- specified an absolute path and we shouldn't impede. 85355661Skevans if module:match(".lua$") == nil then 86355661Skevans module = module .. ".lua" 87355661Skevans end 88344220Skevans end 89355661Skevans if lfs.attributes(module, "mode") ~= "file" then 90355661Skevans return 91355661Skevans end 92355661Skevans 93355661Skevans return dofile(module) 94344220Skevansend 95344220Skevans 96344220Skevans-- Module exports 97344220Skevans-- Commonly appearing constants 98344220Skevanscore.KEY_BACKSPACE = 8 99344220Skevanscore.KEY_ENTER = 13 100344220Skevanscore.KEY_DELETE = 127 101344220Skevans 102344220Skevans-- Note that this is a decimal representation, despite the leading 0 that in 103344220Skevans-- other contexts (outside of Lua) may mean 'octal' 104344220Skevanscore.KEYSTR_ESCAPE = "\027" 105344220Skevanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 106360599Skevanscore.KEYSTR_RESET = core.KEYSTR_ESCAPE .. "c" 107344220Skevans 108344220Skevanscore.MENU_RETURN = "return" 109344220Skevanscore.MENU_ENTRY = "entry" 110344220Skevanscore.MENU_SEPARATOR = "separator" 111344220Skevanscore.MENU_SUBMENU = "submenu" 112344220Skevanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 113344220Skevans 114344220Skevansfunction core.setVerbose(verbose) 115344220Skevans if verbose == nil then 116344220Skevans verbose = not core.verbose 117344220Skevans end 118344220Skevans 119344220Skevans if verbose then 120344220Skevans loader.setenv("boot_verbose", "YES") 121329167Simp else 122344220Skevans loader.unsetenv("boot_verbose") 123329167Simp end 124344220Skevans core.verbose = verbose 125329167Simpend 126329167Simp 127344220Skevansfunction core.setSingleUser(single_user) 128344220Skevans if single_user == nil then 129344220Skevans single_user = not core.su 130329167Simp end 131329167Simp 132344220Skevans if single_user then 133344220Skevans loader.setenv("boot_single", "YES") 134329167Simp else 135344220Skevans loader.unsetenv("boot_single") 136329167Simp end 137344220Skevans core.su = single_user 138329167Simpend 139329167Simp 140344220Skevansfunction core.getACPIPresent(checking_system_defaults) 141344220Skevans local c = loader.getenv("hint.acpi.0.rsdp") 142344220Skevans 143344220Skevans if c ~= nil then 144344220Skevans if checking_system_defaults then 145344220Skevans return true 146344220Skevans end 147344220Skevans -- Otherwise, respect disabled if it's set 148344220Skevans c = loader.getenv("hint.acpi.0.disabled") 149344220Skevans return c == nil or tonumber(c) ~= 1 150329167Simp end 151344220Skevans return false 152344220Skevansend 153329167Simp 154344220Skevansfunction core.setACPI(acpi) 155344220Skevans if acpi == nil then 156344220Skevans acpi = not core.acpi 157344220Skevans end 158344220Skevans 159344220Skevans if acpi then 160344220Skevans loader.setenv("acpi_load", "YES") 161344220Skevans loader.setenv("hint.acpi.0.disabled", "0") 162344220Skevans loader.unsetenv("loader.acpi_disabled_by_user") 163329167Simp else 164344220Skevans loader.unsetenv("acpi_load") 165344220Skevans loader.setenv("hint.acpi.0.disabled", "1") 166344220Skevans loader.setenv("loader.acpi_disabled_by_user", "1") 167329167Simp end 168344220Skevans core.acpi = acpi 169329167Simpend 170329167Simp 171344220Skevansfunction core.setSafeMode(safe_mode) 172344220Skevans if safe_mode == nil then 173344220Skevans safe_mode = not core.sm 174329167Simp end 175344220Skevans if safe_mode then 176344220Skevans loader.setenv("kern.smp.disabled", "1") 177344220Skevans loader.setenv("hw.ata.ata_dma", "0") 178344220Skevans loader.setenv("hw.ata.atapi_dma", "0") 179344220Skevans loader.setenv("hw.ata.wc", "0") 180344220Skevans loader.setenv("hw.eisa_slots", "0") 181344220Skevans loader.setenv("kern.eventtimer.periodic", "1") 182344220Skevans loader.setenv("kern.geom.part.check_integrity", "0") 183329167Simp else 184344220Skevans loader.unsetenv("kern.smp.disabled") 185344220Skevans loader.unsetenv("hw.ata.ata_dma") 186344220Skevans loader.unsetenv("hw.ata.atapi_dma") 187344220Skevans loader.unsetenv("hw.ata.wc") 188344220Skevans loader.unsetenv("hw.eisa_slots") 189344220Skevans loader.unsetenv("kern.eventtimer.periodic") 190344220Skevans loader.unsetenv("kern.geom.part.check_integrity") 191329167Simp end 192344220Skevans core.sm = safe_mode 193329167Simpend 194329167Simp 195344220Skevansfunction core.clearCachedKernels() 196344220Skevans -- Clear the kernel cache on config changes, autodetect might have 197344220Skevans -- changed or if we've switched boot environments then we could have 198344220Skevans -- a new kernel set. 199344220Skevans core.cached_kernels = nil 200344220Skevansend 201344220Skevans 202329167Simpfunction core.kernelList() 203344220Skevans if core.cached_kernels ~= nil then 204344220Skevans return core.cached_kernels 205344220Skevans end 206329167Simp 207344220Skevans local k = loader.getenv("kernel") 208344220Skevans local v = loader.getenv("kernels") 209344220Skevans local autodetect = loader.getenv("kernels_autodetect") or "" 210344220Skevans 211344220Skevans local kernels = {} 212344220Skevans local unique = {} 213344220Skevans local i = 0 214329167Simp if k ~= nil then 215344220Skevans i = i + 1 216344220Skevans kernels[i] = k 217344220Skevans unique[k] = true 218329167Simp end 219329167Simp 220344220Skevans if v ~= nil then 221344220Skevans for n in v:gmatch("([^;, ]+)[;, ]?") do 222344220Skevans if unique[n] == nil then 223344220Skevans i = i + 1 224344220Skevans kernels[i] = n 225344220Skevans unique[n] = true 226344220Skevans end 227329167Simp end 228329167Simp end 229344220Skevans 230344220Skevans -- Base whether we autodetect kernels or not on a loader.conf(5) 231344220Skevans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 232344220Skevans -- any kernels we detect based on the criteria described. 233344220Skevans if autodetect:lower() ~= "yes" then 234344220Skevans core.cached_kernels = kernels 235344220Skevans return core.cached_kernels 236344220Skevans end 237344220Skevans 238344220Skevans -- Automatically detect other bootable kernel directories using a 239344220Skevans -- heuristic. Any directory in /boot that contains an ordinary file 240344220Skevans -- named "kernel" is considered eligible. 241344220Skevans for file in lfs.dir("/boot") do 242344220Skevans local fname = "/boot/" .. file 243344220Skevans 244344220Skevans if file == "." or file == ".." then 245344220Skevans goto continue 246344220Skevans end 247344220Skevans 248344220Skevans if lfs.attributes(fname, "mode") ~= "directory" then 249344220Skevans goto continue 250344220Skevans end 251344220Skevans 252344220Skevans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 253344220Skevans goto continue 254344220Skevans end 255344220Skevans 256344220Skevans if unique[file] == nil then 257344220Skevans i = i + 1 258344220Skevans kernels[i] = file 259344220Skevans unique[file] = true 260344220Skevans end 261344220Skevans 262344220Skevans ::continue:: 263344220Skevans end 264344220Skevans core.cached_kernels = kernels 265344220Skevans return core.cached_kernels 266329167Simpend 267329167Simp 268344220Skevansfunction core.bootenvDefault() 269344220Skevans return loader.getenv("zfs_be_active") 270344220Skevansend 271344220Skevans 272344220Skevansfunction core.bootenvList() 273344220Skevans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 274344220Skevans local bootenvs = {} 275344220Skevans local curenv 276344220Skevans local envcount = 0 277344220Skevans local unique = {} 278344220Skevans 279344220Skevans if bootenv_count == nil or bootenv_count <= 0 then 280344220Skevans return bootenvs 281344220Skevans end 282344220Skevans 283344220Skevans -- Currently selected bootenv is always first/default 284344220Skevans curenv = core.bootenvDefault() 285344220Skevans if curenv ~= nil then 286344220Skevans envcount = envcount + 1 287344220Skevans bootenvs[envcount] = curenv 288344220Skevans unique[curenv] = true 289344220Skevans end 290344220Skevans 291344220Skevans for curenv_idx = 0, bootenv_count - 1 do 292344220Skevans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 293344220Skevans if curenv ~= nil and unique[curenv] == nil then 294344220Skevans envcount = envcount + 1 295344220Skevans bootenvs[envcount] = curenv 296344220Skevans unique[curenv] = true 297344220Skevans end 298344220Skevans end 299344220Skevans return bootenvs 300344220Skevansend 301344220Skevans 302329167Simpfunction core.setDefaults() 303344220Skevans core.setACPI(core.getACPIPresent(true)) 304344220Skevans core.setSafeMode(default_safe_mode) 305344220Skevans core.setSingleUser(default_single_user) 306344220Skevans core.setVerbose(default_verbose) 307329167Simpend 308329167Simp 309344220Skevansfunction core.autoboot(argstr) 310344220Skevans -- loadelf() only if we've not already loaded a kernel 311344220Skevans if loader.getenv("kernelname") == nil then 312344220Skevans config.loadelf() 313344220Skevans end 314344220Skevans loader.perform(composeLoaderCmd("autoboot", argstr)) 315329167Simpend 316329167Simp 317344220Skevansfunction core.boot(argstr) 318344220Skevans -- loadelf() only if we've not already loaded a kernel 319344220Skevans if loader.getenv("kernelname") == nil then 320344220Skevans config.loadelf() 321344220Skevans end 322344220Skevans loader.perform(composeLoaderCmd("boot", argstr)) 323329167Simpend 324329167Simp 325344220Skevansfunction core.isSingleUserBoot() 326344220Skevans local single_user = loader.getenv("boot_single") 327344220Skevans return single_user ~= nil and single_user:lower() == "yes" 328344220Skevansend 329329167Simp 330344220Skevansfunction core.isZFSBoot() 331344220Skevans local c = loader.getenv("currdev") 332344220Skevans 333329167Simp if c ~= nil then 334344220Skevans return c:match("^zfs:") ~= nil 335329167Simp end 336344220Skevans return false 337344220Skevansend 338329167Simp 339352349Skevansfunction core.isSerialConsole() 340352349Skevans local c = loader.getenv("console") 341352349Skevans if c ~= nil then 342352349Skevans if c:find("comconsole") ~= nil then 343352349Skevans return true 344352349Skevans end 345352349Skevans end 346352349Skevans return false 347352349Skevansend 348352349Skevans 349344220Skevansfunction core.isSerialBoot() 350344220Skevans local s = loader.getenv("boot_serial") 351329167Simp if s ~= nil then 352344220Skevans return true 353329167Simp end 354329167Simp 355344220Skevans local m = loader.getenv("boot_multicons") 356329167Simp if m ~= nil then 357344220Skevans return true 358329167Simp end 359344220Skevans return false 360329167Simpend 361329167Simp 362344220Skevansfunction core.isSystem386() 363344220Skevans return loader.machine_arch == "i386" 364344220Skevansend 365344220Skevans 366344220Skevans-- Is the menu skipped in the environment in which we've booted? 367344220Skevansfunction core.isMenuSkipped() 368344220Skevans return string.lower(loader.getenv("beastie_disable") or "") == "yes" 369344220Skevansend 370344220Skevans 371344220Skevans-- This may be a better candidate for a 'utility' module. 372344220Skevansfunction core.deepCopyTable(tbl) 373344220Skevans local new_tbl = {} 374344220Skevans for k, v in pairs(tbl) do 375344220Skevans if type(v) == "table" then 376344220Skevans new_tbl[k] = core.deepCopyTable(v) 377344220Skevans else 378344220Skevans new_tbl[k] = v 379344220Skevans end 380344220Skevans end 381344220Skevans return new_tbl 382344220Skevansend 383344220Skevans 384344220Skevans-- XXX This should go away if we get the table lib into shape for importing. 385344220Skevans-- As of now, it requires some 'os' functions, so we'll implement this in lua 386344220Skevans-- for our uses 387344220Skevansfunction core.popFrontTable(tbl) 388344220Skevans -- Shouldn't reasonably happen 389344220Skevans if #tbl == 0 then 390344220Skevans return nil, nil 391344220Skevans elseif #tbl == 1 then 392344220Skevans return tbl[1], {} 393344220Skevans end 394344220Skevans 395344220Skevans local first_value = tbl[1] 396344220Skevans local new_tbl = {} 397344220Skevans -- This is not a cheap operation 398344220Skevans for k, v in ipairs(tbl) do 399344220Skevans if k > 1 then 400344220Skevans new_tbl[k - 1] = v 401344220Skevans end 402344220Skevans end 403344220Skevans 404344220Skevans return first_value, new_tbl 405344220Skevansend 406344220Skevans 407344220SkevansrecordDefaults() 408344220Skevanshook.register("config.reloaded", core.clearCachedKernels) 409329167Simpreturn core 410