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