1-- 2-- Copyright (c) 2015 Pedro Souza <[email protected]> 3-- All rights reserved. 4-- 5-- Redistribution and use in source and binary forms, with or without 6-- modification, are permitted provided that the following conditions 7-- are met: 8-- 1. Redistributions of source code must retain the above copyright 9-- notice, this list of conditions and the following disclaimer. 10-- 2. Redistributions in binary form must reproduce the above copyright 11-- notice, this list of conditions and the following disclaimer in the 12-- documentation and/or other materials provided with the distribution. 13-- 14-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24-- SUCH DAMAGE. 25-- 26-- $FreeBSD$ 27-- 28 29local config = require("config") 30 31local core = {} 32 33local compose_loader_cmd = function(cmd_name, argstr) 34 if argstr ~= nil then 35 cmd_name = cmd_name .. " " .. argstr 36 end 37 return cmd_name 38end 39 40-- Module exports 41-- Commonly appearing constants 42core.KEY_BACKSPACE = 8 43core.KEY_ENTER = 13 44core.KEY_DELETE = 127 45 46core.KEYSTR_ESCAPE = "\027" 47 48core.MENU_RETURN = "return" 49core.MENU_ENTRY = "entry" 50core.MENU_SEPARATOR = "separator" 51core.MENU_SUBMENU = "submenu" 52core.MENU_CAROUSEL_ENTRY = "carousel_entry" 53 54function core.setVerbose(b) 55 if b == nil then 56 b = not core.verbose 57 end 58 59 if b then 60 loader.setenv("boot_verbose", "YES") 61 else 62 loader.unsetenv("boot_verbose") 63 end 64 core.verbose = b 65end 66 67function core.setSingleUser(b) 68 if b == nil then 69 b = not core.su 70 end 71 72 if b then 73 loader.setenv("boot_single", "YES") 74 else 75 loader.unsetenv("boot_single") 76 end 77 core.su = b 78end 79 80function core.getACPIPresent(checkingSystemDefaults) 81 local c = loader.getenv("hint.acpi.0.rsdp") 82 83 if c ~= nil then 84 if checkingSystemDefaults then 85 return true 86 end 87 -- Otherwise, respect disabled if it's set 88 c = loader.getenv("hint.acpi.0.disabled") 89 return c == nil or tonumber(c) ~= 1 90 end 91 return false 92end 93 94function core.setACPI(b) 95 if b == nil then 96 b = not core.acpi 97 end 98 99 if b then 100 loader.setenv("acpi_load", "YES") 101 loader.setenv("hint.acpi.0.disabled", "0") 102 loader.unsetenv("loader.acpi_disabled_by_user") 103 else 104 loader.unsetenv("acpi_load") 105 loader.setenv("hint.acpi.0.disabled", "1") 106 loader.setenv("loader.acpi_disabled_by_user", "1") 107 end 108 core.acpi = b 109end 110 111function core.setSafeMode(b) 112 if b == nil then 113 b = not core.sm 114 end 115 if b then 116 loader.setenv("kern.smp.disabled", "1") 117 loader.setenv("hw.ata.ata_dma", "0") 118 loader.setenv("hw.ata.atapi_dma", "0") 119 loader.setenv("hw.ata.wc", "0") 120 loader.setenv("hw.eisa_slots", "0") 121 loader.setenv("kern.eventtimer.periodic", "1") 122 loader.setenv("kern.geom.part.check_integrity", "0") 123 else 124 loader.unsetenv("kern.smp.disabled") 125 loader.unsetenv("hw.ata.ata_dma") 126 loader.unsetenv("hw.ata.atapi_dma") 127 loader.unsetenv("hw.ata.wc") 128 loader.unsetenv("hw.eisa_slots") 129 loader.unsetenv("kern.eventtimer.periodic") 130 loader.unsetenv("kern.geom.part.check_integrity") 131 end 132 core.sm = b 133end 134 135function core.kernelList() 136 local k = loader.getenv("kernel") 137 local v = loader.getenv("kernels") 138 local autodetect = loader.getenv("kernels_autodetect") or "" 139 140 local kernels = {} 141 local unique = {} 142 local i = 0 143 if k ~= nil then 144 i = i + 1 145 kernels[i] = k 146 unique[k] = true 147 end 148 149 if v ~= nil then 150 for n in v:gmatch("([^; ]+)[; ]?") do 151 if unique[n] == nil then 152 i = i + 1 153 kernels[i] = n 154 unique[n] = true 155 end 156 end 157 end 158 159 -- Base whether we autodetect kernels or not on a loader.conf(5) 160 -- setting, kernels_autodetect. If it's set to 'yes', we'll add 161 -- any kernels we detect based on the criteria described. 162 if autodetect:lower() ~= "yes" then 163 return kernels 164 end 165 166 -- Automatically detect other bootable kernel directories using a 167 -- heuristic. Any directory in /boot that contains an ordinary file 168 -- named "kernel" is considered eligible. 169 for file in lfs.dir("/boot") do 170 local fname = "/boot/" .. file 171 172 if file == "." or file == ".." then 173 goto continue 174 end 175 176 if lfs.attributes(fname, "mode") ~= "directory" then 177 goto continue 178 end 179 180 if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then 181 goto continue 182 end 183 184 if unique[file] == nil then 185 i = i + 1 186 kernels[i] = file 187 unique[file] = true 188 end 189 190 ::continue:: 191 end 192 return kernels 193end 194 195function core.bootenvDefault() 196 return loader.getenv("zfs_be_active") 197end 198 199function core.bootenvList() 200 local bootenv_count = tonumber(loader.getenv("bootenvs_count")) 201 local bootenvs = {} 202 local curenv 203 local curenv_idx = 0 204 local envcount = 0 205 local unique = {} 206 207 if bootenv_count == nil or bootenv_count <= 0 then 208 return bootenvs 209 end 210 211 -- Currently selected bootenv is always first/default 212 curenv = core.bootenvDefault() 213 if curenv ~= nil then 214 envcount = envcount + 1 215 bootenvs[envcount] = curenv 216 unique[curenv] = true 217 end 218 219 for curenv_idx = 0, bootenv_count - 1 do 220 curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]") 221 if curenv ~= nil and unique[curenv] == nil then 222 envcount = envcount + 1 223 bootenvs[envcount] = curenv 224 unique[curenv] = true 225 end 226 end 227 return bootenvs 228end 229 230function core.setDefaults() 231 core.setACPI(core.getACPIPresent(true)) 232 core.setSafeMode(false) 233 core.setSingleUser(false) 234 core.setVerbose(false) 235end 236 237function core.autoboot(argstr) 238 config.loadelf() 239 loader.perform(compose_loader_cmd("autoboot", argstr)) 240end 241 242function core.boot(argstr) 243 config.loadelf() 244 loader.perform(compose_loader_cmd("boot", argstr)) 245end 246 247function core.isSingleUserBoot() 248 local single_user = loader.getenv("boot_single") 249 return single_user ~= nil and single_user:lower() == "yes" 250end 251 252function core.isZFSBoot() 253 local c = loader.getenv("currdev") 254 255 if c ~= nil then 256 return c:match("^zfs:") ~= nil 257 end 258 return false 259end 260 261function core.isSerialBoot() 262 local c = loader.getenv("console") 263 264 if c ~= nil then 265 if c:find("comconsole") ~= nil then 266 return true 267 end 268 end 269 270 local s = loader.getenv("boot_serial") 271 if s ~= nil then 272 return true 273 end 274 275 local m = loader.getenv("boot_multicons") 276 if m ~= nil then 277 return true 278 end 279 return false 280end 281 282function core.isSystem386() 283 return loader.machine_arch == "i386" 284end 285 286-- This may be a better candidate for a 'utility' module. 287function core.shallowCopyTable(tbl) 288 local new_tbl = {} 289 for k, v in pairs(tbl) do 290 if type(v) == "table" then 291 new_tbl[k] = core.shallowCopyTable(v) 292 else 293 new_tbl[k] = v 294 end 295 end 296 return new_tbl 297end 298 299-- XXX This should go away if we get the table lib into shape for importing. 300-- As of now, it requires some 'os' functions, so we'll implement this in lua 301-- for our uses 302function core.popFrontTable(tbl) 303 -- Shouldn't reasonably happen 304 if #tbl == 0 then 305 return nil, nil 306 elseif #tbl == 1 then 307 return tbl[1], {} 308 end 309 310 local first_value = tbl[1] 311 local new_tbl = {} 312 -- This is not a cheap operation 313 for k, v in ipairs(tbl) do 314 if k > 1 then 315 new_tbl[k - 1] = v 316 end 317 end 318 319 return first_value, new_tbl 320end 321 322-- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will 323-- generally be set upon execution of the kernel. Because of this, we can't (or 324-- don't really want to) detect/disable ACPI on !i386 reliably. Just set it 325-- enabled if we detect it and leave well enough alone if we don't. 326if core.isSystem386() and core.getACPIPresent(false) then 327 core.setACPI(true) 328end 329return core 330