#include "shared.h"
#include "freebsd.h"
#include "imgact_aout.h"
#include "i386-elf.h"
static int cur_addr;
entry_func entry_addr;
static struct mod_list mll[99];
static int linux_mem_size;
kernel_t
load_image (char *kernel, char *arg, kernel_t suggested_type,
unsigned long load_flags)
{
int len, i, exec_type = 0, align_4k = 1;
entry_func real_entry_addr = 0;
kernel_t type = KERNEL_TYPE_NONE;
unsigned long flags = 0, text_len = 0, data_len = 0, bss_len = 0;
char *str = 0, *str2 = 0;
struct linux_kernel_header *lh;
union
{
struct multiboot_header *mb;
struct exec *aout;
Elf32_Ehdr *elf;
}
pu;
unsigned char buffer[MULTIBOOT_SEARCH];
pu.aout = (struct exec *) buffer;
if (!grub_open (kernel))
return KERNEL_TYPE_NONE;
if (!(len = grub_read (buffer, MULTIBOOT_SEARCH)) || len < 32)
{
grub_close ();
if (!errnum)
errnum = ERR_EXEC_FORMAT;
return KERNEL_TYPE_NONE;
}
for (i = 0; i < len; i++)
{
if (MULTIBOOT_FOUND ((int) (buffer + i), len - i))
{
flags = ((struct multiboot_header *) (buffer + i))->flags;
if (flags & MULTIBOOT_UNSUPPORTED)
{
grub_close ();
errnum = ERR_BOOT_FEATURES;
return KERNEL_TYPE_NONE;
}
type = KERNEL_TYPE_MULTIBOOT;
str2 = "Multiboot";
break;
}
}
lh = (struct linux_kernel_header *) buffer;
if ((type == KERNEL_TYPE_MULTIBOOT
|| pu.elf->e_ident[EI_OSABI] == ELFOSABI_FREEBSD
|| grub_strcmp (pu.elf->e_ident + EI_BRAND, "FreeBSD") == 0
|| suggested_type == KERNEL_TYPE_NETBSD)
&& len > sizeof (Elf32_Ehdr)
&& BOOTABLE_I386_ELF ((*((Elf32_Ehdr *) buffer))))
{
if (type == KERNEL_TYPE_MULTIBOOT)
entry_addr = (entry_func) pu.elf->e_entry;
else
entry_addr = (entry_func) (pu.elf->e_entry & 0xFFFFFF);
if (entry_addr < (entry_func) 0x100000)
errnum = ERR_BELOW_1MB;
if (pu.elf->e_phoff == 0 || pu.elf->e_phnum == 0
|| ((pu.elf->e_phoff + (pu.elf->e_phentsize * pu.elf->e_phnum))
>= len))
errnum = ERR_EXEC_FORMAT;
str = "elf";
if (type == KERNEL_TYPE_NONE)
{
if (suggested_type == KERNEL_TYPE_NETBSD)
{
str2 = "NetBSD";
type = suggested_type;
}
else
{
str2 = "FreeBSD";
type = KERNEL_TYPE_FREEBSD;
}
}
}
else if (flags & MULTIBOOT_AOUT_KLUDGE)
{
pu.mb = (struct multiboot_header *) (buffer + i);
entry_addr = (entry_func) pu.mb->entry_addr;
cur_addr = pu.mb->load_addr;
grub_seek (i - (pu.mb->header_addr - cur_addr));
if (! pu.mb->load_end_addr)
pu.mb->load_end_addr = cur_addr + filemax;
text_len = pu.mb->load_end_addr - cur_addr;
data_len = 0;
if (! pu.mb->bss_end_addr)
pu.mb->bss_end_addr = pu.mb->load_end_addr;
bss_len = pu.mb->bss_end_addr - pu.mb->load_end_addr;
if (pu.mb->header_addr < pu.mb->load_addr
|| pu.mb->load_end_addr <= pu.mb->load_addr
|| pu.mb->bss_end_addr < pu.mb->load_end_addr
|| (pu.mb->header_addr - pu.mb->load_addr) > i)
errnum = ERR_EXEC_FORMAT;
if (cur_addr < 0x100000)
errnum = ERR_BELOW_1MB;
pu.aout = (struct exec *) buffer;
exec_type = 2;
str = "kludge";
}
else if (len > sizeof (struct exec) && !N_BADMAG ((*(pu.aout))))
{
entry_addr = (entry_func) pu.aout->a_entry;
if (type == KERNEL_TYPE_NONE)
{
entry_addr = (entry_func) ((int) entry_addr & 0xFFFFFF);
if (buffer[0] == 0xb && buffer[1] == 1)
{
type = KERNEL_TYPE_FREEBSD;
cur_addr = (int) entry_addr;
str2 = "FreeBSD";
}
else
{
type = KERNEL_TYPE_NETBSD;
cur_addr = (int) entry_addr & 0xF00000;
if (N_GETMAGIC ((*(pu.aout))) != NMAGIC)
align_4k = 0;
str2 = "NetBSD";
}
}
grub_seek (N_TXTOFF (*(pu.aout)));
text_len = pu.aout->a_text;
data_len = pu.aout->a_data;
bss_len = pu.aout->a_bss;
if (cur_addr < 0x100000)
errnum = ERR_BELOW_1MB;
exec_type = 1;
str = "a.out";
}
else if (lh->boot_flag == BOOTSEC_SIGNATURE
&& lh->setup_sects <= LINUX_MAX_SETUP_SECTS)
{
int big_linux = 0;
int setup_sects = lh->setup_sects;
if (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0200)
{
big_linux = (lh->loadflags & LINUX_FLAG_BIG_KERNEL);
lh->type_of_loader = LINUX_BOOT_LOADER_TYPE;
linux_data_real_addr
= (char *) ((mbi.mem_lower << 10) - LINUX_SETUP_MOVE_SIZE);
if (linux_data_real_addr > (char *) LINUX_OLD_REAL_MODE_ADDR)
linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR;
if (lh->version >= 0x0201)
{
lh->heap_end_ptr = LINUX_HEAP_END_OFFSET;
lh->loadflags |= LINUX_FLAG_CAN_USE_HEAP;
}
if (lh->version >= 0x0202)
lh->cmd_line_ptr = linux_data_real_addr + LINUX_CL_OFFSET;
else
{
lh->cl_magic = LINUX_CL_MAGIC;
lh->cl_offset = LINUX_CL_OFFSET;
lh->setup_move_size = LINUX_SETUP_MOVE_SIZE;
}
}
else
{
lh->cl_magic = LINUX_CL_MAGIC;
lh->cl_offset = LINUX_CL_OFFSET;
setup_sects = LINUX_DEFAULT_SETUP_SECTS;
linux_data_real_addr = (char *) LINUX_OLD_REAL_MODE_ADDR;
}
if (! setup_sects)
setup_sects = LINUX_DEFAULT_SETUP_SECTS;
data_len = setup_sects << 9;
text_len = filemax - data_len - SECTOR_SIZE;
linux_data_tmp_addr = (char *) LINUX_BZIMAGE_ADDR + text_len;
if (! big_linux
&& text_len > linux_data_real_addr - (char *) LINUX_ZIMAGE_ADDR)
{
grub_printf (" linux 'zImage' kernel too big, try 'make bzImage'\n");
errnum = ERR_WONT_FIT;
}
else if (linux_data_real_addr + LINUX_SETUP_MOVE_SIZE
> RAW_ADDR ((char *) (mbi.mem_lower << 10)))
errnum = ERR_WONT_FIT;
else
{
grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
(big_linux ? "bzImage" : "zImage"), data_len, text_len);
{
char *vga;
vga = grub_strstr (arg, "vga=");
if (vga)
{
char *value = vga + 4;
int vid_mode;
if (substring ("normal", value) < 1)
vid_mode = LINUX_VID_MODE_NORMAL;
else if (substring ("ext", value) < 1)
vid_mode = LINUX_VID_MODE_EXTENDED;
else if (substring ("ask", value) < 1)
vid_mode = LINUX_VID_MODE_ASK;
else if (safe_parse_maxint (&value, &vid_mode))
;
else
{
grub_close ();
return KERNEL_TYPE_NONE;
}
lh->vid_mode = vid_mode;
}
}
{
char *mem;
mem = grub_strstr (arg, "mem=");
if (mem)
{
char *value = mem + 4;
safe_parse_maxint (&value, &linux_mem_size);
switch (errnum)
{
case ERR_NUMBER_OVERFLOW:
linux_mem_size = LINUX_INITRD_MAX_ADDRESS;
errnum = ERR_NONE;
break;
case ERR_NONE:
{
int shift = 0;
switch (grub_tolower (*value))
{
case 'g':
shift += 10;
case 'm':
shift += 10;
case 'k':
shift += 10;
default:
break;
}
if (linux_mem_size > (MAXINT >> shift))
linux_mem_size = LINUX_INITRD_MAX_ADDRESS;
else
linux_mem_size <<= shift;
}
break;
default:
linux_mem_size = 0;
errnum = ERR_NONE;
break;
}
}
else
linux_mem_size = 0;
}
if (data_len + SECTOR_SIZE <= MULTIBOOT_SEARCH)
grub_memmove (linux_data_tmp_addr, buffer,
data_len + SECTOR_SIZE);
else
{
grub_memmove (linux_data_tmp_addr, buffer, MULTIBOOT_SEARCH);
grub_read (linux_data_tmp_addr + MULTIBOOT_SEARCH,
data_len + SECTOR_SIZE - MULTIBOOT_SEARCH);
}
if (lh->header != LINUX_MAGIC_SIGNATURE ||
lh->version < 0x0200)
grub_memset (linux_data_tmp_addr + ((setup_sects + 1) << 9),
0,
(64 - setup_sects - 1) << 9);
{
char *src = skip_to (0, arg);
char *dest = linux_data_tmp_addr + LINUX_CL_OFFSET;
while (dest < linux_data_tmp_addr + LINUX_CL_END_OFFSET && *src)
*(dest++) = *(src++);
if (! grub_strstr (arg, "mem=")
&& ! (load_flags & KERNEL_LOAD_NO_MEM_OPTION)
&& lh->version < 0x0203
&& dest + 15 < linux_data_tmp_addr + LINUX_CL_END_OFFSET)
{
*dest++ = ' ';
*dest++ = 'm';
*dest++ = 'e';
*dest++ = 'm';
*dest++ = '=';
dest = convert_to_ascii (dest, 'u', (extended_memory + 0x400));
*dest++ = 'K';
}
*dest = 0;
}
grub_seek (data_len + SECTOR_SIZE);
cur_addr = (int) linux_data_tmp_addr + LINUX_SETUP_MOVE_SIZE;
grub_read ((char *) LINUX_BZIMAGE_ADDR, text_len);
if (errnum == ERR_NONE)
{
grub_close ();
if (suggested_type != KERNEL_TYPE_NONE
&& ((big_linux && suggested_type != KERNEL_TYPE_BIG_LINUX)
|| (! big_linux && suggested_type != KERNEL_TYPE_LINUX)))
{
errnum = ERR_EXEC_FORMAT;
return KERNEL_TYPE_NONE;
}
linux_text_len = text_len;
return big_linux ? KERNEL_TYPE_BIG_LINUX : KERNEL_TYPE_LINUX;
}
}
}
else
errnum = ERR_EXEC_FORMAT;
if (errnum)
{
grub_close ();
return KERNEL_TYPE_NONE;
}
mbi.cmdline = (int) arg;
mbi.mods_count = 0;
mbi.mods_addr = 0;
mbi.boot_device = (current_drive << 24) | current_partition;
mbi.flags &= ~(MB_INFO_MODS | MB_INFO_AOUT_SYMS | MB_INFO_ELF_SHDR);
mbi.syms.a.tabsize = 0;
mbi.syms.a.strsize = 0;
mbi.syms.a.addr = 0;
mbi.syms.a.pad = 0;
printf (" [%s-%s", str2, str);
str = "";
if (exec_type)
{
if (flags & MULTIBOOT_AOUT_KLUDGE)
str = "-and-data";
printf (", loadaddr=0x%x, text%s=0x%x", cur_addr, str, text_len);
if (grub_read ((char *) RAW_ADDR (cur_addr), text_len) == text_len)
{
cur_addr += text_len;
if (!(flags & MULTIBOOT_AOUT_KLUDGE))
{
if (align_4k)
cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
else
printf (", C");
printf (", data=0x%x", data_len);
if ((grub_read ((char *) RAW_ADDR (cur_addr), data_len)
!= data_len)
&& !errnum)
errnum = ERR_EXEC_FORMAT;
cur_addr += data_len;
}
if (!errnum)
{
memset ((char *) RAW_ADDR (cur_addr), 0, bss_len);
cur_addr += bss_len;
printf (", bss=0x%x", bss_len);
}
}
else if (!errnum)
errnum = ERR_EXEC_FORMAT;
if (!errnum && pu.aout->a_syms
&& pu.aout->a_syms < (filemax - filepos))
{
int symtab_err, orig_addr = cur_addr;
if (align_4k)
cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
mbi.syms.a.addr = cur_addr;
*((int *) RAW_ADDR (cur_addr)) = pu.aout->a_syms;
cur_addr += sizeof (int);
printf (", symtab=0x%x", pu.aout->a_syms);
if (grub_read ((char *) RAW_ADDR (cur_addr), pu.aout->a_syms)
== pu.aout->a_syms)
{
cur_addr += pu.aout->a_syms;
mbi.syms.a.tabsize = pu.aout->a_syms;
if (grub_read ((char *) &i, sizeof (int)) == sizeof (int))
{
*((int *) RAW_ADDR (cur_addr)) = i;
cur_addr += sizeof (int);
mbi.syms.a.strsize = i;
i -= sizeof (int);
printf (", strtab=0x%x", i);
symtab_err = (grub_read ((char *) RAW_ADDR (cur_addr), i)
!= i);
cur_addr += i;
}
else
symtab_err = 1;
}
else
symtab_err = 1;
if (symtab_err)
{
printf ("(bad)");
cur_addr = orig_addr;
mbi.syms.a.tabsize = 0;
mbi.syms.a.strsize = 0;
mbi.syms.a.addr = 0;
}
else
mbi.flags |= MB_INFO_AOUT_SYMS;
}
}
else
{
unsigned loaded = 0, memaddr, memsiz, filesiz;
Elf32_Phdr *phdr;
cur_addr = 0;
for (i = 0; i < pu.elf->e_phnum; i++)
{
phdr = (Elf32_Phdr *)
(pu.elf->e_phoff + ((int) buffer)
+ (pu.elf->e_phentsize * i));
if (phdr->p_type == PT_LOAD)
{
grub_seek (phdr->p_offset);
filesiz = phdr->p_filesz;
if (type == KERNEL_TYPE_FREEBSD || type == KERNEL_TYPE_NETBSD)
memaddr = RAW_ADDR (phdr->p_paddr & 0xFFFFFF);
else
memaddr = RAW_ADDR (phdr->p_paddr);
memsiz = phdr->p_memsz;
if (memaddr < RAW_ADDR (0x100000))
errnum = ERR_BELOW_1MB;
if (type == KERNEL_TYPE_MULTIBOOT
&& (unsigned) entry_addr >= phdr->p_vaddr
&& (unsigned) entry_addr < phdr->p_vaddr + memsiz)
real_entry_addr = (entry_func) ((unsigned) entry_addr
+ memaddr - phdr->p_vaddr);
if (filesiz > memsiz)
filesiz = memsiz;
if (cur_addr < memaddr + memsiz)
cur_addr = memaddr + memsiz;
printf (", <0x%x:0x%x:0x%x>", memaddr, filesiz,
memsiz - filesiz);
loaded++;
if (memcheck (memaddr, memsiz)
&& grub_read ((char *) memaddr, filesiz) == filesiz)
{
if (memsiz > filesiz)
memset ((char *) (memaddr + filesiz), 0, memsiz - filesiz);
}
else
break;
}
}
if (! errnum)
{
if (! loaded)
errnum = ERR_EXEC_FORMAT;
else
{
Elf32_Shdr *shdr = NULL;
int tab_size, sec_size;
int symtab_err = 0;
mbi.syms.e.num = pu.elf->e_shnum;
mbi.syms.e.size = pu.elf->e_shentsize;
mbi.syms.e.shndx = pu.elf->e_shstrndx;
if (align_4k)
cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
tab_size = pu.elf->e_shentsize * pu.elf->e_shnum;
grub_seek (pu.elf->e_shoff);
if (grub_read ((char *) (cur_addr), tab_size)
== tab_size)
{
mbi.syms.e.addr = cur_addr;
shdr = (Elf32_Shdr *) mbi.syms.e.addr;
cur_addr += tab_size;
printf (", shtab=0x%x", cur_addr);
for (i = 0; i < mbi.syms.e.num; i++)
{
if (shdr[i].sh_addr != 0)
continue;
if (shdr[i].sh_size == 0)
continue;
cur_addr = ((cur_addr + shdr[i].sh_addralign) &
- (int) shdr[i].sh_addralign);
grub_seek (shdr[i].sh_offset);
sec_size = shdr[i].sh_size;
if (! (memcheck (cur_addr, sec_size)
&& (grub_read ((char *) (cur_addr),
sec_size)
== sec_size)))
{
symtab_err = 1;
break;
}
shdr[i].sh_addr = cur_addr;
cur_addr += sec_size;
}
}
else
symtab_err = 1;
if (mbi.syms.e.addr < RAW_ADDR(0x10000))
symtab_err = 1;
if (symtab_err)
{
printf ("(bad)");
mbi.syms.e.num = 0;
mbi.syms.e.size = 0;
mbi.syms.e.addr = 0;
mbi.syms.e.shndx = 0;
cur_addr = 0;
}
else
mbi.flags |= MB_INFO_ELF_SHDR;
}
}
}
if (! errnum)
{
grub_printf (", entry=0x%x]\n", (unsigned) entry_addr);
if (real_entry_addr)
entry_addr = real_entry_addr;
}
else
{
putchar ('\n');
type = KERNEL_TYPE_NONE;
}
grub_close ();
if (suggested_type != KERNEL_TYPE_NONE && suggested_type != type)
{
errnum = ERR_EXEC_FORMAT;
return KERNEL_TYPE_NONE;
}
return type;
}
int
load_module (char *module, char *arg)
{
int len;
cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
if (!grub_open (module))
return 0;
len = grub_read ((char *) cur_addr, -1);
if (! len)
{
grub_close ();
return 0;
}
printf (" [Multiboot-module @ 0x%x, 0x%x bytes]\n", cur_addr, len);
mbi.flags |= MB_INFO_MODS;
mbi.mods_addr = (int) mll;
mll[mbi.mods_count].cmdline = (int) arg;
mll[mbi.mods_count].mod_start = cur_addr;
cur_addr += len;
mll[mbi.mods_count].mod_end = cur_addr;
mll[mbi.mods_count].pad = 0;
mbi.mods_count++;
grub_close ();
return 1;
}
int
load_initrd (char *initrd)
{
int len;
unsigned long moveto;
unsigned long max_addr;
struct linux_kernel_header *lh
= (struct linux_kernel_header *) (cur_addr - LINUX_SETUP_MOVE_SIZE);
#ifndef NO_DECOMPRESSION
no_decompression = 1;
#endif
if (! grub_open (initrd))
goto fail;
len = grub_read ((char *) cur_addr, -1);
if (! len)
{
grub_close ();
goto fail;
}
if (linux_mem_size)
moveto = linux_mem_size;
else
moveto = (mbi.mem_upper + 0x400) << 10;
moveto = (moveto - len) & 0xfffff000;
max_addr = (lh->header == LINUX_MAGIC_SIGNATURE && lh->version >= 0x0203
? lh->initrd_addr_max : LINUX_INITRD_MAX_ADDRESS);
if (moveto + len >= max_addr)
moveto = (max_addr - len) & 0xfffff000;
moveto -= 0x10000;
memmove ((void *) RAW_ADDR (moveto), (void *) cur_addr, len);
printf (" [Linux-initrd @ 0x%x, 0x%x bytes]\n", moveto, len);
lh->ramdisk_image = RAW_ADDR (moveto);
lh->ramdisk_size = len;
grub_close ();
fail:
#ifndef NO_DECOMPRESSION
no_decompression = 0;
#endif
return ! errnum;
}
#ifdef GRUB_UTIL
static void
bsd_boot_entry (int flags, int bootdev, int sym_start, int sym_end,
int mem_upper, int mem_lower)
{
stop ();
}
#endif
void
bsd_boot (kernel_t type, int bootdev, char *arg)
{
char *str;
int clval = 0, i;
struct bootinfo bi;
#ifdef GRUB_UTIL
entry_addr = (entry_func) bsd_boot_entry;
#else
stop_floppy ();
#endif
while (*(++arg) && *arg != ' ');
str = arg;
while (*str)
{
if (*str == '-')
{
while (*str && *str != ' ')
{
if (*str == 'C')
clval |= RB_CDROM;
if (*str == 'a')
clval |= RB_ASKNAME;
if (*str == 'b')
clval |= RB_HALT;
if (*str == 'c')
clval |= RB_CONFIG;
if (*str == 'd')
clval |= RB_KDB;
if (*str == 'D')
clval |= RB_MULTIPLE;
if (*str == 'g')
clval |= RB_GDB;
if (*str == 'h')
clval |= RB_SERIAL;
if (*str == 'm')
clval |= RB_MUTE;
if (*str == 'r')
clval |= RB_DFLTROOT;
if (*str == 's')
clval |= RB_SINGLE;
if (*str == 'v')
clval |= RB_VERBOSE;
str++;
}
continue;
}
str++;
}
if (type == KERNEL_TYPE_FREEBSD)
{
clval |= RB_BOOTINFO;
bi.bi_version = BOOTINFO_VERSION;
*arg = 0;
while ((--arg) > (char *) MB_CMDLINE_BUF && *arg != '/');
if (*arg == '/')
bi.bi_kernelname = arg + 1;
else
bi.bi_kernelname = 0;
bi.bi_nfs_diskless = 0;
bi.bi_n_bios_used = 0;
for (i = 0; i < N_BIOS_GEOM; i++)
{
struct geometry geom;
get_diskinfo (i + 0x80, &geom);
bi.bi_bios_geom[i] = (((geom.cylinders - 1) << 16)
+ (((geom.heads - 1) & 0xff) << 8)
+ (geom.sectors & 0xff));
}
bi.bi_size = sizeof (struct bootinfo);
bi.bi_memsizes_valid = 1;
bi.bi_bios_dev = saved_drive;
bi.bi_basemem = mbi.mem_lower;
bi.bi_extmem = extended_memory;
if (mbi.flags & MB_INFO_AOUT_SYMS)
{
bi.bi_symtab = mbi.syms.a.addr;
bi.bi_esymtab = mbi.syms.a.addr + 4
+ mbi.syms.a.tabsize + mbi.syms.a.strsize;
}
#if 0
else if (mbi.flags & MB_INFO_ELF_SHDR)
{
}
#endif
else
{
bi.bi_symtab = 0;
bi.bi_esymtab = 0;
}
(*entry_addr) (clval, bootdev, 0, 0, 0, ((int) (&bi)));
}
else
{
unsigned long end_mark;
if (mbi.flags & MB_INFO_AOUT_SYMS)
end_mark = (mbi.syms.a.addr + 4
+ mbi.syms.a.tabsize + mbi.syms.a.strsize);
else
end_mark = 0;
(*entry_addr) (clval, bootdev, 0, end_mark,
extended_memory, mbi.mem_lower);
}
}