blob: 4271bd52b370c5a0c26d5d0fc9665e8263defec1 [file] [log] [blame]
/* Support for Multiboot */
#include "config.h"
#include "asm/io.h"
#include "libopenbios/sys_info.h"
#include "multiboot.h"
#define printf printk
#ifdef CONFIG_DEBUG_BOOT
#define debug printk
#else
#define debug(x...)
#endif
struct mbheader {
unsigned int magic, flags, checksum;
};
const struct mbheader multiboot_header
__attribute__((section (".hdr"))) =
{
MULTIBOOT_HEADER_MAGIC,
MULTIBOOT_HEADER_FLAGS,
-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
};
/* Multiboot information structure, provided by loader to us */
struct multiboot_mmap {
unsigned entry_size;
unsigned base_lo, base_hi;
unsigned size_lo, size_hi;
unsigned type;
};
#define MULTIBOOT_MEM_VALID 0x01
#define MULTIBOOT_BOOT_DEV_VALID 0x02
#define MULTIBOOT_CMDLINE_VALID 0x04
#define MULTIBOOT_MODS_VALID 0x08
#define MULTIBOOT_AOUT_SYMS_VALID 0x10
#define MULTIBOOT_ELF_SYMS_VALID 0x20
#define MULTIBOOT_MMAP_VALID 0x40
void collect_multiboot_info(struct sys_info *info);
void collect_multiboot_info(struct sys_info *info)
{
struct multiboot_info *mbinfo;
struct multiboot_mmap *mbmem;
unsigned mbcount, mbaddr;
int i;
struct memrange *mmap;
int mmap_count;
module_t *mod;
if (info->boot_type != 0x2BADB002)
return;
debug("Using Multiboot information at %#lx\n", info->boot_data);
mbinfo = phys_to_virt(info->boot_data);
if (mbinfo->mods_count != 1) {
printf("Multiboot: no dictionary\n");
return;
}
mod = (module_t *) mbinfo->mods_addr;
info->dict_start=(unsigned long *)mod->mod_start;
info->dict_end=(unsigned long *)mod->mod_end;
if (mbinfo->flags & MULTIBOOT_MMAP_VALID) {
/* convert mmap records */
mbmem = phys_to_virt(mbinfo->mmap_addr);
mbcount = mbinfo->mmap_length / (mbmem->entry_size + 4);
mmap = malloc(mbcount * sizeof(struct memrange));
mmap_count = 0;
mbaddr = mbinfo->mmap_addr;
for (i = 0; i < mbcount; i++) {
mbmem = phys_to_virt(mbaddr);
debug("%08x%08x %08x%08x (%d)\n",
mbmem->base_hi,
mbmem->base_lo,
mbmem->size_hi,
mbmem->size_lo,
mbmem->type);
if (mbmem->type == 1) { /* Only normal RAM */
mmap[mmap_count].base = mbmem->base_lo
+ (((unsigned long long) mbmem->base_hi) << 32);
mmap[mmap_count].size = mbmem->size_lo
+ (((unsigned long long) mbmem->size_hi) << 32);
mmap_count++;
}
mbaddr += mbmem->entry_size + 4;
if (mbaddr >= mbinfo->mmap_addr + mbinfo->mmap_length)
break;
}
/* simple sanity check - there should be at least 2 RAM segments
* (base 640k and extended) */
if (mmap_count >= 2)
goto got_it;
printf("Multiboot mmap is broken\n");
free(mmap);
/* fall back to mem_lower/mem_upper */
}
if (mbinfo->flags & MULTIBOOT_MEM_VALID) {
/* use mem_lower and mem_upper */
mmap_count = 2;
mmap = malloc(2 * sizeof(*mmap));
mmap[0].base = 0;
mmap[0].size = mbinfo->mem_lower << 10;
mmap[1].base = 1 << 20; /* 1MB */
mmap[1].size = mbinfo->mem_upper << 10;
goto got_it;
}
printf("Can't get memory information from Multiboot\n");
return;
got_it:
info->memrange = mmap;
info->n_memranges = mmap_count;
return;
}