core.lua revision 344220
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 344220 2019-02-17 02:39:17Z 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) 72344220Skevans local status, ret = pcall(require, module) 73344220Skevans -- ret is the module if we succeeded. 74344220Skevans if status then 75344220Skevans return ret 76344220Skevans end 77344220Skevans return false, ret 78344220Skevansend 79344220Skevans 80344220Skevans-- Module exports 81344220Skevans-- Commonly appearing constants 82344220Skevanscore.KEY_BACKSPACE = 8 83344220Skevanscore.KEY_ENTER = 13 84344220Skevanscore.KEY_DELETE = 127 85344220Skevans 86344220Skevans-- Note that this is a decimal representation, despite the leading 0 that in 87344220Skevans-- other contexts (outside of Lua) may mean 'octal' 88344220Skevanscore.KEYSTR_ESCAPE = "\027" 89344220Skevanscore.KEYSTR_CSI = core.KEYSTR_ESCAPE .. "[" 90344220Skevans 91344220Skevanscore.MENU_RETURN = "return" 92344220Skevanscore.MENU_ENTRY = "entry" 93344220Skevanscore.MENU_SEPARATOR = "separator" 94344220Skevanscore.MENU_SUBMENU = "submenu" 95344220Skevanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 96344220Skevans 97344220Skevansfunction core.setVerbose(verbose) 98344220Skevans if verbose == nil then 99344220Skevans verbose = not core.verbose 100344220Skevans end 101344220Skevans 102344220Skevans if verbose then 103344220Skevans loader.setenv("boot_verbose", "YES") 104329167Simp else 105344220Skevans loader.unsetenv("boot_verbose") 106329167Simp end 107344220Skevans core.verbose = verbose 108329167Simpend 109329167Simp 110344220Skevansfunction core.setSingleUser(single_user) 111344220Skevans if single_user == nil then 112344220Skevans single_user = not core.su 113329167Simp end 114329167Simp 115344220Skevans if single_user then 116344220Skevans loader.setenv("boot_single", "YES") 117329167Simp else 118344220Skevans loader.unsetenv("boot_single") 119329167Simp end 120344220Skevans core.su = single_user 121329167Simpend 122329167Simp 123344220Skevansfunction core.getACPIPresent(checking_system_defaults) 124344220Skevans local c = loader.getenv("hint.acpi.0.rsdp") 125344220Skevans 126344220Skevans if c ~= nil then 127344220Skevans if checking_system_defaults then 128344220Skevans return true 129344220Skevans end 130344220Skevans -- Otherwise, respect disabled if it's set 131344220Skevans c = loader.getenv("hint.acpi.0.disabled") 132344220Skevans return c == nil or tonumber(c) ~= 1 133329167Simp end 134344220Skevans return false 135344220Skevansend 136329167Simp 137344220Skevansfunction core.setACPI(acpi) 138344220Skevans if acpi == nil then 139344220Skevans acpi = not core.acpi 140344220Skevans end 141344220Skevans 142344220Skevans if acpi then 143344220Skevans loader.setenv("acpi_load", "YES") 144344220Skevans loader.setenv("hint.acpi.0.disabled", "0") 145344220Skevans loader.unsetenv("loader.acpi_disabled_by_user") 146329167Simp else 147344220Skevans loader.unsetenv("acpi_load") 148344220Skevans loader.setenv("hint.acpi.0.disabled", "1") 149344220Skevans loader.setenv("loader.acpi_disabled_by_user", "1") 150329167Simp end 151344220Skevans core.acpi = acpi 152329167Simpend 153329167Simp 154344220Skevansfunction core.setSafeMode(safe_mode) 155344220Skevans if safe_mode == nil then 156344220Skevans safe_mode = not core.sm 157329167Simp end 158344220Skevans if safe_mode then 159344220Skevans loader.setenv("kern.smp.disabled", "1") 160344220Skevans loader.setenv("hw.ata.ata_dma", "0") 161344220Skevans loader.setenv("hw.ata.atapi_dma", "0") 162344220Skevans loader.setenv("hw.ata.wc", "0") 163344220Skevans loader.setenv("hw.eisa_slots", "0") 164344220Skevans loader.setenv("kern.eventtimer.periodic", "1") 165344220Skevans loader.setenv("kern.geom.part.check_integrity", "0") 166329167Simp else 167344220Skevans loader.unsetenv("kern.smp.disabled") 168344220Skevans loader.unsetenv("hw.ata.ata_dma") 169344220Skevans loader.unsetenv("hw.ata.atapi_dma") 170344220Skevans loader.unsetenv("hw.ata.wc") 171344220Skevans loader.unsetenv("hw.eisa_slots") 172344220Skevans loader.unsetenv("kern.eventtimer.periodic") 173344220Skevans loader.unsetenv("kern.geom.part.check_integrity") 174329167Simp end 175344220Skevans core.sm = safe_mode 176329167Simpend 177329167Simp 178344220Skevansfunction core.clearCachedKernels() 179344220Skevans -- Clear the kernel cache on config changes, autodetect might have 180344220Skevans -- changed or if we've switched boot environments then we could have 181344220Skevans -- a new kernel set. 182344220Skevans core.cached_kernels = nil 183344220Skevansend 184344220Skevans 185329167Simpfunction core.kernelList() 186344220Skevans if core.cached_kernels ~= nil then 187344220Skevans return core.cached_kernels 188344220Skevans end 189329167Simp 190344220Skevans local k = loader.getenv("kernel") 191344220Skevans local v = loader.getenv("kernels") 192344220Skevans local autodetect = loader.getenv("kernels_autodetect") or "" 193344220Skevans 194344220Skevans local kernels = {} 195344220Skevans local unique = {} 196344220Skevans local i = 0 197329167Simp if k ~= nil then 198344220Skevans i = i + 1 199344220Skevans kernels[i] = k 200344220Skevans unique[k] = true 201329167Simp end 202329167Simp 203344220Skevans if v ~= nil then 204344220Skevans for n in v:gmatch("([^;, ]+)[;, ]?") do 205344220Skevans if unique[n] == nil then 206344220Skevans i = i + 1 207344220Skevans kernels[i] = n 208344220Skevans unique[n] = true 209344220Skevans end 210329167Simp end 211329167Simp end 212344220Skevans 213344220Skevans -- Base whether we autodetect kernels or not on a loader.conf(5) 214344220Skevans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 215344220Skevans -- any kernels we detect based on the criteria described. 216344220Skevans if autodetect:lower() ~= "yes" then 217344220Skevans core.cached_kernels = kernels 218344220Skevans return core.cached_kernels 219344220Skevans end 220344220Skevans 221344220Skevans -- Automatically detect other bootable kernel directories using a 222344220Skevans -- heuristic. Any directory in /boot that contains an ordinary file 223344220Skevans -- named "kernel" is considered eligible. 224344220Skevans for file in lfs.dir("/boot") do 225344220Skevans local fname = "/boot/" .. file 226344220Skevans 227344220Skevans if file == "." or file == ".." then 228344220Skevans goto continue 229344220Skevans end 230344220Skevans 231344220Skevans if lfs.attributes(fname, "mode") ~= "directory" then 232344220Skevans goto continue 233344220Skevans end 234344220Skevans 235344220Skevans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 236344220Skevans goto continue 237344220Skevans end 238344220Skevans 239344220Skevans if unique[file] == nil then 240344220Skevans i = i + 1 241344220Skevans kernels[i] = file 242344220Skevans unique[file] = true 243344220Skevans end 244344220Skevans 245344220Skevans ::continue:: 246344220Skevans end 247344220Skevans core.cached_kernels = kernels 248344220Skevans return core.cached_kernels 249329167Simpend 250329167Simp 251344220Skevansfunction core.bootenvDefault() 252344220Skevans return loader.getenv("zfs_be_active") 253344220Skevansend 254344220Skevans 255344220Skevansfunction core.bootenvList() 256344220Skevans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 257344220Skevans local bootenvs = {} 258344220Skevans local curenv 259344220Skevans local envcount = 0 260344220Skevans local unique = {} 261344220Skevans 262344220Skevans if bootenv_count == nil or bootenv_count <= 0 then 263344220Skevans return bootenvs 264344220Skevans end 265344220Skevans 266344220Skevans -- Currently selected bootenv is always first/default 267344220Skevans curenv = core.bootenvDefault() 268344220Skevans if curenv ~= nil then 269344220Skevans envcount = envcount + 1 270344220Skevans bootenvs[envcount] = curenv 271344220Skevans unique[curenv] = true 272344220Skevans end 273344220Skevans 274344220Skevans for curenv_idx = 0, bootenv_count - 1 do 275344220Skevans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 276344220Skevans if curenv ~= nil and unique[curenv] == nil then 277344220Skevans envcount = envcount + 1 278344220Skevans bootenvs[envcount] = curenv 279344220Skevans unique[curenv] = true 280344220Skevans end 281344220Skevans end 282344220Skevans return bootenvs 283344220Skevansend 284344220Skevans 285329167Simpfunction core.setDefaults() 286344220Skevans core.setACPI(core.getACPIPresent(true)) 287344220Skevans core.setSafeMode(default_safe_mode) 288344220Skevans core.setSingleUser(default_single_user) 289344220Skevans core.setVerbose(default_verbose) 290329167Simpend 291329167Simp 292344220Skevansfunction core.autoboot(argstr) 293344220Skevans -- loadelf() only if we've not already loaded a kernel 294344220Skevans if loader.getenv("kernelname") == nil then 295344220Skevans config.loadelf() 296344220Skevans end 297344220Skevans loader.perform(composeLoaderCmd("autoboot", argstr)) 298329167Simpend 299329167Simp 300344220Skevansfunction core.boot(argstr) 301344220Skevans -- loadelf() only if we've not already loaded a kernel 302344220Skevans if loader.getenv("kernelname") == nil then 303344220Skevans config.loadelf() 304344220Skevans end 305344220Skevans loader.perform(composeLoaderCmd("boot", argstr)) 306329167Simpend 307329167Simp 308344220Skevansfunction core.isSingleUserBoot() 309344220Skevans local single_user = loader.getenv("boot_single") 310344220Skevans return single_user ~= nil and single_user:lower() == "yes" 311344220Skevansend 312329167Simp 313344220Skevansfunction core.isZFSBoot() 314344220Skevans local c = loader.getenv("currdev") 315344220Skevans 316329167Simp if c ~= nil then 317344220Skevans return c:match("^zfs:") ~= nil 318329167Simp end 319344220Skevans return false 320344220Skevansend 321329167Simp 322344220Skevansfunction core.isSerialBoot() 323344220Skevans local s = loader.getenv("boot_serial") 324329167Simp if s ~= nil then 325344220Skevans return true 326329167Simp end 327329167Simp 328344220Skevans local m = loader.getenv("boot_multicons") 329329167Simp if m ~= nil then 330344220Skevans return true 331329167Simp end 332344220Skevans return false 333329167Simpend 334329167Simp 335344220Skevansfunction core.isSystem386() 336344220Skevans return loader.machine_arch == "i386" 337344220Skevansend 338344220Skevans 339344220Skevans-- Is the menu skipped in the environment in which we've booted? 340344220Skevansfunction core.isMenuSkipped() 341344220Skevans return string.lower(loader.getenv("beastie_disable") or "") == "yes" 342344220Skevansend 343344220Skevans 344344220Skevans-- This may be a better candidate for a 'utility' module. 345344220Skevansfunction core.deepCopyTable(tbl) 346344220Skevans local new_tbl = {} 347344220Skevans for k, v in pairs(tbl) do 348344220Skevans if type(v) == "table" then 349344220Skevans new_tbl[k] = core.deepCopyTable(v) 350344220Skevans else 351344220Skevans new_tbl[k] = v 352344220Skevans end 353344220Skevans end 354344220Skevans return new_tbl 355344220Skevansend 356344220Skevans 357344220Skevans-- XXX This should go away if we get the table lib into shape for importing. 358344220Skevans-- As of now, it requires some 'os' functions, so we'll implement this in lua 359344220Skevans-- for our uses 360344220Skevansfunction core.popFrontTable(tbl) 361344220Skevans -- Shouldn't reasonably happen 362344220Skevans if #tbl == 0 then 363344220Skevans return nil, nil 364344220Skevans elseif #tbl == 1 then 365344220Skevans return tbl[1], {} 366344220Skevans end 367344220Skevans 368344220Skevans local first_value = tbl[1] 369344220Skevans local new_tbl = {} 370344220Skevans -- This is not a cheap operation 371344220Skevans for k, v in ipairs(tbl) do 372344220Skevans if k > 1 then 373344220Skevans new_tbl[k - 1] = v 374344220Skevans end 375344220Skevans end 376344220Skevans 377344220Skevans return first_value, new_tbl 378344220Skevansend 379344220Skevans 380344220SkevansrecordDefaults() 381344220Skevanshook.register("config.reloaded", core.clearCachedKernels) 382329167Simpreturn core 383