]>
Commit | Line | Data |
---|---|---|
d38e24c2 | 1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
cef17233 | 3 | * Copyright (C) 2008, 2009 Free Software Foundation, Inc. |
d38e24c2 | 4 | * |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/loader.h> | |
20 | #include <grub/cpu/loader.h> | |
21 | #include <grub/cpu/bsd.h> | |
22 | #include <grub/machine/init.h> | |
23 | #include <grub/machine/memory.h> | |
cef17233 | 24 | #include <grub/memory.h> |
25 | #include <grub/machine/machine.h> | |
d38e24c2 | 26 | #include <grub/file.h> |
27 | #include <grub/err.h> | |
d38e24c2 | 28 | #include <grub/dl.h> |
29 | #include <grub/mm.h> | |
30 | #include <grub/elfload.h> | |
31 | #include <grub/env.h> | |
32 | #include <grub/misc.h> | |
33 | #include <grub/gzio.h> | |
34 | #include <grub/aout.h> | |
b1b797cb | 35 | #include <grub/command.h> |
c11fded5 | 36 | #include <grub/extcmd.h> |
37 | ||
63963d17 | 38 | #ifdef GRUB_MACHINE_PCBIOS |
39 | #include <grub/machine/biosnum.h> | |
40 | #include <grub/disk.h> | |
41 | #include <grub/device.h> | |
42 | #include <grub/partition.h> | |
43 | #endif | |
d38e24c2 | 44 | |
45 | #define ALIGN_DWORD(a) ALIGN_UP (a, 4) | |
cef17233 | 46 | #define ALIGN_QWORD(a) ALIGN_UP (a, 8) |
47 | #define ALIGN_VAR(a) ((is_64bit) ? (ALIGN_QWORD(a)) : (ALIGN_DWORD(a))) | |
d38e24c2 | 48 | #define ALIGN_PAGE(a) ALIGN_UP (a, 4096) |
49 | ||
50 | #define MOD_BUF_ALLOC_UNIT 4096 | |
51 | ||
81623db6 | 52 | static int kernel_type = KERNEL_TYPE_NONE; |
d38e24c2 | 53 | static grub_dl_t my_mod; |
cef17233 | 54 | static grub_addr_t entry, entry_hi, kern_start, kern_end; |
d38e24c2 | 55 | static grub_uint32_t bootflags; |
56 | static char *mod_buf; | |
cef17233 | 57 | static grub_uint32_t mod_buf_len, mod_buf_max, kern_end_mdofs; |
58 | static int is_elf_kernel, is_64bit; | |
d38e24c2 | 59 | |
c11fded5 | 60 | static const struct grub_arg_option freebsd_opts[] = |
61 | { | |
62 | {"dual", 'D', 0, "Display output on all consoles.", 0, 0}, | |
63 | {"serial", 'h', 0, "Use serial console.", 0, 0}, | |
64 | {"askname", 'a', 0, "Ask for file name to reboot from.", 0, 0}, | |
65 | {"cdrom", 'C', 0, "Use cdrom as root.", 0, 0}, | |
66 | {"config", 'c', 0, "Invoke user configuration routing.", 0, 0}, | |
67 | {"kdb", 'd', 0, "Enter in KDB on boot.", 0, 0}, | |
68 | {"gdb", 'g', 0, "Use GDB remote debugger instead of DDB.", 0, 0}, | |
69 | {"mute", 'm', 0, "Disable all boot output.", 0, 0}, | |
70 | {"nointr", 'n', 0, "", 0, 0}, | |
71 | {"pause", 'p', 0, "Wait for keypress after every line of output.", 0, 0}, | |
72 | {"quiet", 'q', 0, "", 0, 0}, | |
73 | {"dfltroot", 'r', 0, "Use compiled-in rootdev.", 0, 0}, | |
74 | {"single", 's', 0, "Boot into single mode.", 0, 0}, | |
75 | {"verbose", 'v', 0, "Boot with verbose messages.", 0, 0}, | |
76 | {0, 0, 0, 0, 0, 0} | |
77 | }; | |
78 | ||
d38e24c2 | 79 | static const grub_uint32_t freebsd_flags[] = |
80 | { | |
81 | FREEBSD_RB_DUAL, FREEBSD_RB_SERIAL, FREEBSD_RB_ASKNAME, | |
82 | FREEBSD_RB_CDROM, FREEBSD_RB_CONFIG, FREEBSD_RB_KDB, | |
83 | FREEBSD_RB_GDB, FREEBSD_RB_MUTE, FREEBSD_RB_NOINTR, | |
84 | FREEBSD_RB_PAUSE, FREEBSD_RB_QUIET, FREEBSD_RB_DFLTROOT, | |
c11fded5 | 85 | FREEBSD_RB_SINGLE, FREEBSD_RB_VERBOSE, 0 |
d38e24c2 | 86 | }; |
87 | ||
c11fded5 | 88 | static const struct grub_arg_option openbsd_opts[] = |
89 | { | |
90 | {"askname", 'a', 0, "Ask for file name to reboot from.", 0, 0}, | |
91 | {"halt", 'b', 0, "Don't reboot, just halt.", 0, 0}, | |
92 | {"config", 'c', 0, "Change configured devices.", 0, 0}, | |
93 | {"single", 's', 0, "Boot into single mode.", 0, 0}, | |
94 | {"kdb", 'd', 0, "Enter in KDB on boot.", 0, 0}, | |
95 | {0, 0, 0, 0, 0, 0} | |
96 | }; | |
97 | ||
d38e24c2 | 98 | static const grub_uint32_t openbsd_flags[] = |
99 | { | |
100 | OPENBSD_RB_ASKNAME, OPENBSD_RB_HALT, OPENBSD_RB_CONFIG, | |
c11fded5 | 101 | OPENBSD_RB_SINGLE, OPENBSD_RB_KDB, 0 |
d38e24c2 | 102 | }; |
103 | ||
c11fded5 | 104 | static const struct grub_arg_option netbsd_opts[] = |
105 | { | |
f5ae9f74 | 106 | {"no-smp", '1', 0, "Disable SMP.", 0, 0}, |
107 | {"no-acpi", '2', 0, "Disable ACPI.", 0, 0}, | |
c11fded5 | 108 | {"askname", 'a', 0, "Ask for file name to reboot from.", 0, 0}, |
109 | {"halt", 'b', 0, "Don't reboot, just halt.", 0, 0}, | |
110 | {"config", 'c', 0, "Change configured devices.", 0, 0}, | |
111 | {"kdb", 'd', 0, "Enter in KDB on boot.", 0, 0}, | |
112 | {"miniroot", 'm', 0, "", 0, 0}, | |
113 | {"quiet", 'q', 0, "Don't display boot diagnostic messages.", 0, 0}, | |
114 | {"single", 's', 0, "Boot into single mode.", 0, 0}, | |
115 | {"verbose", 'v', 0, "Boot with verbose messages.", 0, 0}, | |
116 | {"debug", 'x', 0, "Boot with debug messages.", 0, 0}, | |
117 | {"silent", 'z', 0, "Supress normal output (warnings remain).", 0, 0}, | |
118 | {0, 0, 0, 0, 0, 0} | |
119 | }; | |
120 | ||
d38e24c2 | 121 | static const grub_uint32_t netbsd_flags[] = |
122 | { | |
f5ae9f74 | 123 | NETBSD_AB_NOSMP, NETBSD_AB_NOACPI, NETBSD_RB_ASKNAME, |
124 | NETBSD_RB_HALT, NETBSD_RB_USERCONFIG, NETBSD_RB_KDB, | |
125 | NETBSD_RB_MINIROOT, NETBSD_AB_QUIET, NETBSD_RB_SINGLE, | |
126 | NETBSD_AB_VERBOSE, NETBSD_AB_DEBUG, NETBSD_AB_SILENT, 0 | |
d38e24c2 | 127 | }; |
128 | ||
129 | static void | |
130 | grub_bsd_get_device (grub_uint32_t * biosdev, | |
131 | grub_uint32_t * unit, | |
132 | grub_uint32_t * slice, grub_uint32_t * part) | |
133 | { | |
134 | char *p; | |
63963d17 | 135 | grub_device_t dev; |
136 | ||
137 | *biosdev = grub_get_root_biosnumber () & 0xff; | |
138 | *unit = (*biosdev & 0x7f); | |
139 | *slice = 0xff; | |
140 | *part = 0xff; | |
141 | dev = grub_device_open (0); | |
142 | if (dev && dev->disk && dev->disk->partition) | |
d38e24c2 | 143 | { |
d38e24c2 | 144 | |
63963d17 | 145 | p = dev->disk->partition->partmap->get_name (dev->disk->partition); |
146 | if (p) | |
d38e24c2 | 147 | { |
63963d17 | 148 | if ((p[0] >= '0') && (p[0] <= '9')) |
d38e24c2 | 149 | { |
63963d17 | 150 | *slice = grub_strtoul (p, &p, 0); |
d38e24c2 | 151 | |
152 | if ((p) && (p[0] == ',')) | |
153 | p++; | |
154 | } | |
155 | ||
156 | if ((p[0] >= 'a') && (p[0] <= 'z')) | |
157 | *part = p[0] - 'a'; | |
158 | } | |
159 | } | |
63963d17 | 160 | if (dev) |
161 | grub_device_close (dev); | |
d38e24c2 | 162 | } |
163 | ||
3f3ec72b | 164 | grub_err_t |
d38e24c2 | 165 | grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len) |
166 | { | |
167 | if (mod_buf_max < mod_buf_len + len + 8) | |
168 | { | |
169 | char *new_buf; | |
170 | ||
171 | do | |
172 | { | |
173 | mod_buf_max += MOD_BUF_ALLOC_UNIT; | |
174 | } | |
175 | while (mod_buf_max < mod_buf_len + len + 8); | |
176 | ||
177 | new_buf = grub_malloc (mod_buf_max); | |
178 | if (!new_buf) | |
179 | return grub_errno; | |
180 | ||
181 | grub_memcpy (new_buf, mod_buf, mod_buf_len); | |
182 | grub_free (mod_buf); | |
183 | ||
184 | mod_buf = new_buf; | |
185 | } | |
186 | ||
187 | *((grub_uint32_t *) (mod_buf + mod_buf_len)) = type; | |
188 | *((grub_uint32_t *) (mod_buf + mod_buf_len + 4)) = len; | |
189 | mod_buf_len += 8; | |
190 | ||
191 | if (len) | |
192 | grub_memcpy (mod_buf + mod_buf_len, data, len); | |
193 | ||
cef17233 | 194 | mod_buf_len = ALIGN_VAR (mod_buf_len + len); |
d38e24c2 | 195 | |
196 | return GRUB_ERR_NONE; | |
197 | } | |
198 | ||
cef17233 | 199 | struct grub_e820_mmap |
200 | { | |
201 | grub_uint64_t addr; | |
202 | grub_uint64_t size; | |
203 | grub_uint32_t type; | |
204 | } __attribute__((packed)); | |
205 | #define GRUB_E820_RAM 1 | |
206 | #define GRUB_E820_RESERVED 2 | |
207 | #define GRUB_E820_ACPI 3 | |
208 | #define GRUB_E820_NVS 4 | |
209 | #define GRUB_E820_EXEC_CODE 5 | |
210 | ||
211 | static grub_err_t | |
212 | grub_freebsd_add_mmap (void) | |
213 | { | |
214 | grub_size_t len = 0; | |
215 | struct grub_e820_mmap *mmap_buf = 0; | |
216 | struct grub_e820_mmap *mmap = 0; | |
217 | int isfirstrun = 1; | |
218 | ||
219 | auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); | |
220 | int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, | |
221 | grub_uint32_t type) | |
222 | { | |
b39f9d20 | 223 | /* FreeBSD assumes that first 64KiB are available. |
cef17233 | 224 | Not always true but try to prevent panic somehow. */ |
225 | if (isfirstrun && addr != 0) | |
226 | { | |
227 | if (mmap) | |
228 | { | |
229 | mmap->addr = 0; | |
230 | mmap->size = (addr < 0x10000) ? addr : 0x10000; | |
231 | mmap->type = GRUB_E820_RAM; | |
232 | mmap++; | |
233 | } | |
234 | else | |
b39f9d20 | 235 | len += sizeof (struct grub_e820_mmap); |
cef17233 | 236 | } |
237 | isfirstrun = 0; | |
238 | if (mmap) | |
239 | { | |
240 | mmap->addr = addr; | |
241 | mmap->size = size; | |
242 | switch (type) | |
243 | { | |
244 | case GRUB_MACHINE_MEMORY_AVAILABLE: | |
245 | mmap->type = GRUB_E820_RAM; | |
246 | break; | |
247 | ||
248 | #ifdef GRUB_MACHINE_MEMORY_ACPI | |
249 | case GRUB_MACHINE_MEMORY_ACPI: | |
250 | mmap->type = GRUB_E820_ACPI; | |
251 | break; | |
252 | #endif | |
253 | ||
254 | #ifdef GRUB_MACHINE_MEMORY_NVS | |
255 | case GRUB_MACHINE_MEMORY_NVS: | |
256 | mmap->type = GRUB_E820_NVS; | |
257 | break; | |
258 | #endif | |
259 | ||
260 | default: | |
261 | #ifdef GRUB_MACHINE_MEMORY_CODE | |
262 | case GRUB_MACHINE_MEMORY_CODE: | |
263 | #endif | |
264 | #ifdef GRUB_MACHINE_MEMORY_RESERVED | |
265 | case GRUB_MACHINE_MEMORY_RESERVED: | |
266 | #endif | |
267 | mmap->type = GRUB_E820_RESERVED; | |
268 | break; | |
269 | } | |
270 | ||
271 | /* Merge regions if possible. */ | |
272 | if (mmap != mmap_buf && mmap->type == mmap[-1].type && | |
273 | mmap->addr == mmap[-1].addr + mmap[-1].size) | |
274 | mmap[-1].size += mmap->size; | |
275 | else | |
276 | mmap++; | |
277 | } | |
278 | else | |
279 | len += sizeof (struct grub_e820_mmap); | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | grub_mmap_iterate (hook); | |
285 | mmap_buf = mmap = grub_malloc (len); | |
286 | if (! mmap) | |
287 | return grub_errno; | |
288 | ||
289 | isfirstrun = 1; | |
290 | grub_mmap_iterate (hook); | |
291 | ||
292 | len = (mmap - mmap_buf) * sizeof (struct grub_e820_mmap); | |
293 | int i; | |
294 | for (i = 0; i < mmap - mmap_buf; i++) | |
295 | grub_dprintf ("bsd", "smap %d, %d:%llx - %llx\n", i, | |
296 | mmap_buf[i].type, | |
b39f9d20 | 297 | (unsigned long long) mmap_buf[i].addr, |
cef17233 | 298 | (unsigned long long) mmap_buf[i].size); |
b39f9d20 | 299 | |
cef17233 | 300 | grub_dprintf ("bsd", "%d entries in smap\n", mmap - mmap_buf); |
301 | grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | | |
302 | FREEBSD_MODINFOMD_SMAP, mmap_buf, len); | |
303 | ||
304 | grub_free (mmap_buf); | |
305 | ||
306 | return grub_errno; | |
307 | } | |
308 | ||
3f3ec72b | 309 | grub_err_t |
310 | grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv, | |
d38e24c2 | 311 | grub_addr_t addr, grub_uint32_t size) |
312 | { | |
3f3ec72b | 313 | char *name; |
314 | name = grub_strrchr (filename, '/'); | |
d38e24c2 | 315 | if (name) |
316 | name++; | |
317 | else | |
3f3ec72b | 318 | name = filename; |
59ade63d | 319 | if (grub_strcmp (type, "/boot/zfs/zpool.cache") == 0) |
320 | name = "/boot/zfs/zpool.cache"; | |
d38e24c2 | 321 | |
322 | if (grub_freebsd_add_meta (FREEBSD_MODINFO_NAME, name, | |
323 | grub_strlen (name) + 1)) | |
324 | return grub_errno; | |
325 | ||
cef17233 | 326 | if (is_64bit) |
327 | { | |
b39f9d20 | 328 | grub_uint64_t addr64 = addr, size64 = size; |
cef17233 | 329 | if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type, |
d38e24c2 | 330 | grub_strlen (type) + 1)) || |
b39f9d20 | 331 | (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, |
cef17233 | 332 | sizeof (addr64))) || |
b39f9d20 | 333 | (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, |
cef17233 | 334 | sizeof (size64)))) |
335 | return grub_errno; | |
336 | } | |
337 | else | |
338 | { | |
339 | if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type, | |
340 | grub_strlen (type) + 1)) || | |
b39f9d20 | 341 | (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, |
cef17233 | 342 | sizeof (addr))) || |
b39f9d20 | 343 | (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size, |
cef17233 | 344 | sizeof (size)))) |
345 | return grub_errno; | |
346 | } | |
d38e24c2 | 347 | |
348 | if (argc) | |
349 | { | |
350 | int i, n; | |
351 | ||
352 | n = 0; | |
353 | for (i = 0; i < argc; i++) | |
354 | { | |
355 | n += grub_strlen (argv[i]) + 1; | |
356 | } | |
357 | ||
358 | if (n) | |
359 | { | |
360 | char cmdline[n], *p; | |
361 | ||
362 | p = cmdline; | |
363 | for (i = 0; i < argc; i++) | |
364 | { | |
365 | grub_strcpy (p, argv[i]); | |
366 | p += grub_strlen (argv[i]); | |
367 | *(p++) = ' '; | |
368 | } | |
369 | *p = 0; | |
370 | ||
371 | if (grub_freebsd_add_meta (FREEBSD_MODINFO_ARGS, cmdline, n)) | |
372 | return grub_errno; | |
373 | } | |
374 | } | |
375 | ||
376 | return GRUB_ERR_NONE; | |
377 | } | |
378 | ||
379 | static void | |
380 | grub_freebsd_list_modules (void) | |
381 | { | |
382 | grub_uint32_t pos = 0; | |
383 | ||
384 | grub_printf (" %-18s %-18s%14s%14s\n", "name", "type", "addr", "size"); | |
385 | while (pos < mod_buf_len) | |
386 | { | |
387 | grub_uint32_t type, size; | |
388 | ||
389 | type = *((grub_uint32_t *) (mod_buf + pos)); | |
390 | size = *((grub_uint32_t *) (mod_buf + pos + 4)); | |
391 | pos += 8; | |
392 | switch (type) | |
393 | { | |
394 | case FREEBSD_MODINFO_NAME: | |
395 | case FREEBSD_MODINFO_TYPE: | |
396 | grub_printf (" %-18s", mod_buf + pos); | |
397 | break; | |
398 | case FREEBSD_MODINFO_ADDR: | |
399 | { | |
400 | grub_addr_t addr; | |
401 | ||
402 | addr = *((grub_addr_t *) (mod_buf + pos)); | |
403 | grub_printf (" 0x%08x", addr); | |
404 | break; | |
405 | } | |
406 | case FREEBSD_MODINFO_SIZE: | |
407 | { | |
408 | grub_uint32_t len; | |
409 | ||
410 | len = *((grub_uint32_t *) (mod_buf + pos)); | |
411 | grub_printf (" 0x%08x\n", len); | |
412 | } | |
413 | } | |
414 | ||
cef17233 | 415 | pos = ALIGN_VAR (pos + size); |
d38e24c2 | 416 | } |
417 | } | |
418 | ||
4241d2b1 | 419 | /* This function would be here but it's under different license. */ |
cef17233 | 420 | #include "bsd_pagetable.c" |
421 | ||
422 | struct gdt_descriptor | |
423 | { | |
424 | grub_uint16_t limit; | |
425 | void *base; | |
426 | } __attribute__ ((packed)); | |
427 | ||
d38e24c2 | 428 | static grub_err_t |
429 | grub_freebsd_boot (void) | |
430 | { | |
431 | struct grub_freebsd_bootinfo bi; | |
432 | char *p; | |
433 | grub_uint32_t bootdev, biosdev, unit, slice, part; | |
434 | ||
435 | auto int iterate_env (struct grub_env_var *var); | |
436 | int iterate_env (struct grub_env_var *var) | |
437 | { | |
438 | if ((!grub_memcmp (var->name, "FreeBSD.", 8)) && (var->name[8])) | |
439 | { | |
440 | grub_strcpy (p, &var->name[8]); | |
441 | p += grub_strlen (p); | |
442 | *(p++) = '='; | |
443 | grub_strcpy (p, var->value); | |
444 | p += grub_strlen (p) + 1; | |
445 | } | |
446 | ||
447 | return 0; | |
448 | } | |
449 | ||
450 | grub_memset (&bi, 0, sizeof (bi)); | |
451 | bi.bi_version = FREEBSD_BOOTINFO_VERSION; | |
452 | bi.bi_size = sizeof (bi); | |
453 | ||
454 | grub_bsd_get_device (&biosdev, &unit, &slice, &part); | |
455 | bootdev = (FREEBSD_B_DEVMAGIC + ((slice + 1) << FREEBSD_B_SLICESHIFT) + | |
456 | (unit << FREEBSD_B_UNITSHIFT) + (part << FREEBSD_B_PARTSHIFT)); | |
457 | ||
458 | bi.bi_bios_dev = biosdev; | |
459 | ||
460 | p = (char *) kern_end; | |
461 | ||
462 | grub_env_iterate (iterate_env); | |
463 | ||
464 | if (p != (char *) kern_end) | |
465 | { | |
466 | *(p++) = 0; | |
467 | ||
468 | bi.bi_envp = kern_end; | |
469 | kern_end = ALIGN_PAGE ((grub_uint32_t) p); | |
470 | } | |
471 | ||
472 | if (is_elf_kernel) | |
473 | { | |
cef17233 | 474 | grub_addr_t md_ofs; |
475 | int ofs; | |
476 | ||
d38e24c2 | 477 | if (grub_freebsd_add_meta (FREEBSD_MODINFO_END, 0, 0)) |
478 | return grub_errno; | |
479 | ||
480 | grub_memcpy ((char *) kern_end, mod_buf, mod_buf_len); | |
481 | bi.bi_modulep = kern_end; | |
482 | ||
483 | kern_end = ALIGN_PAGE (kern_end + mod_buf_len); | |
cef17233 | 484 | |
485 | if (is_64bit) | |
486 | kern_end += 4096 * 4; | |
487 | ||
488 | md_ofs = bi.bi_modulep + kern_end_mdofs; | |
489 | ofs = (is_64bit) ? 16 : 12; | |
490 | *((grub_uint32_t *) md_ofs) = kern_end; | |
491 | md_ofs -= ofs; | |
492 | *((grub_uint32_t *) md_ofs) = bi.bi_envp; | |
493 | md_ofs -= ofs; | |
494 | *((grub_uint32_t *) md_ofs) = bootflags; | |
d38e24c2 | 495 | } |
496 | ||
497 | bi.bi_kernend = kern_end; | |
498 | ||
cef17233 | 499 | if (is_64bit) |
500 | { | |
501 | grub_uint32_t *gdt; | |
502 | grub_uint8_t *trampoline; | |
3a4575d4 | 503 | void (*launch_trampoline) (grub_addr_t entry_lo, ...) |
cef17233 | 504 | __attribute__ ((cdecl, regparm (0))); |
505 | grub_uint8_t *pagetable; | |
506 | ||
507 | struct gdt_descriptor *gdtdesc; | |
508 | ||
509 | pagetable = (grub_uint8_t *) (kern_end - 16384); | |
510 | fill_bsd64_pagetable (pagetable); | |
511 | ||
512 | /* Create GDT. */ | |
513 | gdt = (grub_uint32_t *) (kern_end - 4096); | |
514 | gdt[0] = 0; | |
515 | gdt[1] = 0; | |
516 | gdt[2] = 0; | |
517 | gdt[3] = 0x00209800; | |
518 | gdt[4] = 0; | |
519 | gdt[5] = 0x00008000; | |
520 | ||
521 | /* Create GDT descriptor. */ | |
522 | gdtdesc = (struct gdt_descriptor *) (kern_end - 4096 + 24); | |
523 | gdtdesc->limit = 24; | |
524 | gdtdesc->base = gdt; | |
525 | ||
526 | /* Prepare trampoline. */ | |
b39f9d20 | 527 | trampoline = (grub_uint8_t *) (kern_end - 4096 + 24 |
cef17233 | 528 | + sizeof (struct gdt_descriptor)); |
b39f9d20 | 529 | launch_trampoline = (void __attribute__ ((cdecl, regparm (0))) |
3a4575d4 | 530 | (*) (grub_addr_t entry_lo, ...)) trampoline; |
cef17233 | 531 | grub_bsd64_trampoline_gdt = (grub_uint32_t) gdtdesc; |
b39f9d20 | 532 | grub_bsd64_trampoline_selfjump |
cef17233 | 533 | = (grub_uint32_t) (trampoline + 6 |
b39f9d20 | 534 | + ((grub_uint8_t *) &grub_bsd64_trampoline_selfjump |
cef17233 | 535 | - &grub_bsd64_trampoline_start)); |
536 | ||
537 | /* Copy trampoline. */ | |
b39f9d20 | 538 | grub_memcpy (trampoline, &grub_bsd64_trampoline_start, |
cef17233 | 539 | &grub_bsd64_trampoline_end - &grub_bsd64_trampoline_start); |
540 | ||
541 | /* Launch trampoline. */ | |
b39f9d20 | 542 | launch_trampoline (entry, entry_hi, pagetable, bi.bi_modulep, |
cef17233 | 543 | kern_end); |
544 | } | |
545 | else | |
546 | grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev, | |
547 | 0, 0, 0, &bi, bi.bi_modulep, kern_end); | |
d38e24c2 | 548 | |
549 | /* Not reached. */ | |
550 | return GRUB_ERR_NONE; | |
551 | } | |
552 | ||
553 | static grub_err_t | |
554 | grub_openbsd_boot (void) | |
555 | { | |
556 | char *buf = (char *) GRUB_BSD_TEMP_BUFFER; | |
d38e24c2 | 557 | struct grub_openbsd_bios_mmap *pm; |
558 | struct grub_openbsd_bootargs *pa; | |
09d842b9 | 559 | grub_uint32_t bootdev, biosdev, unit, slice, part; |
560 | ||
561 | auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); | |
562 | int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type) | |
563 | { | |
564 | pm->addr = addr; | |
565 | pm->len = size; | |
566 | ||
567 | switch (type) | |
568 | { | |
569 | case GRUB_MACHINE_MEMORY_AVAILABLE: | |
570 | pm->type = OPENBSD_MMAP_AVAILABLE; | |
571 | break; | |
b39f9d20 | 572 | |
09d842b9 | 573 | default: |
574 | pm->type = OPENBSD_MMAP_RESERVED; | |
575 | break; | |
576 | } | |
577 | pm++; | |
578 | ||
579 | return 0; | |
580 | } | |
d38e24c2 | 581 | |
582 | pa = (struct grub_openbsd_bootargs *) buf; | |
583 | ||
584 | pa->ba_type = OPENBSD_BOOTARG_MMAP; | |
585 | pm = (struct grub_openbsd_bios_mmap *) (pa + 1); | |
09d842b9 | 586 | grub_mmap_iterate (hook); |
d38e24c2 | 587 | |
588 | pa->ba_size = (char *) pm - (char *) pa; | |
589 | pa->ba_next = (struct grub_openbsd_bootargs *) pm; | |
590 | pa = pa->ba_next; | |
591 | pa->ba_type = OPENBSD_BOOTARG_END; | |
592 | pa++; | |
593 | ||
594 | grub_bsd_get_device (&biosdev, &unit, &slice, &part); | |
595 | bootdev = (OPENBSD_B_DEVMAGIC + (unit << OPENBSD_B_UNITSHIFT) + | |
596 | (part << OPENBSD_B_PARTSHIFT)); | |
597 | ||
598 | grub_unix_real_boot (entry, bootflags, bootdev, OPENBSD_BOOTARG_APIVER, | |
b39f9d20 | 599 | 0, grub_mmap_get_upper () >> 10, |
09d842b9 | 600 | grub_mmap_get_lower () >> 10, |
d38e24c2 | 601 | (char *) pa - buf, buf); |
602 | ||
603 | /* Not reached. */ | |
604 | return GRUB_ERR_NONE; | |
605 | } | |
606 | ||
607 | static grub_err_t | |
608 | grub_netbsd_boot (void) | |
609 | { | |
610 | struct grub_netbsd_btinfo_rootdevice *rootdev; | |
611 | struct grub_netbsd_bootinfo *bootinfo; | |
612 | grub_uint32_t biosdev, unit, slice, part; | |
613 | ||
614 | grub_bsd_get_device (&biosdev, &unit, &slice, &part); | |
615 | ||
616 | rootdev = (struct grub_netbsd_btinfo_rootdevice *) GRUB_BSD_TEMP_BUFFER; | |
617 | ||
618 | rootdev->common.len = sizeof (struct grub_netbsd_btinfo_rootdevice); | |
619 | rootdev->common.type = NETBSD_BTINFO_ROOTDEVICE; | |
620 | grub_sprintf (rootdev->devname, "%cd%d%c", (biosdev & 0x80) ? 'w' : 'f', | |
621 | unit, 'a' + part); | |
622 | ||
623 | bootinfo = (struct grub_netbsd_bootinfo *) (rootdev + 1); | |
624 | bootinfo->bi_count = 1; | |
625 | bootinfo->bi_data[0] = rootdev; | |
626 | ||
627 | grub_unix_real_boot (entry, bootflags, 0, bootinfo, | |
b39f9d20 | 628 | 0, grub_mmap_get_upper () >> 10, |
09d842b9 | 629 | grub_mmap_get_lower () >> 10); |
d38e24c2 | 630 | |
631 | /* Not reached. */ | |
632 | return GRUB_ERR_NONE; | |
633 | } | |
634 | ||
635 | static grub_err_t | |
636 | grub_bsd_unload (void) | |
637 | { | |
638 | if (mod_buf) | |
639 | { | |
640 | grub_free (mod_buf); | |
641 | mod_buf = 0; | |
642 | mod_buf_max = 0; | |
643 | } | |
644 | ||
645 | kernel_type = KERNEL_TYPE_NONE; | |
646 | grub_dl_unref (my_mod); | |
647 | ||
648 | return GRUB_ERR_NONE; | |
649 | } | |
650 | ||
651 | static grub_err_t | |
652 | grub_bsd_load_aout (grub_file_t file) | |
653 | { | |
654 | grub_addr_t load_addr, bss_end_addr; | |
655 | int ofs, align_page; | |
656 | union grub_aout_header ah; | |
657 | ||
658 | if ((grub_file_seek (file, 0)) == (grub_off_t) - 1) | |
659 | return grub_errno; | |
660 | ||
5c5215d5 | 661 | if (grub_file_read (file, &ah, sizeof (ah)) != sizeof (ah)) |
81623db6 | 662 | return grub_error (GRUB_ERR_READ_ERROR, "Cannot read the a.out header"); |
d38e24c2 | 663 | |
664 | if (grub_aout_get_type (&ah) != AOUT_TYPE_AOUT32) | |
81623db6 | 665 | return grub_error (GRUB_ERR_BAD_OS, "Invalid a.out header"); |
d38e24c2 | 666 | |
667 | entry = ah.aout32.a_entry & 0xFFFFFF; | |
668 | ||
669 | if (AOUT_GETMAGIC (ah.aout32) == AOUT32_ZMAGIC) | |
670 | { | |
671 | load_addr = entry; | |
672 | ofs = 0x1000; | |
673 | align_page = 0; | |
674 | } | |
675 | else | |
676 | { | |
677 | load_addr = entry & 0xF00000; | |
678 | ofs = sizeof (struct grub_aout32_header); | |
679 | align_page = 1; | |
680 | } | |
681 | ||
682 | if (load_addr < 0x100000) | |
81623db6 | 683 | return grub_error (GRUB_ERR_BAD_OS, "Load address below 1M"); |
d38e24c2 | 684 | |
685 | kern_start = load_addr; | |
686 | kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data; | |
687 | if (align_page) | |
688 | kern_end = ALIGN_PAGE (kern_end); | |
689 | ||
690 | if (ah.aout32.a_bss) | |
691 | { | |
692 | kern_end += ah.aout32.a_bss; | |
693 | if (align_page) | |
694 | kern_end = ALIGN_PAGE (kern_end); | |
695 | ||
696 | bss_end_addr = kern_end; | |
697 | } | |
698 | else | |
699 | bss_end_addr = 0; | |
700 | ||
701 | return grub_aout_load (file, ofs, load_addr, | |
702 | ah.aout32.a_text + ah.aout32.a_data, bss_end_addr); | |
703 | } | |
704 | ||
705 | static grub_err_t | |
3f3ec72b | 706 | grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load) |
d38e24c2 | 707 | { |
708 | Elf32_Addr paddr; | |
709 | ||
3f3ec72b | 710 | if (phdr->p_type != PT_LOAD |
711 | && phdr->p_type != PT_DYNAMIC) | |
712 | { | |
713 | *do_load = 0; | |
714 | return 0; | |
715 | } | |
716 | ||
717 | *do_load = 1; | |
d38e24c2 | 718 | phdr->p_paddr &= 0xFFFFFF; |
719 | paddr = phdr->p_paddr; | |
720 | ||
721 | if ((paddr < grub_os_area_addr) | |
722 | || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) | |
723 | return grub_error (GRUB_ERR_OUT_OF_RANGE, "Address 0x%x is out of range", | |
724 | paddr); | |
725 | ||
726 | if ((!kern_start) || (paddr < kern_start)) | |
727 | kern_start = paddr; | |
728 | ||
729 | if (paddr + phdr->p_memsz > kern_end) | |
730 | kern_end = paddr + phdr->p_memsz; | |
731 | ||
556f3775 | 732 | *addr = paddr; |
733 | ||
d38e24c2 | 734 | return GRUB_ERR_NONE; |
735 | } | |
736 | ||
cef17233 | 737 | static grub_err_t |
3f3ec72b | 738 | grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load) |
cef17233 | 739 | { |
740 | Elf64_Addr paddr; | |
741 | ||
3f3ec72b | 742 | if (phdr->p_type != PT_LOAD |
743 | && phdr->p_type != PT_DYNAMIC) | |
744 | { | |
745 | *do_load = 0; | |
746 | return 0; | |
747 | } | |
748 | ||
749 | *do_load = 1; | |
cef17233 | 750 | paddr = phdr->p_paddr & 0xffffff; |
751 | ||
752 | if ((paddr < grub_os_area_addr) | |
753 | || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) | |
754 | return grub_error (GRUB_ERR_OUT_OF_RANGE, "Address 0x%x is out of range", | |
755 | paddr); | |
756 | ||
757 | if ((!kern_start) || (paddr < kern_start)) | |
758 | kern_start = paddr; | |
759 | ||
760 | if (paddr + phdr->p_memsz > kern_end) | |
761 | kern_end = paddr + phdr->p_memsz; | |
762 | ||
763 | *addr = paddr; | |
764 | ||
765 | return GRUB_ERR_NONE; | |
766 | } | |
767 | ||
d38e24c2 | 768 | static grub_err_t |
769 | grub_bsd_load_elf (grub_elf_t elf) | |
770 | { | |
771 | kern_start = kern_end = 0; | |
772 | ||
773 | if (grub_elf_is_elf32 (elf)) | |
774 | { | |
775 | entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF; | |
776 | return grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0); | |
777 | } | |
cef17233 | 778 | else if (grub_elf_is_elf64 (elf)) |
779 | { | |
780 | is_64bit = 1; | |
adb29902 | 781 | |
782 | /* FreeBSD has 64-bit entry point. */ | |
783 | if (kernel_type == KERNEL_TYPE_FREEBSD) | |
784 | { | |
785 | entry = elf->ehdr.ehdr64.e_entry & 0xffffffff; | |
786 | entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff; | |
787 | } | |
788 | else | |
789 | { | |
790 | entry = elf->ehdr.ehdr64.e_entry & 0x0fffffff; | |
791 | entry_hi = 0; | |
792 | } | |
cef17233 | 793 | return grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0); |
794 | } | |
d38e24c2 | 795 | else |
81623db6 | 796 | return grub_error (GRUB_ERR_BAD_OS, "Invalid elf"); |
d38e24c2 | 797 | } |
798 | ||
799 | static grub_err_t | |
800 | grub_bsd_load (int argc, char *argv[]) | |
801 | { | |
802 | grub_file_t file; | |
803 | grub_elf_t elf; | |
804 | ||
805 | grub_dl_ref (my_mod); | |
806 | ||
807 | grub_loader_unset (); | |
808 | ||
809 | if (argc == 0) | |
810 | { | |
81623db6 | 811 | grub_error (GRUB_ERR_BAD_ARGUMENT, "No kernel specified"); |
d38e24c2 | 812 | goto fail; |
813 | } | |
814 | ||
815 | file = grub_gzfile_open (argv[0], 1); | |
816 | if (!file) | |
817 | goto fail; | |
818 | ||
819 | elf = grub_elf_file (file); | |
820 | if (elf) | |
821 | { | |
822 | is_elf_kernel = 1; | |
823 | grub_bsd_load_elf (elf); | |
824 | grub_elf_close (elf); | |
825 | } | |
826 | else | |
827 | { | |
828 | is_elf_kernel = 0; | |
829 | grub_errno = 0; | |
830 | grub_bsd_load_aout (file); | |
831 | grub_file_close (file); | |
832 | } | |
833 | ||
834 | fail: | |
835 | ||
836 | if (grub_errno != GRUB_ERR_NONE) | |
837 | grub_dl_unref (my_mod); | |
838 | ||
839 | return grub_errno; | |
840 | } | |
841 | ||
842 | static grub_uint32_t | |
c11fded5 | 843 | grub_bsd_parse_flags (const struct grub_arg_list *state, |
d38e24c2 | 844 | const grub_uint32_t * flags) |
845 | { | |
846 | grub_uint32_t result = 0; | |
c11fded5 | 847 | unsigned i; |
d38e24c2 | 848 | |
c11fded5 | 849 | for (i = 0; flags[i]; i++) |
850 | if (state[i].set) | |
851 | result |= flags[i]; | |
d38e24c2 | 852 | |
853 | return result; | |
854 | } | |
855 | ||
b1b797cb | 856 | static grub_err_t |
c11fded5 | 857 | grub_cmd_freebsd (grub_extcmd_t cmd, int argc, char *argv[]) |
d38e24c2 | 858 | { |
859 | kernel_type = KERNEL_TYPE_FREEBSD; | |
c11fded5 | 860 | bootflags = grub_bsd_parse_flags (cmd->state, freebsd_flags); |
d38e24c2 | 861 | |
862 | if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE) | |
863 | { | |
864 | kern_end = ALIGN_PAGE (kern_end); | |
3f3ec72b | 865 | if (is_elf_kernel) |
866 | { | |
867 | grub_err_t err; | |
868 | grub_uint64_t data = 0; | |
869 | grub_file_t file; | |
870 | int len = is_64bit ? 8 : 4; | |
871 | ||
872 | err = grub_freebsd_add_meta_module (argv[0], is_64bit | |
873 | ? FREEBSD_MODTYPE_KERNEL64 | |
874 | : FREEBSD_MODTYPE_KERNEL, | |
875 | argc - 1, argv + 1, | |
876 | kern_start, | |
877 | kern_end - kern_start); | |
878 | if (err) | |
879 | return err; | |
880 | ||
881 | file = grub_gzfile_open (argv[0], 1); | |
882 | if (! file) | |
883 | return grub_errno; | |
884 | ||
885 | if (is_64bit) | |
886 | err = grub_freebsd_load_elf_meta64 (file, &kern_end); | |
887 | else | |
888 | err = grub_freebsd_load_elf_meta32 (file, &kern_end); | |
889 | if (err) | |
890 | return err; | |
891 | ||
892 | err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | | |
893 | FREEBSD_MODINFOMD_HOWTO, &data, 4); | |
894 | if (err) | |
895 | return err; | |
896 | ||
897 | err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | | |
898 | FREEBSD_MODINFOMD_ENVP, &data, len); | |
899 | if (err) | |
900 | return err; | |
901 | ||
902 | err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | | |
903 | FREEBSD_MODINFOMD_KERNEND, &data, len); | |
904 | if (err) | |
905 | return err; | |
906 | ||
907 | kern_end_mdofs = mod_buf_len - len; | |
908 | ||
909 | err = grub_freebsd_add_mmap (); | |
910 | if (err) | |
911 | return err; | |
912 | } | |
d38e24c2 | 913 | grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 1); |
914 | } | |
b1b797cb | 915 | |
916 | return grub_errno; | |
d38e24c2 | 917 | } |
918 | ||
b1b797cb | 919 | static grub_err_t |
c11fded5 | 920 | grub_cmd_openbsd (grub_extcmd_t cmd, int argc, char *argv[]) |
d38e24c2 | 921 | { |
922 | kernel_type = KERNEL_TYPE_OPENBSD; | |
c11fded5 | 923 | bootflags = grub_bsd_parse_flags (cmd->state, openbsd_flags); |
d38e24c2 | 924 | |
925 | if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE) | |
926 | grub_loader_set (grub_openbsd_boot, grub_bsd_unload, 1); | |
b1b797cb | 927 | |
928 | return grub_errno; | |
d38e24c2 | 929 | } |
930 | ||
b1b797cb | 931 | static grub_err_t |
c11fded5 | 932 | grub_cmd_netbsd (grub_extcmd_t cmd, int argc, char *argv[]) |
d38e24c2 | 933 | { |
934 | kernel_type = KERNEL_TYPE_NETBSD; | |
c11fded5 | 935 | bootflags = grub_bsd_parse_flags (cmd->state, netbsd_flags); |
d38e24c2 | 936 | |
937 | if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE) | |
938 | grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 1); | |
b1b797cb | 939 | |
940 | return grub_errno; | |
d38e24c2 | 941 | } |
942 | ||
b1b797cb | 943 | static grub_err_t |
944 | grub_cmd_freebsd_loadenv (grub_command_t cmd __attribute__ ((unused)), | |
945 | int argc, char *argv[]) | |
d38e24c2 | 946 | { |
947 | grub_file_t file = 0; | |
948 | char *buf = 0, *curr, *next; | |
949 | int len; | |
950 | ||
81623db6 | 951 | if (kernel_type == KERNEL_TYPE_NONE) |
952 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
953 | "You need to load the kernel first."); | |
954 | ||
d38e24c2 | 955 | if (kernel_type != KERNEL_TYPE_FREEBSD) |
b1b797cb | 956 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
81623db6 | 957 | "Only FreeBSD support environment"); |
d38e24c2 | 958 | |
959 | if (argc == 0) | |
960 | { | |
81623db6 | 961 | grub_error (GRUB_ERR_BAD_ARGUMENT, "No filename"); |
d38e24c2 | 962 | goto fail; |
963 | } | |
964 | ||
965 | file = grub_gzfile_open (argv[0], 1); | |
966 | if ((!file) || (!file->size)) | |
967 | goto fail; | |
968 | ||
969 | len = file->size; | |
970 | buf = grub_malloc (len + 1); | |
971 | if (!buf) | |
972 | goto fail; | |
973 | ||
974 | if (grub_file_read (file, buf, len) != len) | |
975 | goto fail; | |
976 | ||
977 | buf[len] = 0; | |
978 | ||
979 | next = buf; | |
980 | while (next) | |
981 | { | |
982 | char *p; | |
983 | ||
984 | curr = next; | |
985 | next = grub_strchr (curr, '\n'); | |
986 | if (next) | |
987 | { | |
988 | ||
989 | p = next - 1; | |
990 | while (p > curr) | |
991 | { | |
992 | if ((*p != '\r') && (*p != ' ') && (*p != '\t')) | |
993 | break; | |
994 | p--; | |
995 | } | |
996 | ||
997 | if ((p > curr) && (*p == '"')) | |
998 | p--; | |
999 | ||
1000 | *(p + 1) = 0; | |
1001 | next++; | |
1002 | } | |
1003 | ||
1004 | if (*curr == '#') | |
1005 | continue; | |
1006 | ||
1007 | p = grub_strchr (curr, '='); | |
1008 | if (!p) | |
1009 | continue; | |
1010 | ||
1011 | *(p++) = 0; | |
1012 | ||
1013 | if (*curr) | |
1014 | { | |
1015 | char name[grub_strlen (curr) + 8 + 1]; | |
1016 | ||
1017 | if (*p == '"') | |
1018 | p++; | |
1019 | ||
1020 | grub_sprintf (name, "FreeBSD.%s", curr); | |
1021 | if (grub_env_set (name, p)) | |
1022 | goto fail; | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | fail: | |
1027 | grub_free (buf); | |
1028 | ||
1029 | if (file) | |
1030 | grub_file_close (file); | |
b1b797cb | 1031 | |
1032 | return grub_errno; | |
d38e24c2 | 1033 | } |
1034 | ||
b1b797cb | 1035 | static grub_err_t |
1036 | grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)), | |
1037 | int argc, char *argv[]) | |
d38e24c2 | 1038 | { |
1039 | grub_file_t file = 0; | |
3f3ec72b | 1040 | grub_err_t err; |
1041 | int modargc; | |
1042 | char **modargv; | |
1043 | char *type; | |
d38e24c2 | 1044 | |
81623db6 | 1045 | if (kernel_type == KERNEL_TYPE_NONE) |
1046 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
1047 | "You need to load the kernel first."); | |
1048 | ||
d38e24c2 | 1049 | if (kernel_type != KERNEL_TYPE_FREEBSD) |
b1b797cb | 1050 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
81623db6 | 1051 | "Only FreeBSD support module"); |
d38e24c2 | 1052 | |
1053 | if (!is_elf_kernel) | |
b1b797cb | 1054 | return grub_error (GRUB_ERR_BAD_ARGUMENT, |
81623db6 | 1055 | "Only ELF kernel support module"); |
d38e24c2 | 1056 | |
1057 | /* List the current modules if no parameter. */ | |
1058 | if (!argc) | |
1059 | { | |
1060 | grub_freebsd_list_modules (); | |
b1b797cb | 1061 | return 0; |
d38e24c2 | 1062 | } |
1063 | ||
1064 | file = grub_gzfile_open (argv[0], 1); | |
1065 | if ((!file) || (!file->size)) | |
1066 | goto fail; | |
1067 | ||
1068 | if (kern_end + file->size > grub_os_area_addr + grub_os_area_size) | |
1069 | { | |
1070 | grub_error (GRUB_ERR_OUT_OF_RANGE, "Not enough memory for the module"); | |
1071 | goto fail; | |
1072 | } | |
1073 | ||
5c5215d5 | 1074 | grub_file_read (file, (void *) kern_end, file->size); |
3f3ec72b | 1075 | if (grub_errno) |
1076 | goto fail; | |
1077 | ||
1078 | modargc = argc - 1; | |
1079 | modargv = argv + 1; | |
1080 | ||
1081 | if (modargc && (! grub_memcmp (modargv[0], "type=", 5))) | |
1082 | { | |
1083 | type = &modargv[0][5]; | |
1084 | modargc--; | |
1085 | modargv++; | |
1086 | } | |
1087 | else | |
1088 | type = FREEBSD_MODTYPE_RAW; | |
1089 | ||
1090 | err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv, | |
1091 | kern_end, file->size); | |
1092 | if (err) | |
1093 | goto fail; | |
1094 | ||
1095 | kern_end = ALIGN_PAGE (kern_end + file->size); | |
d38e24c2 | 1096 | |
1097 | fail: | |
1098 | if (file) | |
1099 | grub_file_close (file); | |
b1b797cb | 1100 | |
1101 | return grub_errno; | |
d38e24c2 | 1102 | } |
1103 | ||
3f3ec72b | 1104 | static grub_err_t |
1105 | grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)), | |
1106 | int argc, char *argv[]) | |
1107 | { | |
1108 | grub_file_t file = 0; | |
1109 | grub_err_t err; | |
1110 | ||
81623db6 | 1111 | if (kernel_type == KERNEL_TYPE_NONE) |
1112 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
1113 | "You need to load the kernel first."); | |
1114 | ||
3f3ec72b | 1115 | if (kernel_type != KERNEL_TYPE_FREEBSD) |
1116 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
81623db6 | 1117 | "Only FreeBSD support module"); |
3f3ec72b | 1118 | |
1119 | if (! is_elf_kernel) | |
1120 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
81623db6 | 1121 | "Only ELF kernel support module"); |
3f3ec72b | 1122 | |
1123 | /* List the current modules if no parameter. */ | |
1124 | if (! argc) | |
1125 | { | |
1126 | grub_freebsd_list_modules (); | |
1127 | return 0; | |
1128 | } | |
1129 | ||
1130 | file = grub_gzfile_open (argv[0], 1); | |
1131 | if (!file) | |
1132 | return grub_errno; | |
1133 | if (!file->size) | |
1134 | { | |
1135 | grub_file_close (file); | |
1136 | return grub_errno; | |
1137 | } | |
1138 | ||
1139 | if (is_64bit) | |
1140 | err = grub_freebsd_load_elfmodule_obj64 (file, argc, argv, &kern_end); | |
1141 | else | |
1142 | err = grub_freebsd_load_elfmodule32 (file, argc, argv, &kern_end); | |
1143 | grub_file_close (file); | |
1144 | ||
1145 | return err; | |
1146 | } | |
1147 | ||
1148 | ||
c11fded5 | 1149 | static grub_extcmd_t cmd_freebsd, cmd_openbsd, cmd_netbsd; |
b1b797cb | 1150 | static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module; |
3f3ec72b | 1151 | static grub_command_t cmd_freebsd_module_elf; |
b1b797cb | 1152 | |
d38e24c2 | 1153 | GRUB_MOD_INIT (bsd) |
1154 | { | |
c11fded5 | 1155 | cmd_freebsd = grub_register_extcmd ("freebsd", grub_cmd_freebsd, |
1156 | GRUB_COMMAND_FLAG_BOTH, | |
1157 | "freebsd FILE", "Load kernel of FreeBSD.", | |
1158 | freebsd_opts); | |
1159 | cmd_openbsd = grub_register_extcmd ("openbsd", grub_cmd_openbsd, | |
1160 | GRUB_COMMAND_FLAG_BOTH, | |
1161 | "openbsd FILE", "Load kernel of OpenBSD.", | |
1162 | openbsd_opts); | |
1163 | cmd_netbsd = grub_register_extcmd ("netbsd", grub_cmd_netbsd, | |
1164 | GRUB_COMMAND_FLAG_BOTH, | |
1165 | "netbsd FILE", "Load kernel of NetBSD.", | |
1166 | netbsd_opts); | |
b1b797cb | 1167 | cmd_freebsd_loadenv = |
1168 | grub_register_command ("freebsd_loadenv", grub_cmd_freebsd_loadenv, | |
81623db6 | 1169 | 0, "load FreeBSD env"); |
b1b797cb | 1170 | cmd_freebsd_module = |
1171 | grub_register_command ("freebsd_module", grub_cmd_freebsd_module, | |
81623db6 | 1172 | 0, "load FreeBSD kernel module"); |
3f3ec72b | 1173 | cmd_freebsd_module_elf = |
1174 | grub_register_command ("freebsd_module_elf", grub_cmd_freebsd_module_elf, | |
81623db6 | 1175 | 0, "load FreeBSD kernel module (ELF)"); |
d38e24c2 | 1176 | |
1177 | my_mod = mod; | |
1178 | } | |
1179 | ||
1180 | GRUB_MOD_FINI (bsd) | |
1181 | { | |
c11fded5 | 1182 | grub_unregister_extcmd (cmd_freebsd); |
1183 | grub_unregister_extcmd (cmd_openbsd); | |
1184 | grub_unregister_extcmd (cmd_netbsd); | |
d38e24c2 | 1185 | |
b1b797cb | 1186 | grub_unregister_command (cmd_freebsd_loadenv); |
1187 | grub_unregister_command (cmd_freebsd_module); | |
3f3ec72b | 1188 | grub_unregister_command (cmd_freebsd_module_elf); |
d38e24c2 | 1189 | |
1190 | if (mod_buf) | |
1191 | { | |
1192 | grub_free (mod_buf); | |
1193 | mod_buf = 0; | |
1194 | mod_buf_max = 0; | |
1195 | } | |
1196 | } |