First run at an E1000 driver, with various LWIP fixes.
[akaros.git] / kern / arch / i686 / boot / main.c
1 #ifdef __DEPUTY__
2 #pragma nodeputy
3 #endif
4
5 #include <arch/x86.h>
6 #include <arch/arch.h>
7 #include <elf.h>
8
9 /**********************************************************************
10  * This a dirt simple boot loader, whose sole job is to boot
11  * an elf kernel image from the first IDE hard disk.
12  *
13  * DISK LAYOUT
14  *  * This program(boot.S and main.c) is the bootloader.  It should
15  *    be stored in the first sector of the disk.
16  * 
17  *  * The 2nd sector onward holds the kernel image.
18  *      
19  *  * The kernel image must be in ELF format.
20  *
21  * BOOT UP STEPS        
22  *  * when the CPU boots it loads the BIOS into memory and executes it
23  *
24  *  * the BIOS intializes devices, sets of the interrupt routines, and
25  *    reads the first sector of the boot device(e.g., hard-drive) 
26  *    into memory and jumps to it.
27  *
28  *  * Assuming this boot loader is stored in the first sector of the
29  *    hard-drive, this code takes over...
30  *
31  *  * control starts in bootloader.S -- which sets up protected mode,
32  *    and a stack so C code then run, then calls cmain()
33  *
34  *  * cmain() in this file takes over, reads in the kernel and jumps to it.
35  **********************************************************************/
36
37 #define SECTSIZE        512
38 #define ELFHDR          ((elf_t *) 0x10000) // scratch space
39
40 void readsect(void*, uint32_t);
41 void readseg(uint32_t, uint32_t, uint32_t);
42
43 void
44 cmain(void)
45 {
46         proghdr_t *ph, *eph;
47
48         // read 1st page off disk
49         readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
50
51         // is this a valid ELF?
52         if (ELFHDR->e_magic != ELF_MAGIC)
53                 goto bad;
54
55         // load each program segment (ignores ph flags)
56         ph = (proghdr_t *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
57         eph = ph + ELFHDR->e_phnum;
58         for (; ph < eph; ph++)
59                 readseg(ph->p_va, ph->p_memsz, ph->p_offset);
60
61         // call the entry point from the ELF header
62         // note: does not return!
63         ((void (*)(void)) (ELFHDR->e_entry & 0x0FFFFFFF))();
64
65 bad:
66         outw(0x8A00, 0x8A00);
67         outw(0x8A00, 0x8E00);
68         while (1)
69                 /* do nothing */;
70 }
71
72 // Read 'count' bytes at 'offset' from kernel into virtual address 'va'.
73 // Might copy more than asked
74 void
75 readseg(uint32_t va, uint32_t count, uint32_t offset)
76 {
77         uint32_t end_va;
78
79         va &= 0x0FFFFFFF;
80         end_va = va + count;
81         
82         // round down to sector boundary
83         va &= ~(SECTSIZE - 1);
84
85         // translate from bytes to sectors, and kernel starts at sector 1
86         offset = (offset / SECTSIZE) + 1;
87
88         // If this is too slow, we could read lots of sectors at a time.
89         // We'd write more to memory than asked, but it doesn't matter --
90         // we load in increasing order.
91         while (va < end_va) {
92                 readsect((uint8_t*) va, offset);
93                 va += SECTSIZE;
94                 offset++;
95         }
96 }
97
98 void
99 waitdisk(void)
100 {
101         // wait for disk ready
102         while ((inb(0x1F7) & 0xC0) != 0x40)
103                 /* do nothing */;
104 }
105
106 void
107 readsect(void *dst, uint32_t offset)
108 {
109         // wait for disk to be ready
110         waitdisk();
111
112         /* the ISA uses a specified block of memory, 
113            addresses 0x1F0-0x1F7, that can use the special 
114            instructions inb/outb, as demonstrated in the 
115            following code in order to access the disk
116            Offset is 28 bytes long
117         */
118
119         outb(0x1F2, 1);                         // number of sectors to read
120         outb(0x1F3, offset);                    // bits 0-7 (low bits) of 28-bit offset
121         outb(0x1F4, offset >> 8);               // bits 8-15 of 28-bit offset
122         outb(0x1F5, offset >> 16);              // bits 16-23 of 28-bit offset
123         outb(0x1F6, (offset >> 24) | 0xE0);     // bits 24-27 of 28-bit offset
124                                                 // bit 28 (= 0) means Disk 0
125                                                 // other bits (29-31) must be set to one
126         outb(0x1F7, 0x20);                      // cmd 0x20 - read sectors
127
128         // wait for disk to be ready
129         waitdisk();
130
131         // read a sector
132         insl(0x1F0, dst, SECTSIZE/4);
133 }
134