Initial checkin.
[seabios.git] / src / post.c
1 // 32bit code to Power On Self Test (POST) a machine.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "ioport.h" // PORT_*
9 #include "../out/rom16.offset.auto.h" // OFFSET_*
10 #include "config.h" // CONFIG_*
11 #include "cmos.h" // CMOS_*
12 #include "util.h" // memset
13 #include "biosvar.h" // struct bios_data_area_s
14
15 #define bda ((struct bios_data_area_s *)0)
16 #define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
17
18 static void
19 init_bda()
20 {
21     memset(bda, 0, sizeof(*bda));
22
23     int i;
24     for (i=0; i<256; i++) {
25         bda->ivecs[i].seg = 0xf000;
26         bda->ivecs[i].offset = OFFSET_dummy_iret_handler;
27     }
28
29     bda->mem_size_kb = BASE_MEM_IN_K;
30 }
31
32 static void
33 init_handlers()
34 {
35     // set vector 0x79 to zero
36     // this is used by 'gardian angel' protection system
37     bda->ivecs[0x79].seg = 0;
38     bda->ivecs[0x79].offset = 0;
39
40     bda->ivecs[0x40].offset = OFFSET_entry_40;
41     bda->ivecs[0x0e].offset = OFFSET_entry_0e;
42     bda->ivecs[0x13].offset = OFFSET_entry_13;
43     bda->ivecs[0x76].offset = OFFSET_entry_76;
44     bda->ivecs[0x17].offset = OFFSET_entry_17;
45     bda->ivecs[0x18].offset = OFFSET_entry_18;
46     bda->ivecs[0x19].offset = OFFSET_entry_19;
47     bda->ivecs[0x1c].offset = OFFSET_entry_1c;
48     bda->ivecs[0x12].offset = OFFSET_entry_12;
49     bda->ivecs[0x11].offset = OFFSET_entry_11;
50     bda->ivecs[0x15].offset = OFFSET_entry_15;
51     bda->ivecs[0x08].offset = OFFSET_entry_08;
52     bda->ivecs[0x09].offset = OFFSET_entry_09;
53     bda->ivecs[0x16].offset = OFFSET_entry_16;
54     bda->ivecs[0x14].offset = OFFSET_entry_14;
55     bda->ivecs[0x1a].offset = OFFSET_entry_1a;
56     bda->ivecs[0x70].offset = OFFSET_entry_70;
57     bda->ivecs[0x74].offset = OFFSET_entry_74;
58     bda->ivecs[0x75].offset = OFFSET_entry_75;
59     bda->ivecs[0x10].offset = OFFSET_entry_10;
60 }
61
62 static void
63 init_ebda()
64 {
65     ebda->size = EBDA_SIZE;
66     bda->ebda_seg = EBDA_SEG;
67     bda->ivecs[0x41].seg = EBDA_SEG;
68     bda->ivecs[0x41].offset = 0x3d; // XXX
69     bda->ivecs[0x46].seg = EBDA_SEG;
70     bda->ivecs[0x46].offset = 0x4d; // XXX
71 }
72
73 static void
74 pit_setup()
75 {
76     // timer0: binary count, 16bit count, mode 2
77     outb(0x34, PORT_PIT_MODE);
78     // maximum count of 0000H = 18.2Hz
79     outb(0x0, PORT_PIT_COUNTER0);
80     outb(0x0, PORT_PIT_COUNTER0);
81 }
82
83 static void
84 kbd_init()
85 {
86 }
87
88 static void
89 kbd_setup()
90 {
91     bda->kbd_mode = 0x10;
92     bda->kbd_buf_head = bda->kbd_buf_tail = offsetof(struct bios_data_area_s, kbd_buf);
93     bda->kbd_buf_start_offset = offsetof(struct bios_data_area_s, kbd_buf);
94     bda->kbd_buf_end_offset = offsetof(struct bios_data_area_s, kbd_buf[sizeof(bda->kbd_buf)]);
95     kbd_init();
96
97     // XXX
98     u16 eqb = bda->equipment_list_flags;
99     eqb = (eqb & 0xff00) | inb_cmos(CMOS_EQUIPMENT_INFO);
100     bda->equipment_list_flags = eqb;
101 }
102
103 static void
104 lpt_setup()
105 {
106     // XXX
107 }
108
109 static void
110 serial_setup()
111 {
112     // XXX
113 }
114
115 static u32
116 bcd2bin(u8 val)
117 {
118     return (val & 0xf) + ((val >> 4) * 10);
119 }
120
121 static void
122 timer_setup()
123 {
124     u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
125     u32 ticks = (seconds * 18206507) / 1000000;
126     u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
127     ticks += (minutes * 10923904) / 10000;
128     u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
129     ticks += (hours * 65543427) / 1000;
130     bda->timer_counter = ticks;
131     bda->timer_rollover = 0;
132 }
133
134 static void
135 pic_setup()
136 {
137     outb(0x11, PORT_PIC1);
138     outb(0x11, PORT_PIC2_DATA);
139     outb(0x08, PORT_PIC1_DATA);
140     outb(0x70, PORT_PIC2_DATA);
141     outb(0x04, PORT_PIC1_DATA);
142     outb(0x02, PORT_PIC2_DATA);
143     outb(0x01, PORT_PIC1_DATA);
144     outb(0x01, PORT_PIC2_DATA);
145     outb(0xb8, PORT_PIC1_DATA);
146     if (CONFIG_PS2_MOUSE)
147         outb(0x8f, PORT_PIC2_DATA);
148     else
149         outb(0x9f, PORT_PIC2_DATA);
150 }
151
152 static void
153 floppy_drive_post()
154 {
155     u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
156     u8 out = 0;
157     if (type & 0xf0)
158         out |= 0x07;
159     if (type & 0x0f)
160         out |= 0x70;
161     bda->floppy_harddisk_info = out;
162     outb(0x02, PORT_DMA1_MASK_REG);
163
164     bda->ivecs[0x1E].offset = OFFSET_diskette_param_table2;
165 }
166
167 static void
168 cdemu_init()
169 {
170     //ebda->cdemu.active = 0;
171 }
172
173 static void
174 ata_init()
175 {
176 }
177
178 static void
179 ata_detect()
180 {
181 }
182
183 static void
184 hard_drive_post()
185 {
186 }
187
188 static void
189 init_boot_vectors()
190 {
191 }
192
193 static void __attribute__((noinline))
194 call16(u16 seg, u16 offset)
195 {
196     u32 segoff = (seg << 16) | offset;
197     asm volatile(
198         "pushal\n"  // Save registers
199         "ljmp $0x20, %0\n" // Jump to 16bit transition code
200         ".globl call16_resume\n"
201         "call16_resume:\n"  // point of return
202         "popal\n"   // restore registers
203         : : "Z" (OFFSET_call16), "b" (segoff));
204 }
205
206 static int
207 checksum(u8 *p, u32 len)
208 {
209     u32 i;
210     u8 sum = 0;
211     for (i=0; i<len; i++)
212         sum += p[i];
213     return sum;
214 }
215
216 #define PTR_TO_SEG(p) ((((u32)(p)) >> 4) & 0xf000)
217 #define PTR_TO_OFFSET(p) (((u32)(p)) & 0xffff)
218
219 static void
220 rom_scan()
221 {
222     u8 *p = (u8*)0xc0000;
223     for (; p <= (u8*)0xe0000; p += 2048) {
224         u8 *rom = p;
225         if (*(u16*)rom != 0xaa55)
226             continue;
227         u32 len = rom[2] * 512;
228         if (checksum(rom, len) != 0)
229             continue;
230         p = (u8*)(((u32)p + len) / 2048 * 2048);
231         call16(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
232
233         // Look at the ROM's PnP Expansion header.  Properly, we're supposed
234         // to init all the ROMs and then go back and build an IPL table of
235         // all the bootable devices, but we can get away with one pass.
236         if (rom[0x1a] != '$' || rom[0x1b] != 'P'
237             || rom[0x1c] != 'n' || rom[0x1d] != 'P')
238             continue;
239         // 0x1A is also the offset into the expansion header of...
240         // the Bootstrap Entry Vector, or zero if there is none.
241         u16 entry = *(u16*)&rom[0x1a+0x1a];
242         if (!entry)
243             continue;
244         // Found a device that thinks it can boot the system.  Record
245         // its BEV and product name string.
246
247         // XXX
248     }
249 }
250
251 static void
252 status_restart(u8 status)
253 {
254 #if 0
255     if (status == 0x05)
256         eoi_jmp_post();
257 #endif
258
259     BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
260 }
261
262 static void
263 post()
264 {
265     // first reset the DMA controllers
266     outb(0, PORT_DMA1_MASTER_CLEAR);
267     outb(0, PORT_DMA2_MASTER_CLEAR);
268
269     // then initialize the DMA controllers
270     outb(0xc0, PORT_DMA2_MODE_REG);
271     outb(0x00, PORT_DMA2_MASK_REG);
272
273     // Get and then clear CMOS shutdown status.
274     u8 status = inb_cmos(CMOS_RESET_CODE);
275     outb_cmos(0, CMOS_RESET_CODE);
276
277     if (status != 0x00 && status != 0x09 && status < 0x0d)
278         status_restart(status);
279
280     BX_INFO("Start bios");
281
282     init_bda();
283     init_handlers();
284     init_ebda();
285
286     pit_setup();
287     kbd_setup();
288     lpt_setup();
289     serial_setup();
290     timer_setup();
291     pic_setup();
292     //pci_setup();
293     init_boot_vectors();
294     rom_scan();
295
296     printf("BIOS - begin\n\n");
297
298     floppy_drive_post();
299     hard_drive_post();
300     if (CONFIG_ATA) {
301         ata_init();
302         ata_detect();
303     }
304     cdemu_init();
305     call16(0xf000, OFFSET_begin_boot);
306 }
307
308 void VISIBLE
309 _start()
310 {
311     post();
312 }