blob: 5b68e3f739549a354c76fbac8a70abd20a4e6553 [file] [log] [blame]
#include <arch/x86.h>
#include <arch/arch.h>
#include <elf.h>
/**********************************************************************
* This a dirt simple boot loader, whose sole job is to boot
* an elf kernel image from the first IDE hard disk.
*
* DISK LAYOUT
* * This program(boot.S and main.c) is the bootloader. It should
* be stored in the first sector of the disk.
*
* * The 2nd sector onward holds the kernel image.
*
* * The kernel image must be in ELF format.
*
* BOOT UP STEPS
* * when the CPU boots it loads the BIOS into memory and executes it
*
* * the BIOS intializes devices, sets of the interrupt routines, and
* reads the first sector of the boot device(e.g., hard-drive)
* into memory and jumps to it.
*
* * Assuming this boot loader is stored in the first sector of the
* hard-drive, this code takes over...
*
* * control starts in bootloader.S -- which sets up protected mode,
* and a stack so C code then run, then calls cmain()
*
* * cmain() in this file takes over, reads in the kernel and jumps to it.
**********************************************************************/
#define SECTSIZE 512
#define ELFHDR ((elf_t *) 0x10000) // scratch space
void readsect(void*, uint32_t);
void readseg(uint32_t, uint32_t, uint32_t);
void cmain(void)
{
proghdr_t *ph, *eph;
// read 1st page off disk
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
// is this a valid ELF?
if (ELFHDR->e_magic != ELF_MAGIC)
goto bad;
// load each program segment (ignores ph flags)
ph = (proghdr_t *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ph++)
readseg(ph->p_va, ph->p_memsz, ph->p_offset);
// call the entry point from the ELF header
// note: does not return!
((void (*)(void)) (ELFHDR->e_entry & 0x0FFFFFFF))();
bad:
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
while (1)
/* do nothing */;
}
// Read 'count' bytes at 'offset' from kernel into virtual address 'va'.
// Might copy more than asked
void readseg(uint32_t va, uint32_t count, uint32_t offset)
{
uint32_t end_va;
va &= 0x0FFFFFFF;
end_va = va + count;
// round down to sector boundary
va &= ~(SECTSIZE - 1);
// translate from bytes to sectors, and kernel starts at sector 1
offset = (offset / SECTSIZE) + 1;
// If this is too slow, we could read lots of sectors at a time.
// We'd write more to memory than asked, but it doesn't matter --
// we load in increasing order.
while (va < end_va) {
readsect((uint8_t*) va, offset);
va += SECTSIZE;
offset++;
}
}
void waitdisk(void)
{
// wait for disk ready
while ((inb(0x1F7) & 0xC0) != 0x40)
/* do nothing */;
}
void readsect(void *dst, uint32_t offset)
{
// wait for disk to be ready
waitdisk();
/* the ISA uses a specified block of memory,
addresses 0x1F0-0x1F7, that can use the special
instructions inb/outb, as demonstrated in the
following code in order to access the disk
Offset is 28 bytes long
*/
outb(0x1F2, 1); // number of sectors to read
outb(0x1F3, offset); // bits 0-7 (low bits) of 28-bit offset
outb(0x1F4, offset >> 8); // bits 8-15 of 28-bit offset
outb(0x1F5, offset >> 16); // bits 16-23 of 28-bit offset
outb(0x1F6, (offset >> 24) | 0xE0); // bits 24-27 of 28-bit offset
// bit 28 (= 0) means Disk 0
// other bits (29-31) must be set to one
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
// wait for disk to be ready
waitdisk();
// read a sector
insl(0x1F0, dst, SECTSIZE/4);
}