1088b4f5fSWarner Losh-- 2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 3088b4f5fSWarner Losh-- All rights reserved. 4088b4f5fSWarner Losh-- 5088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without 6088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions 7088b4f5fSWarner Losh-- are met: 8088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright 9088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer. 10088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright 11088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer in the 12088b4f5fSWarner Losh-- documentation and/or other materials provided with the distribution. 13088b4f5fSWarner Losh-- 14088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17088b4f5fSWarner Losh-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24088b4f5fSWarner Losh-- SUCH DAMAGE. 25088b4f5fSWarner Losh-- 26088b4f5fSWarner Losh-- $FreeBSD$ 27088b4f5fSWarner Losh-- 28088b4f5fSWarner Losh 29aedd6be5SKyle Evanslocal config = require('config') 30fa4a2394SKyle Evans 31aedd6be5SKyle Evanslocal core = {} 32088b4f5fSWarner Losh 336d4ed94dSKyle Evanslocal compose_loader_cmd = function(cmd_name, argstr) 349f71d421SKyle Evans if argstr ~= nil then 35aedd6be5SKyle Evans cmd_name = cmd_name .. " " .. argstr 366d4ed94dSKyle Evans end 37aedd6be5SKyle Evans return cmd_name 386d4ed94dSKyle Evansend 396d4ed94dSKyle Evans 406d3bcc06SKyle Evans-- Internal function 41f0cb3b6bSKyle Evans-- Parses arguments to boot and returns two values: kernel_name, argstr 42f0cb3b6bSKyle Evans-- Defaults to nil and "" respectively. 436d3bcc06SKyle Evans-- This will also parse arguments to autoboot, but the with_kernel argument 446d3bcc06SKyle Evans-- will need to be explicitly overwritten to false 456d3bcc06SKyle Evanslocal parse_boot_args = function(argv, with_kernel) 469f71d421SKyle Evans if with_kernel == nil then 47aedd6be5SKyle Evans with_kernel = true 486d3bcc06SKyle Evans end 491d38e553SKyle Evans if #argv == 0 then 501d38e553SKyle Evans if with_kernel then 511d38e553SKyle Evans return nil, "" 521d38e553SKyle Evans else 531d38e553SKyle Evans return "" 541d38e553SKyle Evans end 551d38e553SKyle Evans end 56aedd6be5SKyle Evans local kernel_name 57aedd6be5SKyle Evans local argstr = "" 58f0cb3b6bSKyle Evans 59f0cb3b6bSKyle Evans for k, v in ipairs(argv) do 609f71d421SKyle Evans if with_kernel and v:sub(1,1) ~= "-" then 61aedd6be5SKyle Evans kernel_name = v 62f0cb3b6bSKyle Evans else 63aedd6be5SKyle Evans argstr = argstr .. " " .. v 64f0cb3b6bSKyle Evans end 65f0cb3b6bSKyle Evans end 669f71d421SKyle Evans if with_kernel then 67aedd6be5SKyle Evans return kernel_name, argstr 686d3bcc06SKyle Evans else 69aedd6be5SKyle Evans return argstr 706d3bcc06SKyle Evans end 71f0cb3b6bSKyle Evansend 72f0cb3b6bSKyle Evans 73f0cb3b6bSKyle Evans-- Globals 74f0cb3b6bSKyle Evansfunction boot(...) 75aedd6be5SKyle Evans local argv = {...} 76aedd6be5SKyle Evans local cmd_name = "" 77aedd6be5SKyle Evans cmd_name, argv = core.popFrontTable(argv) 78aedd6be5SKyle Evans local kernel, argstr = parse_boot_args(argv) 799f71d421SKyle Evans if kernel ~= nil then 80aedd6be5SKyle Evans loader.perform("unload") 81aedd6be5SKyle Evans config.selectkernel(kernel) 82f0cb3b6bSKyle Evans end 83aedd6be5SKyle Evans core.boot(argstr) 84f0cb3b6bSKyle Evansend 85f0cb3b6bSKyle Evans 866d3bcc06SKyle Evansfunction autoboot(...) 876d3bcc06SKyle Evans local argv = {...} 88aedd6be5SKyle Evans local cmd_name = "" 89aedd6be5SKyle Evans cmd_name, argv = core.popFrontTable(argv) 90aedd6be5SKyle Evans local argstr = parse_boot_args(argv, false) 91aedd6be5SKyle Evans core.autoboot(argstr) 926d3bcc06SKyle Evansend 936d3bcc06SKyle Evans 94b5746545SKyle Evans-- Module exports 95fe672a15SKyle Evans-- Commonly appearing constants 96aedd6be5SKyle Evanscore.KEY_BACKSPACE = 8 97aedd6be5SKyle Evanscore.KEY_ENTER = 13 98aedd6be5SKyle Evanscore.KEY_DELETE = 127 99fe672a15SKyle Evans 100aedd6be5SKyle Evanscore.KEYSTR_ESCAPE = "\027" 10139006570SKyle Evans 102aedd6be5SKyle Evanscore.MENU_RETURN = "return" 103aedd6be5SKyle Evanscore.MENU_ENTRY = "entry" 104aedd6be5SKyle Evanscore.MENU_SEPARATOR = "separator" 105aedd6be5SKyle Evanscore.MENU_SUBMENU = "submenu" 106aedd6be5SKyle Evanscore.MENU_CAROUSEL_ENTRY = "carousel_entry" 107a7cf0562SKyle Evans 108088b4f5fSWarner Loshfunction core.setVerbose(b) 1099f71d421SKyle Evans if b == nil then 110aedd6be5SKyle Evans b = not core.verbose 111088b4f5fSWarner Losh end 112088b4f5fSWarner Losh 11349550489SKyle Evans if b then 114aedd6be5SKyle Evans loader.setenv("boot_verbose", "YES") 115088b4f5fSWarner Losh else 116aedd6be5SKyle Evans loader.unsetenv("boot_verbose") 117088b4f5fSWarner Losh end 118aedd6be5SKyle Evans core.verbose = b 119088b4f5fSWarner Loshend 120088b4f5fSWarner Losh 121088b4f5fSWarner Loshfunction core.setSingleUser(b) 1229f71d421SKyle Evans if b == nil then 123aedd6be5SKyle Evans b = not core.su 124088b4f5fSWarner Losh end 125088b4f5fSWarner Losh 12649550489SKyle Evans if b then 127aedd6be5SKyle Evans loader.setenv("boot_single", "YES") 128088b4f5fSWarner Losh else 129aedd6be5SKyle Evans loader.unsetenv("boot_single") 130088b4f5fSWarner Losh end 131aedd6be5SKyle Evans core.su = b 132088b4f5fSWarner Loshend 133088b4f5fSWarner Losh 1346401094fSKyle Evansfunction core.getACPIPresent(checkingSystemDefaults) 135aedd6be5SKyle Evans local c = loader.getenv("hint.acpi.0.rsdp") 1366401094fSKyle Evans 1379f71d421SKyle Evans if c ~= nil then 13849550489SKyle Evans if checkingSystemDefaults then 139aedd6be5SKyle Evans return true 1406401094fSKyle Evans end 1416401094fSKyle Evans -- Otherwise, respect disabled if it's set 142aedd6be5SKyle Evans c = loader.getenv("hint.acpi.0.disabled") 1439f71d421SKyle Evans return c == nil or tonumber(c) ~= 1 1446401094fSKyle Evans end 145aedd6be5SKyle Evans return false 1466401094fSKyle Evansend 1476401094fSKyle Evans 148088b4f5fSWarner Loshfunction core.setACPI(b) 1499f71d421SKyle Evans if b == nil then 150aedd6be5SKyle Evans b = not core.acpi 151088b4f5fSWarner Losh end 152088b4f5fSWarner Losh 15349550489SKyle Evans if b then 154aedd6be5SKyle Evans loader.setenv("acpi_load", "YES") 155aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "0") 156aedd6be5SKyle Evans loader.unsetenv("loader.acpi_disabled_by_user") 157088b4f5fSWarner Losh else 158aedd6be5SKyle Evans loader.unsetenv("acpi_load") 159aedd6be5SKyle Evans loader.setenv("hint.acpi.0.disabled", "1") 160aedd6be5SKyle Evans loader.setenv("loader.acpi_disabled_by_user", "1") 161088b4f5fSWarner Losh end 162aedd6be5SKyle Evans core.acpi = b 163088b4f5fSWarner Loshend 164088b4f5fSWarner Losh 165088b4f5fSWarner Loshfunction core.setSafeMode(b) 1669f71d421SKyle Evans if b == nil then 167aedd6be5SKyle Evans b = not core.sm 168088b4f5fSWarner Losh end 16949550489SKyle Evans if b then 170aedd6be5SKyle Evans loader.setenv("kern.smp.disabled", "1") 171aedd6be5SKyle Evans loader.setenv("hw.ata.ata_dma", "0") 172aedd6be5SKyle Evans loader.setenv("hw.ata.atapi_dma", "0") 173aedd6be5SKyle Evans loader.setenv("hw.ata.wc", "0") 174aedd6be5SKyle Evans loader.setenv("hw.eisa_slots", "0") 175aedd6be5SKyle Evans loader.setenv("kern.eventtimer.periodic", "1") 176aedd6be5SKyle Evans loader.setenv("kern.geom.part.check_integrity", "0") 177088b4f5fSWarner Losh else 178aedd6be5SKyle Evans loader.unsetenv("kern.smp.disabled") 179aedd6be5SKyle Evans loader.unsetenv("hw.ata.ata_dma") 180aedd6be5SKyle Evans loader.unsetenv("hw.ata.atapi_dma") 181aedd6be5SKyle Evans loader.unsetenv("hw.ata.wc") 182aedd6be5SKyle Evans loader.unsetenv("hw.eisa_slots") 183aedd6be5SKyle Evans loader.unsetenv("kern.eventtimer.periodic") 184aedd6be5SKyle Evans loader.unsetenv("kern.geom.part.check_integrity") 185088b4f5fSWarner Losh end 186aedd6be5SKyle Evans core.sm = b 187088b4f5fSWarner Loshend 188088b4f5fSWarner Losh 189088b4f5fSWarner Loshfunction core.kernelList() 190aedd6be5SKyle Evans local k = loader.getenv("kernel") 1913f4eb56bSKyle Evans local v = loader.getenv("kernels") 192*3889e6cdSKyle Evans local autodetect = loader.getenv("kernels_autodetect") or "" 193088b4f5fSWarner Losh 194aedd6be5SKyle Evans local kernels = {} 195aedd6be5SKyle Evans local unique = {} 196aedd6be5SKyle Evans local i = 0 1979f71d421SKyle Evans if k ~= nil then 198aedd6be5SKyle Evans i = i + 1 199aedd6be5SKyle Evans kernels[i] = k 200aedd6be5SKyle Evans unique[k] = true 201088b4f5fSWarner Losh end 202088b4f5fSWarner Losh 2033f4eb56bSKyle Evans if v ~= nil then 204088b4f5fSWarner Losh for n in v:gmatch("([^; ]+)[; ]?") do 2059f71d421SKyle Evans if unique[n] == nil then 206aedd6be5SKyle Evans i = i + 1 207aedd6be5SKyle Evans kernels[i] = n 208aedd6be5SKyle Evans unique[n] = true 209088b4f5fSWarner Losh end 210088b4f5fSWarner Losh end 211*3889e6cdSKyle Evans end 212a108046fSConrad Meyer 213*3889e6cdSKyle Evans -- Base whether we autodetect kernels or not on a loader.conf(5) 214*3889e6cdSKyle Evans -- setting, kernels_autodetect. If it's set to 'yes', we'll add 215*3889e6cdSKyle Evans -- any kernels we detect based on the criteria described. 216*3889e6cdSKyle Evans if autodetect:lower() ~= "yes" then 2173f4eb56bSKyle Evans return kernels 2183f4eb56bSKyle Evans end 2193f4eb56bSKyle Evans 220a108046fSConrad Meyer -- Automatically detect other bootable kernel directories using a 221a108046fSConrad Meyer -- heuristic. Any directory in /boot that contains an ordinary file 222a108046fSConrad Meyer -- named "kernel" is considered eligible. 223a108046fSConrad Meyer for file in lfs.dir("/boot") do 224aedd6be5SKyle Evans local fname = "/boot/" .. file 225a108046fSConrad Meyer 2269f71d421SKyle Evans if file == "." or file == ".." then 227aedd6be5SKyle Evans goto continue 228a108046fSConrad Meyer end 229a108046fSConrad Meyer 2309f71d421SKyle Evans if lfs.attributes(fname, "mode") ~= "directory" then 231aedd6be5SKyle Evans goto continue 232a108046fSConrad Meyer end 233a108046fSConrad Meyer 2349f71d421SKyle Evans if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 235aedd6be5SKyle Evans goto continue 236a108046fSConrad Meyer end 237a108046fSConrad Meyer 2389f71d421SKyle Evans if unique[file] == nil then 239aedd6be5SKyle Evans i = i + 1 240aedd6be5SKyle Evans kernels[i] = file 241aedd6be5SKyle Evans unique[file] = true 242a108046fSConrad Meyer end 243a108046fSConrad Meyer 244a108046fSConrad Meyer ::continue:: 245a108046fSConrad Meyer end 246aedd6be5SKyle Evans return kernels 247088b4f5fSWarner Loshend 248088b4f5fSWarner Losh 2497efc058fSKyle Evansfunction core.bootenvDefault() 2507efc058fSKyle Evans return loader.getenv("zfs_be_active") 2517efc058fSKyle Evansend 2527efc058fSKyle Evans 2537efc058fSKyle Evansfunction core.bootenvList() 2547efc058fSKyle Evans local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 2557efc058fSKyle Evans local bootenvs = {} 2567efc058fSKyle Evans local curenv 2577efc058fSKyle Evans local curenv_idx = 0 2587efc058fSKyle Evans local envcount = 0 2597efc058fSKyle Evans local unique = {} 2607efc058fSKyle Evans 2617efc058fSKyle Evans if bootenv_count == nil or bootenv_count <= 0 then 2627efc058fSKyle Evans return bootenvs 2637efc058fSKyle Evans end 2647efc058fSKyle Evans 2657efc058fSKyle Evans -- Currently selected bootenv is always first/default 2667efc058fSKyle Evans curenv = core.bootenvDefault() 2677efc058fSKyle Evans if curenv ~= nil then 2687efc058fSKyle Evans envcount = envcount + 1 2697efc058fSKyle Evans bootenvs[envcount] = curenv 2707efc058fSKyle Evans unique[curenv] = true 2717efc058fSKyle Evans end 2727efc058fSKyle Evans 2737efc058fSKyle Evans for curenv_idx = 0, bootenv_count - 1 do 2747efc058fSKyle Evans curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 2757efc058fSKyle Evans if curenv ~= nil and unique[curenv] == nil then 2767efc058fSKyle Evans envcount = envcount + 1 2777efc058fSKyle Evans bootenvs[envcount] = curenv 2787efc058fSKyle Evans unique[curenv] = true 2797efc058fSKyle Evans end 2807efc058fSKyle Evans end 2817efc058fSKyle Evans return bootenvs 2827efc058fSKyle Evansend 2837efc058fSKyle Evans 284088b4f5fSWarner Loshfunction core.setDefaults() 285aedd6be5SKyle Evans core.setACPI(core.getACPIPresent(true)) 286aedd6be5SKyle Evans core.setSafeMode(false) 287aedd6be5SKyle Evans core.setSingleUser(false) 288aedd6be5SKyle Evans core.setVerbose(false) 289088b4f5fSWarner Loshend 290088b4f5fSWarner Losh 2916d4ed94dSKyle Evansfunction core.autoboot(argstr) 292aedd6be5SKyle Evans config.loadelf() 293aedd6be5SKyle Evans loader.perform(compose_loader_cmd("autoboot", argstr)) 294088b4f5fSWarner Loshend 295088b4f5fSWarner Losh 2966d4ed94dSKyle Evansfunction core.boot(argstr) 297aedd6be5SKyle Evans config.loadelf() 298aedd6be5SKyle Evans loader.perform(compose_loader_cmd("boot", argstr)) 299088b4f5fSWarner Loshend 300088b4f5fSWarner Losh 301e07fc39cSKyle Evansfunction core.isSingleUserBoot() 302aedd6be5SKyle Evans local single_user = loader.getenv("boot_single") 303aedd6be5SKyle Evans return single_user ~= nil and single_user:lower() == "yes" 304e07fc39cSKyle Evansend 305e07fc39cSKyle Evans 3067efc058fSKyle Evansfunction core.isZFSBoot() 3077efc058fSKyle Evans local c = loader.getenv("currdev") 3087efc058fSKyle Evans 3097efc058fSKyle Evans if c ~= nil then 3107efc058fSKyle Evans return c:match("^zfs:") ~= nil 3117efc058fSKyle Evans end 3127efc058fSKyle Evans return false 3137efc058fSKyle Evansend 3147efc058fSKyle Evans 315b140d14bSKyle Evansfunction core.isSerialBoot() 316aedd6be5SKyle Evans local c = loader.getenv("console") 317088b4f5fSWarner Losh 3189f71d421SKyle Evans if c ~= nil then 3199f71d421SKyle Evans if c:find("comconsole") ~= nil then 320aedd6be5SKyle Evans return true 321088b4f5fSWarner Losh end 322088b4f5fSWarner Losh end 323088b4f5fSWarner Losh 324aedd6be5SKyle Evans local s = loader.getenv("boot_serial") 3259f71d421SKyle Evans if s ~= nil then 326aedd6be5SKyle Evans return true 327088b4f5fSWarner Losh end 328088b4f5fSWarner Losh 329aedd6be5SKyle Evans local m = loader.getenv("boot_multicons") 3309f71d421SKyle Evans if m ~= nil then 331aedd6be5SKyle Evans return true 332088b4f5fSWarner Losh end 333aedd6be5SKyle Evans return false 334088b4f5fSWarner Loshend 335088b4f5fSWarner Losh 336c1ab36f5SKyle Evansfunction core.isSystem386() 3379f71d421SKyle Evans return loader.machine_arch == "i386" 338c1ab36f5SKyle Evansend 339c1ab36f5SKyle Evans 3405c1b5165SKyle Evans-- This may be a better candidate for a 'utility' module. 3415c1b5165SKyle Evansfunction core.shallowCopyTable(tbl) 342aedd6be5SKyle Evans local new_tbl = {} 3435c1b5165SKyle Evans for k, v in pairs(tbl) do 3449f71d421SKyle Evans if type(v) == "table" then 345aedd6be5SKyle Evans new_tbl[k] = core.shallowCopyTable(v) 3465c1b5165SKyle Evans else 347aedd6be5SKyle Evans new_tbl[k] = v 3485c1b5165SKyle Evans end 3495c1b5165SKyle Evans end 350aedd6be5SKyle Evans return new_tbl 3515c1b5165SKyle Evansend 3525c1b5165SKyle Evans 3536d4ed94dSKyle Evans-- XXX This should go away if we get the table lib into shape for importing. 3546d4ed94dSKyle Evans-- As of now, it requires some 'os' functions, so we'll implement this in lua 3556d4ed94dSKyle Evans-- for our uses 3566d4ed94dSKyle Evansfunction core.popFrontTable(tbl) 3576d4ed94dSKyle Evans -- Shouldn't reasonably happen 3589f71d421SKyle Evans if #tbl == 0 then 359aedd6be5SKyle Evans return nil, nil 3609f71d421SKyle Evans elseif #tbl == 1 then 361aedd6be5SKyle Evans return tbl[1], {} 3626d4ed94dSKyle Evans end 3636d4ed94dSKyle Evans 364aedd6be5SKyle Evans local first_value = tbl[1] 365aedd6be5SKyle Evans local new_tbl = {} 3666d4ed94dSKyle Evans -- This is not a cheap operation 3676d4ed94dSKyle Evans for k, v in ipairs(tbl) do 3689f71d421SKyle Evans if k > 1 then 369aedd6be5SKyle Evans new_tbl[k - 1] = v 3706d4ed94dSKyle Evans end 3716d4ed94dSKyle Evans end 3726d4ed94dSKyle Evans 373aedd6be5SKyle Evans return first_value, new_tbl 3746d4ed94dSKyle Evansend 3756d4ed94dSKyle Evans 3766f412147SKyle Evans-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 3776f412147SKyle Evans-- generally be set upon execution of the kernel. Because of this, we can't (or 3786f412147SKyle Evans-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 3796f412147SKyle Evans-- enabled if we detect it and leave well enough alone if we don't. 3809f71d421SKyle Evansif core.isSystem386() and core.getACPIPresent(false) then 381aedd6be5SKyle Evans core.setACPI(true) 3826f412147SKyle Evansend 383aedd6be5SKyle Evansreturn core 384