2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2008 Free Software Foundation, Inc.
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.
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.
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/>.
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>
24 #include <grub/file.h>
26 #include <grub/rescue.h>
29 #include <grub/elfload.h>
31 #include <grub/misc.h>
32 #include <grub/gzio.h>
33 #include <grub/aout.h>
35 #define ALIGN_DWORD(a) ALIGN_UP (a, 4)
36 #define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
38 #define MOD_BUF_ALLOC_UNIT 4096
40 static int kernel_type
;
41 static grub_dl_t my_mod
;
42 static grub_addr_t entry
, kern_start
, kern_end
;
43 static grub_uint32_t bootflags
;
45 static grub_uint32_t mod_buf_len
, mod_buf_max
;
46 static int is_elf_kernel
;
48 static const char freebsd_opts
[] = "DhaCcdgmnpqrsv";
49 static const grub_uint32_t freebsd_flags
[] =
51 FREEBSD_RB_DUAL
, FREEBSD_RB_SERIAL
, FREEBSD_RB_ASKNAME
,
52 FREEBSD_RB_CDROM
, FREEBSD_RB_CONFIG
, FREEBSD_RB_KDB
,
53 FREEBSD_RB_GDB
, FREEBSD_RB_MUTE
, FREEBSD_RB_NOINTR
,
54 FREEBSD_RB_PAUSE
, FREEBSD_RB_QUIET
, FREEBSD_RB_DFLTROOT
,
55 FREEBSD_RB_SINGLE
, FREEBSD_RB_VERBOSE
58 static const char openbsd_opts
[] = "abcsd";
59 static const grub_uint32_t openbsd_flags
[] =
61 OPENBSD_RB_ASKNAME
, OPENBSD_RB_HALT
, OPENBSD_RB_CONFIG
,
62 OPENBSD_RB_SINGLE
, OPENBSD_RB_KDB
65 static const char netbsd_opts
[] = "abcdmqsvxz";
66 static const grub_uint32_t netbsd_flags
[] =
68 NETBSD_RB_ASKNAME
, NETBSD_RB_HALT
, NETBSD_RB_USERCONFIG
,
69 NETBSD_RB_KDB
, NETBSD_RB_MINIROOT
, NETBSD_AB_QUIET
,
70 NETBSD_RB_SINGLE
, NETBSD_AB_VERBOSE
, NETBSD_AB_DEBUG
,
75 grub_bsd_get_device (grub_uint32_t
* biosdev
,
77 grub_uint32_t
* slice
, grub_uint32_t
* part
)
81 *biosdev
= *unit
= *slice
= *part
= 0;
82 p
= grub_env_get ("root");
83 if ((p
) && ((p
[0] == 'h') || (p
[0] == 'f')) && (p
[1] == 'd') &&
84 (p
[2] >= '0') && (p
[2] <= '9'))
89 *unit
= grub_strtoul (p
+ 2, &p
, 0);
92 if ((p
) && (p
[0] == ','))
94 if ((p
[1] >= '0') && (p
[1] <= '9'))
96 *slice
= grub_strtoul (p
+ 1, &p
, 0);
98 if ((p
) && (p
[0] == ','))
102 if ((p
[0] >= 'a') && (p
[0] <= 'z'))
109 grub_freebsd_add_meta (grub_uint32_t type
, void *data
, grub_uint32_t len
)
111 if (mod_buf_max
< mod_buf_len
+ len
+ 8)
117 mod_buf_max
+= MOD_BUF_ALLOC_UNIT
;
119 while (mod_buf_max
< mod_buf_len
+ len
+ 8);
121 new_buf
= grub_malloc (mod_buf_max
);
125 grub_memcpy (new_buf
, mod_buf
, mod_buf_len
);
131 *((grub_uint32_t
*) (mod_buf
+ mod_buf_len
)) = type
;
132 *((grub_uint32_t
*) (mod_buf
+ mod_buf_len
+ 4)) = len
;
136 grub_memcpy (mod_buf
+ mod_buf_len
, data
, len
);
138 mod_buf_len
= ALIGN_DWORD (mod_buf_len
+ len
);
140 return GRUB_ERR_NONE
;
144 grub_freebsd_add_meta_module (int is_kern
, int argc
, char **argv
,
145 grub_addr_t addr
, grub_uint32_t size
)
149 name
= grub_strrchr (argv
[0], '/');
155 if (grub_freebsd_add_meta (FREEBSD_MODINFO_NAME
, name
,
156 grub_strlen (name
) + 1))
162 if ((argc
) && (!grub_memcmp (argv
[0], "type=", 5)))
169 type
= (is_kern
) ? FREEBSD_MODTYPE_KERNEL
: FREEBSD_MODTYPE_RAW
;
171 if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE
, type
,
172 grub_strlen (type
) + 1)) ||
173 (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR
, &addr
, sizeof (addr
))) ||
174 (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE
, &size
, sizeof (size
))))
182 for (i
= 0; i
< argc
; i
++)
184 n
+= grub_strlen (argv
[i
]) + 1;
192 for (i
= 0; i
< argc
; i
++)
194 grub_strcpy (p
, argv
[i
]);
195 p
+= grub_strlen (argv
[i
]);
200 if (grub_freebsd_add_meta (FREEBSD_MODINFO_ARGS
, cmdline
, n
))
205 return GRUB_ERR_NONE
;
209 grub_freebsd_list_modules (void)
211 grub_uint32_t pos
= 0;
213 grub_printf (" %-18s %-18s%14s%14s\n", "name", "type", "addr", "size");
214 while (pos
< mod_buf_len
)
216 grub_uint32_t type
, size
;
218 type
= *((grub_uint32_t
*) (mod_buf
+ pos
));
219 size
= *((grub_uint32_t
*) (mod_buf
+ pos
+ 4));
223 case FREEBSD_MODINFO_NAME
:
224 case FREEBSD_MODINFO_TYPE
:
225 grub_printf (" %-18s", mod_buf
+ pos
);
227 case FREEBSD_MODINFO_ADDR
:
231 addr
= *((grub_addr_t
*) (mod_buf
+ pos
));
232 grub_printf (" 0x%08x", addr
);
235 case FREEBSD_MODINFO_SIZE
:
239 len
= *((grub_uint32_t
*) (mod_buf
+ pos
));
240 grub_printf (" 0x%08x\n", len
);
244 pos
= ALIGN_DWORD (pos
+ size
);
249 grub_freebsd_boot (void)
251 struct grub_freebsd_bootinfo bi
;
253 grub_uint32_t bootdev
, biosdev
, unit
, slice
, part
;
255 auto int iterate_env (struct grub_env_var
*var
);
256 int iterate_env (struct grub_env_var
*var
)
258 if ((!grub_memcmp (var
->name
, "FreeBSD.", 8)) && (var
->name
[8]))
260 grub_strcpy (p
, &var
->name
[8]);
261 p
+= grub_strlen (p
);
263 grub_strcpy (p
, var
->value
);
264 p
+= grub_strlen (p
) + 1;
270 grub_memset (&bi
, 0, sizeof (bi
));
271 bi
.bi_version
= FREEBSD_BOOTINFO_VERSION
;
272 bi
.bi_size
= sizeof (bi
);
274 grub_bsd_get_device (&biosdev
, &unit
, &slice
, &part
);
275 bootdev
= (FREEBSD_B_DEVMAGIC
+ ((slice
+ 1) << FREEBSD_B_SLICESHIFT
) +
276 (unit
<< FREEBSD_B_UNITSHIFT
) + (part
<< FREEBSD_B_PARTSHIFT
));
278 bi
.bi_bios_dev
= biosdev
;
280 p
= (char *) kern_end
;
282 grub_env_iterate (iterate_env
);
284 if (p
!= (char *) kern_end
)
288 bi
.bi_envp
= kern_end
;
289 kern_end
= ALIGN_PAGE ((grub_uint32_t
) p
);
294 if (grub_freebsd_add_meta (FREEBSD_MODINFO_END
, 0, 0))
297 grub_memcpy ((char *) kern_end
, mod_buf
, mod_buf_len
);
298 bi
.bi_modulep
= kern_end
;
300 kern_end
= ALIGN_PAGE (kern_end
+ mod_buf_len
);
303 bi
.bi_kernend
= kern_end
;
305 grub_unix_real_boot (entry
, bootflags
| FREEBSD_RB_BOOTINFO
, bootdev
,
306 0, 0, 0, &bi
, bi
.bi_modulep
, kern_end
);
309 return GRUB_ERR_NONE
;
313 grub_openbsd_boot (void)
315 char *buf
= (char *) GRUB_BSD_TEMP_BUFFER
;
316 struct grub_machine_mmap_entry mmap
;
317 struct grub_openbsd_bios_mmap
*pm
;
318 struct grub_openbsd_bootargs
*pa
;
319 grub_uint32_t bootdev
, biosdev
, unit
, slice
, part
, cont
;
321 pa
= (struct grub_openbsd_bootargs
*) buf
;
323 pa
->ba_type
= OPENBSD_BOOTARG_MMAP
;
324 pm
= (struct grub_openbsd_bios_mmap
*) (pa
+ 1);
325 cont
= grub_get_mmap_entry (&mmap
, 0);
329 pm
->addr
= mmap
.addr
;
331 pm
->type
= mmap
.type
;
337 cont
= grub_get_mmap_entry (&mmap
, cont
);
341 pa
->ba_size
= (char *) pm
- (char *) pa
;
342 pa
->ba_next
= (struct grub_openbsd_bootargs
*) pm
;
344 pa
->ba_type
= OPENBSD_BOOTARG_END
;
347 grub_bsd_get_device (&biosdev
, &unit
, &slice
, &part
);
348 bootdev
= (OPENBSD_B_DEVMAGIC
+ (unit
<< OPENBSD_B_UNITSHIFT
) +
349 (part
<< OPENBSD_B_PARTSHIFT
));
351 grub_unix_real_boot (entry
, bootflags
, bootdev
, OPENBSD_BOOTARG_APIVER
,
352 0, grub_upper_mem
>> 10, grub_lower_mem
>> 10,
353 (char *) pa
- buf
, buf
);
356 return GRUB_ERR_NONE
;
360 grub_netbsd_boot (void)
362 struct grub_netbsd_btinfo_rootdevice
*rootdev
;
363 struct grub_netbsd_bootinfo
*bootinfo
;
364 grub_uint32_t biosdev
, unit
, slice
, part
;
366 grub_bsd_get_device (&biosdev
, &unit
, &slice
, &part
);
368 rootdev
= (struct grub_netbsd_btinfo_rootdevice
*) GRUB_BSD_TEMP_BUFFER
;
370 rootdev
->common
.len
= sizeof (struct grub_netbsd_btinfo_rootdevice
);
371 rootdev
->common
.type
= NETBSD_BTINFO_ROOTDEVICE
;
372 grub_sprintf (rootdev
->devname
, "%cd%d%c", (biosdev
& 0x80) ? 'w' : 'f',
375 bootinfo
= (struct grub_netbsd_bootinfo
*) (rootdev
+ 1);
376 bootinfo
->bi_count
= 1;
377 bootinfo
->bi_data
[0] = rootdev
;
379 grub_unix_real_boot (entry
, bootflags
, 0, bootinfo
,
380 0, grub_upper_mem
>> 10, grub_lower_mem
>> 10);
383 return GRUB_ERR_NONE
;
387 grub_bsd_unload (void)
396 kernel_type
= KERNEL_TYPE_NONE
;
397 grub_dl_unref (my_mod
);
399 return GRUB_ERR_NONE
;
403 grub_bsd_load_aout (grub_file_t file
)
405 grub_addr_t load_addr
, bss_end_addr
;
407 union grub_aout_header ah
;
409 if ((grub_file_seek (file
, 0)) == (grub_off_t
) - 1)
412 if (grub_file_read (file
, (char *) &ah
, sizeof (ah
)) != sizeof (ah
))
413 return grub_error (GRUB_ERR_READ_ERROR
, "cannot read the a.out header");
415 if (grub_aout_get_type (&ah
) != AOUT_TYPE_AOUT32
)
416 return grub_error (GRUB_ERR_BAD_OS
, "invalid a.out header");
418 entry
= ah
.aout32
.a_entry
& 0xFFFFFF;
420 if (AOUT_GETMAGIC (ah
.aout32
) == AOUT32_ZMAGIC
)
428 load_addr
= entry
& 0xF00000;
429 ofs
= sizeof (struct grub_aout32_header
);
433 if (load_addr
< 0x100000)
434 return grub_error (GRUB_ERR_BAD_OS
, "load address below 1M");
436 kern_start
= load_addr
;
437 kern_end
= load_addr
+ ah
.aout32
.a_text
+ ah
.aout32
.a_data
;
439 kern_end
= ALIGN_PAGE (kern_end
);
443 kern_end
+= ah
.aout32
.a_bss
;
445 kern_end
= ALIGN_PAGE (kern_end
);
447 bss_end_addr
= kern_end
;
452 return grub_aout_load (file
, ofs
, load_addr
,
453 ah
.aout32
.a_text
+ ah
.aout32
.a_data
, bss_end_addr
);
457 grub_bsd_elf32_hook (Elf32_Phdr
* phdr
, grub_addr_t
* addr
)
461 phdr
->p_paddr
&= 0xFFFFFF;
462 paddr
= phdr
->p_paddr
;
464 if ((paddr
< grub_os_area_addr
)
465 || (paddr
+ phdr
->p_memsz
> grub_os_area_addr
+ grub_os_area_size
))
466 return grub_error (GRUB_ERR_OUT_OF_RANGE
, "Address 0x%x is out of range",
469 if ((!kern_start
) || (paddr
< kern_start
))
472 if (paddr
+ phdr
->p_memsz
> kern_end
)
473 kern_end
= paddr
+ phdr
->p_memsz
;
477 return GRUB_ERR_NONE
;
481 grub_bsd_load_elf (grub_elf_t elf
)
483 kern_start
= kern_end
= 0;
485 if (grub_elf_is_elf32 (elf
))
487 entry
= elf
->ehdr
.ehdr32
.e_entry
& 0xFFFFFF;
488 return grub_elf32_load (elf
, grub_bsd_elf32_hook
, 0, 0);
491 return grub_error (GRUB_ERR_BAD_OS
, "invalid elf");
495 grub_bsd_load (int argc
, char *argv
[])
500 grub_dl_ref (my_mod
);
502 grub_loader_unset ();
506 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no kernel specified");
510 file
= grub_gzfile_open (argv
[0], 1);
514 elf
= grub_elf_file (file
);
518 grub_bsd_load_elf (elf
);
519 grub_elf_close (elf
);
525 grub_bsd_load_aout (file
);
526 grub_file_close (file
);
531 if (grub_errno
!= GRUB_ERR_NONE
)
532 grub_dl_unref (my_mod
);
538 grub_bsd_parse_flags (char *str
, const char *opts
,
539 const grub_uint32_t
* flags
)
541 grub_uint32_t result
= 0;
546 const grub_uint32_t
*pf
;
567 grub_rescue_cmd_freebsd (int argc
, char *argv
[])
569 kernel_type
= KERNEL_TYPE_FREEBSD
;
570 bootflags
= ((argc
<= 1) ? 0 :
571 grub_bsd_parse_flags (argv
[1], freebsd_opts
, freebsd_flags
));
573 if (grub_bsd_load (argc
, argv
) == GRUB_ERR_NONE
)
575 kern_end
= ALIGN_PAGE (kern_end
);
576 if ((is_elf_kernel
) &&
577 (grub_freebsd_add_meta_module (1, argc
, argv
, kern_start
,
578 kern_end
- kern_start
)))
580 grub_loader_set (grub_freebsd_boot
, grub_bsd_unload
, 1);
585 grub_rescue_cmd_openbsd (int argc
, char *argv
[])
587 kernel_type
= KERNEL_TYPE_OPENBSD
;
588 bootflags
= ((argc
<= 1) ? 0 :
589 grub_bsd_parse_flags (argv
[1], openbsd_opts
, openbsd_flags
));
591 if (grub_bsd_load (argc
, argv
) == GRUB_ERR_NONE
)
592 grub_loader_set (grub_openbsd_boot
, grub_bsd_unload
, 1);
596 grub_rescue_cmd_netbsd (int argc
, char *argv
[])
598 kernel_type
= KERNEL_TYPE_NETBSD
;
599 bootflags
= ((argc
<= 1) ? 0 :
600 grub_bsd_parse_flags (argv
[1], netbsd_opts
, netbsd_flags
));
602 if (grub_bsd_load (argc
, argv
) == GRUB_ERR_NONE
)
603 grub_loader_set (grub_netbsd_boot
, grub_bsd_unload
, 1);
607 grub_rescue_cmd_freebsd_loadenv (int argc
, char *argv
[])
609 grub_file_t file
= 0;
610 char *buf
= 0, *curr
, *next
;
613 if (kernel_type
!= KERNEL_TYPE_FREEBSD
)
615 grub_error (GRUB_ERR_BAD_ARGUMENT
, "only freebsd support environment");
621 grub_error (GRUB_ERR_BAD_ARGUMENT
, "no filename");
625 file
= grub_gzfile_open (argv
[0], 1);
626 if ((!file
) || (!file
->size
))
630 buf
= grub_malloc (len
+ 1);
634 if (grub_file_read (file
, buf
, len
) != len
)
645 next
= grub_strchr (curr
, '\n');
652 if ((*p
!= '\r') && (*p
!= ' ') && (*p
!= '\t'))
657 if ((p
> curr
) && (*p
== '"'))
667 p
= grub_strchr (curr
, '=');
675 char name
[grub_strlen (curr
) + 8 + 1];
680 grub_sprintf (name
, "FreeBSD.%s", curr
);
681 if (grub_env_set (name
, p
))
690 grub_file_close (file
);
694 grub_rescue_cmd_freebsd_module (int argc
, char *argv
[])
696 grub_file_t file
= 0;
698 if (kernel_type
!= KERNEL_TYPE_FREEBSD
)
700 grub_error (GRUB_ERR_BAD_ARGUMENT
, "only freebsd support module");
706 grub_error (GRUB_ERR_BAD_ARGUMENT
, "only elf kernel support module");
710 /* List the current modules if no parameter. */
713 grub_freebsd_list_modules ();
717 file
= grub_gzfile_open (argv
[0], 1);
718 if ((!file
) || (!file
->size
))
721 if (kern_end
+ file
->size
> grub_os_area_addr
+ grub_os_area_size
)
723 grub_error (GRUB_ERR_OUT_OF_RANGE
, "Not enough memory for the module");
727 grub_file_read (file
, (char *) kern_end
, file
->size
);
729 (!grub_freebsd_add_meta_module (0, argc
, argv
, kern_end
, file
->size
)))
730 kern_end
= ALIGN_PAGE (kern_end
+ file
->size
);
734 grub_file_close (file
);
739 grub_rescue_register_command ("freebsd",
740 grub_rescue_cmd_freebsd
,
741 "load freebsd kernel");
742 grub_rescue_register_command ("openbsd",
743 grub_rescue_cmd_openbsd
,
744 "load openbsd kernel");
745 grub_rescue_register_command ("netbsd",
746 grub_rescue_cmd_netbsd
, "load netbsd kernel");
748 grub_rescue_register_command ("freebsd_loadenv",
749 grub_rescue_cmd_freebsd_loadenv
,
751 grub_rescue_register_command ("freebsd_module",
752 grub_rescue_cmd_freebsd_module
,
753 "load freebsd module");
760 grub_rescue_unregister_command ("freebsd");
761 grub_rescue_unregister_command ("openbsd");
762 grub_rescue_unregister_command ("netbsd");
764 grub_rescue_unregister_command ("freebsd_loadenv");
765 grub_rescue_unregister_command ("freebsd_module");