blob: 3ff8f305fe1e0b0356227bee475f331b46bbcdb2 [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 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
Kevin O'Connor63ccc132008-03-12 20:57:08 -040014#include "ata.h"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050015
16#define bda ((struct bios_data_area_s *)0)
17#define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050018#define ipl ((struct ipl_s *)(IPL_SEG<<4))
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050019
Kevin O'Connor63dbcfb2008-03-01 22:17:07 -050020static u8
Kevin O'Connorbdce35f2008-02-26 21:33:14 -050021checksum(u8 *p, u32 len)
22{
23 u32 i;
24 u8 sum = 0;
25 for (i=0; i<len; i++)
26 sum += p[i];
27 return sum;
28}
29
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050030static void
31init_bda()
32{
33 memset(bda, 0, sizeof(*bda));
34
35 int i;
36 for (i=0; i<256; i++) {
Kevin O'Connore9061492008-03-11 21:54:18 -040037 SET_BDA(ivecs[i].seg, SEG_BIOS);
38 SET_BDA(ivecs[i].offset, OFFSET_dummy_iret_handler);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050039 }
40
Kevin O'Connore9061492008-03-11 21:54:18 -040041 SET_BDA(mem_size_kb, BASE_MEM_IN_K);
Kevin O'Connor9bd83912008-03-08 12:42:36 -050042
43 // mov CMOS Equipment Byte to BDA Equipment Word
Kevin O'Connore9061492008-03-11 21:54:18 -040044 SET_BDA(equipment_list_flags, inb_cmos(CMOS_EQUIPMENT_INFO));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050045}
46
47static void
48init_handlers()
49{
50 // set vector 0x79 to zero
51 // this is used by 'gardian angel' protection system
Kevin O'Connore9061492008-03-11 21:54:18 -040052 SET_BDA(ivecs[0x79].seg, 0);
53 SET_BDA(ivecs[0x79].offset, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050054
Kevin O'Connore9061492008-03-11 21:54:18 -040055 SET_BDA(ivecs[0x40].offset, OFFSET_entry_40);
56 SET_BDA(ivecs[0x0e].offset, OFFSET_entry_0e);
57 SET_BDA(ivecs[0x13].offset, OFFSET_entry_13);
58 SET_BDA(ivecs[0x76].offset, OFFSET_entry_76);
59 SET_BDA(ivecs[0x17].offset, OFFSET_entry_17);
60 SET_BDA(ivecs[0x18].offset, OFFSET_entry_18);
61 SET_BDA(ivecs[0x19].offset, OFFSET_entry_19);
62 SET_BDA(ivecs[0x1c].offset, OFFSET_entry_1c);
63 SET_BDA(ivecs[0x12].offset, OFFSET_entry_12);
64 SET_BDA(ivecs[0x11].offset, OFFSET_entry_11);
65 SET_BDA(ivecs[0x15].offset, OFFSET_entry_15);
66 SET_BDA(ivecs[0x08].offset, OFFSET_entry_08);
67 SET_BDA(ivecs[0x09].offset, OFFSET_entry_09);
68 SET_BDA(ivecs[0x16].offset, OFFSET_entry_16);
69 SET_BDA(ivecs[0x14].offset, OFFSET_entry_14);
70 SET_BDA(ivecs[0x1a].offset, OFFSET_entry_1a);
71 SET_BDA(ivecs[0x70].offset, OFFSET_entry_70);
72 SET_BDA(ivecs[0x74].offset, OFFSET_entry_74);
73 SET_BDA(ivecs[0x75].offset, OFFSET_entry_75);
74 SET_BDA(ivecs[0x10].offset, OFFSET_entry_10);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050075}
76
77static void
78init_ebda()
79{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050080 memset(ebda, 0, sizeof(*ebda));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050081 ebda->size = EBDA_SIZE;
Kevin O'Connore9061492008-03-11 21:54:18 -040082 SET_BDA(ebda_seg, EBDA_SEG);
83 SET_BDA(ivecs[0x41].seg, EBDA_SEG);
84 SET_BDA(ivecs[0x41].offset
85 , offsetof(struct extended_bios_data_area_s, fdpt0));
86 SET_BDA(ivecs[0x46].seg, EBDA_SEG);
87 SET_BDA(ivecs[0x41].offset
88 , offsetof(struct extended_bios_data_area_s, fdpt1));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050089}
90
91static void
92pit_setup()
93{
94 // timer0: binary count, 16bit count, mode 2
95 outb(0x34, PORT_PIT_MODE);
96 // maximum count of 0000H = 18.2Hz
97 outb(0x0, PORT_PIT_COUNTER0);
98 outb(0x0, PORT_PIT_COUNTER0);
99}
100
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500101//--------------------------------------------------------------------------
102// keyboard_panic
103//--------------------------------------------------------------------------
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500104static void
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500105keyboard_panic(u16 status)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500106{
Kevin O'Connore9061492008-03-11 21:54:18 -0400107 // If you're getting a 993 keyboard panic here,
108 // please see the comment in keyboard_init
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500109
Kevin O'Connore9061492008-03-11 21:54:18 -0400110 BX_PANIC("Keyboard error:%u\n",status);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500111}
112
113static void
Kevin O'Connorc77ee3a2008-03-08 12:28:49 -0500114kbd_flush(u8 code)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500115{
116 u16 max = 0xffff;
Kevin O'Connorc77ee3a2008-03-08 12:28:49 -0500117 while ((inb(PORT_PS2_STATUS) & 0x02) && (--max > 0))
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500118 outb(code, PORT_DIAG);
Kevin O'Connorc77ee3a2008-03-08 12:28:49 -0500119 if (!max && code != 0xff)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500120 keyboard_panic(code);
121}
122
Kevin O'Connorc77ee3a2008-03-08 12:28:49 -0500123static void
124kbd_waitdata(u8 code)
125{
126 u16 max = 0xffff;
127 while ( ((inb(PORT_PS2_STATUS) & 0x01) == 0) && (--max>0) )
128 outb(code, PORT_DIAG);
129 if (!max)
130 keyboard_panic(code);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500131}
132
133//--------------------------------------------------------------------------
134// keyboard_init
135//--------------------------------------------------------------------------
136// this file is based on LinuxBIOS implementation of keyboard.c
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500137static void
138keyboard_init()
139{
140 /* ------------------- Flush buffers ------------------------*/
141 /* Wait until buffer is empty */
Kevin O'Connorc77ee3a2008-03-08 12:28:49 -0500142 kbd_flush(0xff);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500143
144 /* flush incoming keys */
145 u16 max=0x2000;
146 while (--max > 0) {
147 outb(0x00, PORT_DIAG);
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500148 if (inb(PORT_PS2_STATUS) & 0x01) {
149 inb(PORT_PS2_DATA);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500150 max = 0x2000;
151 }
152 }
153
154 // Due to timer issues, and if the IPS setting is > 15000000,
155 // the incoming keys might not be flushed here. That will
156 // cause a panic a few lines below. See sourceforge bug report :
157 // [ 642031 ] FATAL: Keyboard RESET error:993
158
159 /* ------------------- controller side ----------------------*/
160 /* send cmd = 0xAA, self test 8042 */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500161 outb(0xaa, PORT_PS2_STATUS);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500162
163 kbd_flush(0x00);
164 kbd_waitdata(0x01);
165
166 /* read self-test result, 0x55 should be returned from 0x60 */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500167 if (inb(PORT_PS2_DATA) != 0x55)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500168 keyboard_panic(991);
169
170 /* send cmd = 0xAB, keyboard interface test */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500171 outb(0xab, PORT_PS2_STATUS);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500172
173 kbd_flush(0x10);
174 kbd_waitdata(0x11);
175
176 /* read keyboard interface test result, */
177 /* 0x00 should be returned form 0x60 */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500178 if (inb(PORT_PS2_DATA) != 0x00)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500179 keyboard_panic(992);
180
181 /* Enable Keyboard clock */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500182 outb(0xae, PORT_PS2_STATUS);
183 outb(0xa8, PORT_PS2_STATUS);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500184
185 /* ------------------- keyboard side ------------------------*/
186 /* reset kerboard and self test (keyboard side) */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500187 outb(0xff, PORT_PS2_DATA);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500188
189 kbd_flush(0x20);
190 kbd_waitdata(0x21);
191
192 /* keyboard should return ACK */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500193 if (inb(PORT_PS2_DATA) != 0xfa)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500194 keyboard_panic(993);
195
196 kbd_waitdata(0x31);
197
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500198 if (inb(PORT_PS2_DATA) != 0xaa)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500199 keyboard_panic(994);
200
201 /* Disable keyboard */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500202 outb(0xf5, PORT_PS2_DATA);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500203
204 kbd_flush(0x40);
205 kbd_waitdata(0x41);
206
207 /* keyboard should return ACK */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500208 if (inb(PORT_PS2_DATA) != 0xfa)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500209 keyboard_panic(995);
210
211 /* Write Keyboard Mode */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500212 outb(0x60, PORT_PS2_STATUS);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500213
214 kbd_flush(0x50);
215
216 /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500217 outb(0x61, PORT_PS2_DATA);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500218
219 kbd_flush(0x60);
220
221 /* Enable keyboard */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500222 outb(0xf4, PORT_PS2_DATA);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500223
224 kbd_flush(0x70);
225 kbd_waitdata(0x71);
226
227 /* keyboard should return ACK */
Kevin O'Connoree2fd7a2008-03-02 08:42:16 -0500228 if (inb(PORT_PS2_DATA) != 0xfa)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500229 keyboard_panic(996);
230
231 outb(0x77, PORT_DIAG);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500232}
233
234static void
235kbd_setup()
236{
Kevin O'Connore9061492008-03-11 21:54:18 -0400237 u16 x = offsetof(struct bios_data_area_s, kbd_buf) - 0x400;
238 SET_BDA(kbd_mode, 0x10);
239 SET_BDA(kbd_buf_head, x);
240 SET_BDA(kbd_buf_tail, x);
241 SET_BDA(kbd_buf_start_offset, x);
242
243 SET_BDA(kbd_buf_end_offset, x + sizeof(bda->kbd_buf));
244
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500245 keyboard_init();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500246}
247
248static u16
249detect_parport(u16 port, u8 timeout, u8 count)
250{
251 // clear input mode
252 outb(inb(port+2) & 0xdf, port+2);
253
254 outb(0xaa, port);
255 if (inb(port) != 0xaa)
256 // Not present
257 return 0;
Kevin O'Connore9061492008-03-11 21:54:18 -0400258 SET_BDA(port_lpt[count], port);
259 SET_BDA(lpt_timeout[count], timeout);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500260 return 1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500261}
262
263static void
264lpt_setup()
265{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500266 u16 count = 0;
267 count += detect_parport(0x378, 0x14, count);
268 count += detect_parport(0x278, 0x14, count);
269
270 // Equipment word bits 14..15 determing # parallel ports
Kevin O'Connore9061492008-03-11 21:54:18 -0400271 u16 eqb = GET_BDA(equipment_list_flags);
272 SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500273}
274
275static u16
276detect_serial(u16 port, u8 timeout, u8 count)
277{
278 outb(0x02, port+1);
279 if (inb(port+1) != 0x02)
280 return 0;
281 if (inb(port+2) != 0x02)
282 return 0;
283 outb(0x00, port+1);
Kevin O'Connore9061492008-03-11 21:54:18 -0400284 SET_BDA(port_com[count], port);
285 SET_BDA(com_timeout[count], timeout);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500286 return 1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500287}
288
289static void
290serial_setup()
291{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500292 u16 count = 0;
293 count += detect_serial(0x3f8, 0x0a, count);
294 count += detect_serial(0x2f8, 0x0a, count);
295 count += detect_serial(0x3e8, 0x0a, count);
296 count += detect_serial(0x2e8, 0x0a, count);
297
298 // Equipment word bits 9..11 determing # serial ports
Kevin O'Connore9061492008-03-11 21:54:18 -0400299 u16 eqb = GET_BDA(equipment_list_flags);
300 SET_BDA(equipment_list_flags, (eqb & 0xf1ff) | (count << 9));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500301}
302
303static u32
304bcd2bin(u8 val)
305{
306 return (val & 0xf) + ((val >> 4) * 10);
307}
308
309static void
310timer_setup()
311{
312 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
313 u32 ticks = (seconds * 18206507) / 1000000;
314 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
315 ticks += (minutes * 10923904) / 10000;
316 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
317 ticks += (hours * 65543427) / 1000;
Kevin O'Connore9061492008-03-11 21:54:18 -0400318 SET_BDA(timer_counter, ticks);
319 SET_BDA(timer_rollover, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500320}
321
322static void
323pic_setup()
324{
325 outb(0x11, PORT_PIC1);
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500326 outb(0x11, PORT_PIC2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500327 outb(0x08, PORT_PIC1_DATA);
328 outb(0x70, PORT_PIC2_DATA);
329 outb(0x04, PORT_PIC1_DATA);
330 outb(0x02, PORT_PIC2_DATA);
331 outb(0x01, PORT_PIC1_DATA);
332 outb(0x01, PORT_PIC2_DATA);
333 outb(0xb8, PORT_PIC1_DATA);
334 if (CONFIG_PS2_MOUSE)
335 outb(0x8f, PORT_PIC2_DATA);
336 else
337 outb(0x9f, PORT_PIC2_DATA);
338}
339
340static void
341floppy_drive_post()
342{
343 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
344 u8 out = 0;
345 if (type & 0xf0)
346 out |= 0x07;
347 if (type & 0x0f)
348 out |= 0x70;
Kevin O'Connore9061492008-03-11 21:54:18 -0400349 SET_BDA(floppy_harddisk_info, out);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500350 outb(0x02, PORT_DMA1_MASK_REG);
351
Kevin O'Connore9061492008-03-11 21:54:18 -0400352 SET_BDA(ivecs[0x1E].offset, OFFSET_diskette_param_table2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500353}
354
355static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500356ata_init()
357{
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500358 // hdidmap and cdidmap init.
359 u8 device;
360 for (device=0; device < CONFIG_MAX_ATA_DEVICES; device++) {
Kevin O'Connor180a9592008-03-04 22:50:53 -0500361 ebda->ata.idmap[0][device] = CONFIG_MAX_ATA_DEVICES;
362 ebda->ata.idmap[1][device] = CONFIG_MAX_ATA_DEVICES;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500363 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500364}
365
366static void
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500367fill_hdinfo(struct fdpt_s *info, u8 typecmos, u8 basecmos)
368{
369 u8 type = inb_cmos(typecmos);
370 if (type != 47)
371 // XXX - halt
372 return;
373
374 info->precompensation = (inb_cmos(basecmos+4) << 8) | inb_cmos(basecmos+3);
375 info->drive_control_byte = inb_cmos(basecmos+5);
376 info->landing_zone = (inb_cmos(basecmos+7) << 8) | inb_cmos(basecmos+6);
377 u16 cyl = (inb_cmos(basecmos+1) << 8) | inb_cmos(basecmos+0);
378 u8 heads = inb_cmos(basecmos+2);
379 u8 sectors = inb_cmos(basecmos+8);
380 if (cyl < 1024) {
381 // no logical CHS mapping used, just physical CHS
382 // use Standard Fixed Disk Parameter Table (FDPT)
383 info->cylinders = cyl;
384 info->heads = heads;
385 info->sectors = sectors;
386 return;
387 }
388
389 // complies with Phoenix style Translated Fixed Disk Parameter
390 // Table (FDPT)
391 info->phys_cylinders = cyl;
392 info->phys_heads = heads;
393 info->phys_sectors = sectors;
394 info->sectors = sectors;
395 info->a0h_signature = 0xa0;
396 if (cyl > 8192) {
397 cyl >>= 4;
398 heads <<= 4;
399 } else if (cyl > 4096) {
400 cyl >>= 3;
401 heads <<= 3;
402 } else if (cyl > 2048) {
403 cyl >>= 2;
404 heads <<= 2;
405 }
406 info->cylinders = cyl;
407 info->heads = heads;
408 info->checksum = ~checksum((u8*)info, sizeof(*info)-1) + 1;
409}
410
411static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500412hard_drive_post()
413{
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500414 outb(0x0a, 0x03f6); // 0000 1010 = reserved, disable IRQ 14
Kevin O'Connore9061492008-03-11 21:54:18 -0400415 SET_BDA(disk_count, 1);
416 SET_BDA(disk_control_byte, 0xc0);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500417
418 // move disk geometry data from CMOS to EBDA disk parameter table(s)
419 u8 diskinfo = inb_cmos(CMOS_DISK_DATA);
420 if ((diskinfo & 0xf0) == 0xf0)
421 // Fill EBDA table for hard disk 0.
422 fill_hdinfo(&ebda->fdpt0, CMOS_DISK_DRIVE1_TYPE, CMOS_DISK_DRIVE1_CYL);
423 if ((diskinfo & 0x0f) == 0x0f)
424 // XXX - bochs halts on any other type
425 // Fill EBDA table for hard disk 1.
426 fill_hdinfo(&ebda->fdpt0, CMOS_DISK_DRIVE2_TYPE, CMOS_DISK_DRIVE2_CYL);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500427}
428
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500429
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500430static void
431init_boot_vectors()
432{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500433 // Clear out the IPL table.
434 memset(ipl, 0, sizeof(*ipl));
435
436 // Floppy drive
437 struct ipl_entry_s *ip = &ipl->table[0];
438 ip->type = IPL_TYPE_FLOPPY;
439 ip++;
440
441 // First HDD
442 ip->type = IPL_TYPE_HARDDISK;
443 ip++;
444
445 // CDROM
Kevin O'Connor180a9592008-03-04 22:50:53 -0500446 if (CONFIG_CDROM_BOOT) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500447 ip->type = IPL_TYPE_CDROM;
448 ip++;
449 }
450
451 ipl->count = ip - ipl->table;
452 ipl->sequence = 0xffff;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500453}
454
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500455static void
456callrom(u16 seg, u16 offset)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500457{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500458 struct bregs br;
459 memset(&br, 0, sizeof(br));
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500460 br.es = SEG_BIOS;
461 br.di = OFFSET_pnp_string + 1; // starts 1 past for alignment
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500462 br.cs = seg;
463 br.ip = offset;
464 call16(&br);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500465}
466
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500467static void
Kevin O'Connora2e73802008-02-27 10:27:00 -0500468rom_scan(u32 start, u32 end)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500469{
Kevin O'Connora2e73802008-02-27 10:27:00 -0500470 u8 *p = (u8*)start;
471 for (; p <= (u8*)end; p += 2048) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500472 u8 *rom = p;
473 if (*(u16*)rom != 0xaa55)
474 continue;
475 u32 len = rom[2] * 512;
476 if (checksum(rom, len) != 0)
477 continue;
478 p = (u8*)(((u32)p + len) / 2048 * 2048);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500479 callrom(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500480
481 // Look at the ROM's PnP Expansion header. Properly, we're supposed
482 // to init all the ROMs and then go back and build an IPL table of
483 // all the bootable devices, but we can get away with one pass.
484 if (rom[0x1a] != '$' || rom[0x1b] != 'P'
485 || rom[0x1c] != 'n' || rom[0x1d] != 'P')
486 continue;
487 // 0x1A is also the offset into the expansion header of...
488 // the Bootstrap Entry Vector, or zero if there is none.
489 u16 entry = *(u16*)&rom[0x1a+0x1a];
490 if (!entry)
491 continue;
492 // Found a device that thinks it can boot the system. Record
493 // its BEV and product name string.
494
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500495 if (ipl->count >= ARRAY_SIZE(ipl->table))
496 continue;
497
498 struct ipl_entry_s *ip = &ipl->table[ipl->count];
499 ip->type = IPL_TYPE_BEV;
500 ip->vector = (PTR_TO_SEG(rom) << 16) | entry;
501
502 u16 desc = *(u16*)&rom[0x1a+0x10];
503 if (desc)
504 ip->description = (PTR_TO_SEG(rom) << 16) | desc;
505
506 ipl->count++;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500507 }
508}
509
510static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500511post()
512{
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500513 BX_INFO("Start bios\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500514
515 init_bda();
516 init_handlers();
517 init_ebda();
518
519 pit_setup();
520 kbd_setup();
521 lpt_setup();
522 serial_setup();
523 timer_setup();
524 pic_setup();
Kevin O'Connora2e73802008-02-27 10:27:00 -0500525
526 rom_scan(0xc0000, 0xc7800);
527
528 printf("BIOS - begin\n\n");
529
Kevin O'Connora4d35762008-03-08 15:43:03 -0500530 rombios32_init();
531
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500532 init_boot_vectors();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500533
534 floppy_drive_post();
535 hard_drive_post();
Kevin O'Connor63ccc132008-03-12 20:57:08 -0400536 if (CONFIG_ATA) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500537 ata_init();
Kevin O'Connor63ccc132008-03-12 20:57:08 -0400538 ata_detect();
539 }
Kevin O'Connora2e73802008-02-27 10:27:00 -0500540
541 init_boot_vectors();
Kevin O'Connora4d35762008-03-08 15:43:03 -0500542
Kevin O'Connora2e73802008-02-27 10:27:00 -0500543 rom_scan(0xc8000, 0xe0000);
544
Kevin O'Connor157e2132008-03-09 13:32:03 -0400545 // reset the memory (some boot loaders such as syslinux suppose
546 // that the memory is set to zero)
547 memset((void*)0x40000, 0, 0x40000); // XXX - shouldn't use globals
548
Kevin O'Connor63ccc132008-03-12 20:57:08 -0400549 // Invoke int 19 to start boot process.
550 struct bregs br;
551 memset(&br, 0, sizeof(br));
552 call16_int(0x19, &br);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500553}
554
Kevin O'Connor95576e72008-03-01 09:57:51 -0500555static void
556init_dma()
557{
558 // first reset the DMA controllers
559 outb(0, PORT_DMA1_MASTER_CLEAR);
560 outb(0, PORT_DMA2_MASTER_CLEAR);
561
562 // then initialize the DMA controllers
563 outb(0xc0, PORT_DMA2_MODE_REG);
564 outb(0x00, PORT_DMA2_MASK_REG);
565}
566
567static void
568check_restart_status()
569{
570 // Get and then clear CMOS shutdown status.
571 u8 status = inb_cmos(CMOS_RESET_CODE);
572 outb_cmos(0, CMOS_RESET_CODE);
573
574 if (status == 0x00 || status == 0x09 || status >= 0x0d)
575 // Normal post
576 return;
577
Kevin O'Connorad4ec342008-03-02 23:25:11 -0500578 if (status != 0x05) {
579 BX_PANIC("Unimplemented shutdown status: %02x\n", status);
580 return;
581 }
Kevin O'Connor95576e72008-03-01 09:57:51 -0500582
Kevin O'Connorad4ec342008-03-02 23:25:11 -0500583 // XXX - this is supposed to jump without changing any memory -
584 // but the stack has been altered by the time the code gets here.
585 eoi_both_pics();
586 struct bregs br;
587 memset(&br, 0, sizeof(br));
Kevin O'Connore9061492008-03-11 21:54:18 -0400588 br.cs = GET_BDA(jump_cs_ip) >> 16;
589 br.ip = GET_BDA(jump_cs_ip);
Kevin O'Connorad4ec342008-03-02 23:25:11 -0500590 call16(&br);
Kevin O'Connor95576e72008-03-01 09:57:51 -0500591}
592
Kevin O'Connor19786762008-03-05 21:09:59 -0500593void VISIBLE32
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500594_start()
595{
Kevin O'Connor95576e72008-03-01 09:57:51 -0500596 init_dma();
597 check_restart_status();
598
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500599 post();
600}