forked from CS161/chickadee
-
Notifications
You must be signed in to change notification settings - Fork 0
/
boot.cc
115 lines (98 loc) · 3.81 KB
/
boot.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include "x86-64.h"
#include "elf.h"
// boot.c
//
// Chickadee boot loader. Loads the kernel from the first IDE hard disk.
//
// A BOOT LOADER is a tiny program that loads an operating system into
// memory. It has to be tiny because it can contain no more than 510 bytes
// of instructions: it is stored in the disk's first 512-byte sector.
//
// When the CPU boots it loads the BIOS into memory and executes it. The
// BIOS intializes devices and CPU state, reads the first 512-byte sector of
// the boot device (hard drive) into memory at address 0x7C00, and jumps to
// that address.
//
// The boot loader is contained in bootstart.S and boot.c. Control starts
// in bootstart.S, which initializes the CPU and sets up a stack, then
// transfers here. This code reads in the kernel image and calls the
// kernel.
//
// The main kernel is stored as a contiguous ELF executable image
// starting in the disk's sector KERNEL_START_SECTOR.
#define SECTORSIZE 512
#define ELFHDR ((elf_header*) 0x3000) // scratch space
#define KERNEL_START_SECTOR 128
extern "C" {
void boot(void) __attribute__((noreturn));
static void boot_readsect(uintptr_t dst, uint32_t src_sect);
static void boot_readseg(uintptr_t dst, uint32_t src_sect,
size_t filesz, size_t memsz);
}
// boot
// Load the kernel and jump to it.
void boot(void) {
// read 1st page off disk (should include programs as well as header)
// and check validity
boot_readseg((uintptr_t) ELFHDR, KERNEL_START_SECTOR,
PAGESIZE, PAGESIZE);
while (ELFHDR->e_magic != ELF_MAGIC) {
/* do nothing */
}
// load each program segment
elf_program* ph = (elf_program*) ((uint8_t*) ELFHDR + ELFHDR->e_phoff);
elf_program* eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ++ph) {
boot_readseg(ph->p_va,
KERNEL_START_SECTOR + ph->p_offset / SECTORSIZE,
ph->p_filesz, ph->p_memsz);
}
// jump to the kernel
typedef void (*kernel_entry_t)(void) __attribute__((noreturn));
kernel_entry_t kernel_entry = (kernel_entry_t) ELFHDR->e_entry;
kernel_entry();
}
// boot_readseg(dst, src_sect, filesz, memsz)
// Load an ELF segment at virtual address `dst` from the IDE disk's sector
// `src_sect`. Copies `filesz` bytes into memory at `dst` from sectors
// `src_sect` and up, then clears memory in the range
// `[dst+filesz, dst+memsz)`.
static void boot_readseg(uintptr_t ptr, uint32_t src_sect,
size_t filesz, size_t memsz) {
uintptr_t end_ptr = ptr + filesz;
memsz += ptr;
// round down to sector boundary
ptr &= ~(SECTORSIZE - 1);
// read sectors
for (; ptr < end_ptr; ptr += SECTORSIZE, ++src_sect) {
boot_readsect(ptr, src_sect);
}
// clear bss segment
for (; end_ptr < memsz; ++end_ptr) {
*(uint8_t*) end_ptr = 0;
}
}
// boot_waitdisk
// Wait for the disk to be ready.
static void boot_waitdisk(void) {
// Wait until the ATA status register says ready (0x40 is on)
// & not busy (0x80 is off)
while ((inb(0x1F7) & 0xC0) != 0x40) {
/* do nothing */
}
}
// boot_readsect(dst, src_sect)
// Read disk sector number `src_sect` into address `dst`.
static void boot_readsect(uintptr_t dst, uint32_t src_sect) {
// programmed I/O for "read sector"
boot_waitdisk();
outb(0x1F2, 1); // send `count = 1` as an ATA argument
outb(0x1F3, src_sect); // send `src_sect`, the sector number
outb(0x1F4, src_sect >> 8);
outb(0x1F5, src_sect >> 16);
outb(0x1F6, (src_sect >> 24) | 0xE0);
outb(0x1F7, 0x20); // send the command: 0x20 = read sectors
// then move the data into memory
boot_waitdisk();
insl(0x1F0, (void*) dst, SECTORSIZE/4); // read 128 words from the disk
}