]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/core.lua
lualoader: Don't autodetect kernels if 'kernels' is explicitly set
[FreeBSD/FreeBSD.git] / stand / lua / core.lua
1 --
2 -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
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
29 local config = require('config')
30
31 local core = {}
32
33 local compose_loader_cmd = function(cmd_name, argstr)
34         if argstr ~= nil then
35                 cmd_name = cmd_name .. " " .. argstr
36         end
37         return cmd_name
38 end
39
40 -- Internal function
41 -- Parses arguments to boot and returns two values: kernel_name, argstr
42 -- Defaults to nil and "" respectively.
43 -- This will also parse arguments to autoboot, but the with_kernel argument
44 -- will need to be explicitly overwritten to false
45 local parse_boot_args = function(argv, with_kernel)
46         if with_kernel == nil then
47                 with_kernel = true
48         end
49         if #argv == 0 then
50                 if with_kernel then
51                         return nil, ""
52                 else
53                         return ""
54                 end
55         end
56         local kernel_name
57         local argstr = ""
58
59         for k, v in ipairs(argv) do
60                 if with_kernel and v:sub(1,1) ~= "-" then
61                         kernel_name = v
62                 else
63                         argstr = argstr .. " " .. v
64                 end
65         end
66         if with_kernel then
67                 return kernel_name, argstr
68         else
69                 return argstr
70         end
71 end
72
73 -- Globals
74 function boot(...)
75         local argv = {...}
76         local cmd_name = ""
77         cmd_name, argv = core.popFrontTable(argv)
78         local kernel, argstr = parse_boot_args(argv)
79         if kernel ~= nil then
80                 loader.perform("unload")
81                 config.selectkernel(kernel)
82         end
83         core.boot(argstr)
84 end
85
86 function autoboot(...)
87         local argv = {...}
88         local cmd_name = ""
89         cmd_name, argv = core.popFrontTable(argv)
90         local argstr = parse_boot_args(argv, false)
91         core.autoboot(argstr)
92 end
93
94 -- Module exports
95 -- Commonly appearing constants
96 core.KEY_BACKSPACE      = 8
97 core.KEY_ENTER          = 13
98 core.KEY_DELETE         = 127
99
100 core.KEYSTR_ESCAPE      = "\027"
101
102 core.MENU_RETURN        = "return"
103 core.MENU_ENTRY         = "entry"
104 core.MENU_SEPARATOR     = "separator"
105 core.MENU_SUBMENU       = "submenu"
106 core.MENU_CAROUSEL_ENTRY        = "carousel_entry"
107
108 function core.setVerbose(b)
109         if b == nil then
110                 b = not core.verbose
111         end
112
113         if b then
114                 loader.setenv("boot_verbose", "YES")
115         else
116                 loader.unsetenv("boot_verbose")
117         end
118         core.verbose = b
119 end
120
121 function core.setSingleUser(b)
122         if b == nil then
123                 b = not core.su
124         end
125
126         if b then
127                 loader.setenv("boot_single", "YES")
128         else
129                 loader.unsetenv("boot_single")
130         end
131         core.su = b
132 end
133
134 function core.getACPIPresent(checkingSystemDefaults)
135         local c = loader.getenv("hint.acpi.0.rsdp")
136
137         if c ~= nil then
138                 if checkingSystemDefaults then
139                         return true
140                 end
141                 -- Otherwise, respect disabled if it's set
142                 c = loader.getenv("hint.acpi.0.disabled")
143                 return c == nil or tonumber(c) ~= 1
144         end
145         return false
146 end
147
148 function core.setACPI(b)
149         if b == nil then
150                 b = not core.acpi
151         end
152
153         if b then
154                 loader.setenv("acpi_load", "YES")
155                 loader.setenv("hint.acpi.0.disabled", "0")
156                 loader.unsetenv("loader.acpi_disabled_by_user")
157         else
158                 loader.unsetenv("acpi_load")
159                 loader.setenv("hint.acpi.0.disabled", "1")
160                 loader.setenv("loader.acpi_disabled_by_user", "1")
161         end
162         core.acpi = b
163 end
164
165 function core.setSafeMode(b)
166         if b == nil then
167                 b = not core.sm
168         end
169         if b then
170                 loader.setenv("kern.smp.disabled", "1")
171                 loader.setenv("hw.ata.ata_dma", "0")
172                 loader.setenv("hw.ata.atapi_dma", "0")
173                 loader.setenv("hw.ata.wc", "0")
174                 loader.setenv("hw.eisa_slots", "0")
175                 loader.setenv("kern.eventtimer.periodic", "1")
176                 loader.setenv("kern.geom.part.check_integrity", "0")
177         else
178                 loader.unsetenv("kern.smp.disabled")
179                 loader.unsetenv("hw.ata.ata_dma")
180                 loader.unsetenv("hw.ata.atapi_dma")
181                 loader.unsetenv("hw.ata.wc")
182                 loader.unsetenv("hw.eisa_slots")
183                 loader.unsetenv("kern.eventtimer.periodic")
184                 loader.unsetenv("kern.geom.part.check_integrity")
185         end
186         core.sm = b
187 end
188
189 function core.kernelList()
190         local k = loader.getenv("kernel")
191         local v = loader.getenv("kernels")
192
193         local kernels = {}
194         local unique = {}
195         local i = 0
196         if k ~= nil then
197                 i = i + 1
198                 kernels[i] = k
199                 unique[k] = true
200         end
201
202         if v ~= nil then
203                 for n in v:gmatch("([^; ]+)[; ]?") do
204                         if unique[n] == nil then
205                                 i = i + 1
206                                 kernels[i] = n
207                                 unique[n] = true
208                         end
209                 end
210
211                 -- We will not automatically detect kernels to be displayed if
212                 -- loader.conf(5) explicitly set 'kernels'.
213                 return kernels
214         end
215
216         -- Automatically detect other bootable kernel directories using a
217         -- heuristic.  Any directory in /boot that contains an ordinary file
218         -- named "kernel" is considered eligible.
219         for file in lfs.dir("/boot") do
220                 local fname = "/boot/" .. file
221
222                 if file == "." or file == ".." then
223                         goto continue
224                 end
225
226                 if lfs.attributes(fname, "mode") ~= "directory" then
227                         goto continue
228                 end
229
230                 if lfs.attributes(fname .. "/kernel", "mode") ~= "file" then
231                         goto continue
232                 end
233
234                 if unique[file] == nil then
235                         i = i + 1
236                         kernels[i] = file
237                         unique[file] = true
238                 end
239
240                 ::continue::
241         end
242         return kernels
243 end
244
245 function core.setDefaults()
246         core.setACPI(core.getACPIPresent(true))
247         core.setSafeMode(false)
248         core.setSingleUser(false)
249         core.setVerbose(false)
250 end
251
252 function core.autoboot(argstr)
253         config.loadelf()
254         loader.perform(compose_loader_cmd("autoboot", argstr))
255 end
256
257 function core.boot(argstr)
258         config.loadelf()
259         loader.perform(compose_loader_cmd("boot", argstr))
260 end
261
262 function core.isSingleUserBoot()
263         local single_user = loader.getenv("boot_single")
264         return single_user ~= nil and single_user:lower() == "yes"
265 end
266
267 function core.isSerialBoot()
268         local c = loader.getenv("console")
269
270         if c ~= nil then
271                 if c:find("comconsole") ~= nil then
272                         return true
273                 end
274         end
275
276         local s = loader.getenv("boot_serial")
277         if s ~= nil then
278                 return true
279         end
280
281         local m = loader.getenv("boot_multicons")
282         if m ~= nil then
283                 return true
284         end
285         return false
286 end
287
288 function core.isSystem386()
289         return loader.machine_arch == "i386"
290 end
291
292 -- This may be a better candidate for a 'utility' module.
293 function core.shallowCopyTable(tbl)
294         local new_tbl = {}
295         for k, v in pairs(tbl) do
296                 if type(v) == "table" then
297                         new_tbl[k] = core.shallowCopyTable(v)
298                 else
299                         new_tbl[k] = v
300                 end
301         end
302         return new_tbl
303 end
304
305 -- XXX This should go away if we get the table lib into shape for importing.
306 -- As of now, it requires some 'os' functions, so we'll implement this in lua
307 -- for our uses
308 function core.popFrontTable(tbl)
309         -- Shouldn't reasonably happen
310         if #tbl == 0 then
311                 return nil, nil
312         elseif #tbl == 1 then
313                 return tbl[1], {}
314         end
315
316         local first_value = tbl[1]
317         local new_tbl = {}
318         -- This is not a cheap operation
319         for k, v in ipairs(tbl) do
320                 if k > 1 then
321                         new_tbl[k - 1] = v
322                 end
323         end
324
325         return first_value, new_tbl
326 end
327
328 -- On i386, hint.acpi.0.rsdp will be set before we're loaded. On !i386, it will
329 -- generally be set upon execution of the kernel. Because of this, we can't (or
330 -- don't really want to) detect/disable ACPI on !i386 reliably. Just set it
331 -- enabled if we detect it and leave well enough alone if we don't.
332 if core.isSystem386() and core.getACPIPresent(false) then
333         core.setACPI(true)
334 end
335 return core