Rename VISIBLE macro for better control.
[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 #include "acpi.h"
16 #include "smbios.h"
17 #include "smp.h"
18 #include "pci.h"
19
20 u32 cpuid_signature;
21 u32 cpuid_features;
22 u32 cpuid_ext_features;
23 unsigned long ram_size;
24 unsigned long bios_table_cur_addr;
25 unsigned long bios_table_end_addr;
26
27 #ifdef CONFIG_USE_EBDA_TABLES
28 unsigned long ebda_cur_addr;
29 #endif
30
31 #define cpuid(index, eax, ebx, ecx, edx) \
32   asm volatile ("cpuid" \
33                 : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \
34                 : "0" (index))
35
36 #define bda ((struct bios_data_area_s *)0)
37 #define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
38 #define ipl ((struct ipl_s *)(IPL_SEG<<4))
39
40 static u8
41 checksum(u8 *p, u32 len)
42 {
43     u32 i;
44     u8 sum = 0;
45     for (i=0; i<len; i++)
46         sum += p[i];
47     return sum;
48 }
49
50 static void
51 init_bda()
52 {
53     memset(bda, 0, sizeof(*bda));
54
55     int i;
56     for (i=0; i<256; i++) {
57         bda->ivecs[i].seg = SEG_BIOS;
58         bda->ivecs[i].offset = OFFSET_dummy_iret_handler;
59     }
60
61     bda->mem_size_kb = BASE_MEM_IN_K;
62 }
63
64 static void
65 init_handlers()
66 {
67     // set vector 0x79 to zero
68     // this is used by 'gardian angel' protection system
69     bda->ivecs[0x79].seg = 0;
70     bda->ivecs[0x79].offset = 0;
71
72     bda->ivecs[0x40].offset = OFFSET_entry_40;
73     bda->ivecs[0x0e].offset = OFFSET_entry_0e;
74     bda->ivecs[0x13].offset = OFFSET_entry_13;
75     bda->ivecs[0x76].offset = OFFSET_entry_76;
76     bda->ivecs[0x17].offset = OFFSET_entry_17;
77     bda->ivecs[0x18].offset = OFFSET_entry_18;
78     bda->ivecs[0x19].offset = OFFSET_entry_19;
79     bda->ivecs[0x1c].offset = OFFSET_entry_1c;
80     bda->ivecs[0x12].offset = OFFSET_entry_12;
81     bda->ivecs[0x11].offset = OFFSET_entry_11;
82     bda->ivecs[0x15].offset = OFFSET_entry_15;
83     bda->ivecs[0x08].offset = OFFSET_entry_08;
84     bda->ivecs[0x09].offset = OFFSET_entry_09;
85     bda->ivecs[0x16].offset = OFFSET_entry_16;
86     bda->ivecs[0x14].offset = OFFSET_entry_14;
87     bda->ivecs[0x1a].offset = OFFSET_entry_1a;
88     bda->ivecs[0x70].offset = OFFSET_entry_70;
89     bda->ivecs[0x74].offset = OFFSET_entry_74;
90     bda->ivecs[0x75].offset = OFFSET_entry_75;
91     bda->ivecs[0x10].offset = OFFSET_entry_10;
92 }
93
94 static void
95 init_ebda()
96 {
97     memset(ebda, 0, sizeof(*ebda));
98     ebda->size = EBDA_SIZE;
99     bda->ebda_seg = EBDA_SEG;
100     bda->ivecs[0x41].seg = EBDA_SEG;
101     bda->ivecs[0x41].offset = offsetof(struct extended_bios_data_area_s, fdpt0);
102     bda->ivecs[0x46].seg = EBDA_SEG;
103     bda->ivecs[0x41].offset = offsetof(struct extended_bios_data_area_s, fdpt1);
104 }
105
106 static void
107 pit_setup()
108 {
109     // timer0: binary count, 16bit count, mode 2
110     outb(0x34, PORT_PIT_MODE);
111     // maximum count of 0000H = 18.2Hz
112     outb(0x0, PORT_PIT_COUNTER0);
113     outb(0x0, PORT_PIT_COUNTER0);
114 }
115
116 //--------------------------------------------------------------------------
117 // keyboard_panic
118 //--------------------------------------------------------------------------
119 static void
120 keyboard_panic(u16 status)
121 {
122   // If you're getting a 993 keyboard panic here,
123   // please see the comment in keyboard_init
124
125   BX_PANIC("Keyboard error:%u\n",status);
126 }
127
128 static void
129 kbd_wait(u8 mask, u8 code)
130 {
131     u16 max = 0xffff;
132     while ( ((inb(PORT_PS2_STATUS) & mask) == 0) && (--max>0) )
133         outb(code, PORT_DIAG);
134     if (!max)
135         keyboard_panic(code);
136 }
137
138 static inline void kbd_flush(u8 code) {
139     kbd_wait(0x02, code);
140 }
141 static inline void kbd_waitdata(u8 code) {
142     kbd_wait(0x01, code);
143 }
144
145 //--------------------------------------------------------------------------
146 // keyboard_init
147 //--------------------------------------------------------------------------
148 // this file is based on LinuxBIOS implementation of keyboard.c
149 // could convert to #asm to gain space
150 static void
151 keyboard_init()
152 {
153     /* ------------------- Flush buffers ------------------------*/
154     /* Wait until buffer is empty */
155     kbd_flush(0x00);
156
157     /* flush incoming keys */
158     u16 max=0x2000;
159     while (--max > 0) {
160         outb(0x00, PORT_DIAG);
161         if (inb(PORT_PS2_STATUS) & 0x01) {
162             inb(PORT_PS2_DATA);
163             max = 0x2000;
164             }
165         }
166
167     // Due to timer issues, and if the IPS setting is > 15000000,
168     // the incoming keys might not be flushed here. That will
169     // cause a panic a few lines below.  See sourceforge bug report :
170     // [ 642031 ] FATAL: Keyboard RESET error:993
171
172     /* ------------------- controller side ----------------------*/
173     /* send cmd = 0xAA, self test 8042 */
174     outb(0xaa, PORT_PS2_STATUS);
175
176     kbd_flush(0x00);
177     kbd_waitdata(0x01);
178
179     /* read self-test result, 0x55 should be returned from 0x60 */
180     if (inb(PORT_PS2_DATA) != 0x55)
181         keyboard_panic(991);
182
183     /* send cmd = 0xAB, keyboard interface test */
184     outb(0xab, PORT_PS2_STATUS);
185
186     kbd_flush(0x10);
187     kbd_waitdata(0x11);
188
189     /* read keyboard interface test result, */
190     /* 0x00 should be returned form 0x60 */
191     if (inb(PORT_PS2_DATA) != 0x00)
192         keyboard_panic(992);
193
194     /* Enable Keyboard clock */
195     outb(0xae, PORT_PS2_STATUS);
196     outb(0xa8, PORT_PS2_STATUS);
197
198     /* ------------------- keyboard side ------------------------*/
199     /* reset kerboard and self test  (keyboard side) */
200     outb(0xff, PORT_PS2_DATA);
201
202     kbd_flush(0x20);
203     kbd_waitdata(0x21);
204
205     /* keyboard should return ACK */
206     if (inb(PORT_PS2_DATA) != 0xfa)
207         keyboard_panic(993);
208
209     kbd_waitdata(0x31);
210
211     if (inb(PORT_PS2_DATA) != 0xaa)
212         keyboard_panic(994);
213
214     /* Disable keyboard */
215     outb(0xf5, PORT_PS2_DATA);
216
217     kbd_flush(0x40);
218     kbd_waitdata(0x41);
219
220     /* keyboard should return ACK */
221     if (inb(PORT_PS2_DATA) != 0xfa)
222         keyboard_panic(995);
223
224     /* Write Keyboard Mode */
225     outb(0x60, PORT_PS2_STATUS);
226
227     kbd_flush(0x50);
228
229     /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
230     outb(0x61, PORT_PS2_DATA);
231
232     kbd_flush(0x60);
233
234     /* Enable keyboard */
235     outb(0xf4, PORT_PS2_DATA);
236
237     kbd_flush(0x70);
238     kbd_waitdata(0x71);
239
240     /* keyboard should return ACK */
241     if (inb(PORT_PS2_DATA) != 0xfa)
242         keyboard_panic(996);
243
244     outb(0x77, PORT_DIAG);
245 }
246
247 static void
248 kbd_setup()
249 {
250     bda->kbd_mode = 0x10;
251     bda->kbd_buf_head = bda->kbd_buf_tail = bda->kbd_buf_start_offset
252         = offsetof(struct bios_data_area_s, kbd_buf) - 0x400;
253     bda->kbd_buf_end_offset
254         = (offsetof(struct bios_data_area_s, kbd_buf[sizeof(bda->kbd_buf)])
255            - 0x400);
256     keyboard_init();
257
258     // mov CMOS Equipment Byte to BDA Equipment Word
259     u16 eqb = bda->equipment_list_flags;
260     bda->equipment_list_flags = (eqb & 0xff00) | inb_cmos(CMOS_EQUIPMENT_INFO);
261 }
262
263 static u16
264 detect_parport(u16 port, u8 timeout, u8 count)
265 {
266     // clear input mode
267     outb(inb(port+2) & 0xdf, port+2);
268
269     outb(0xaa, port);
270     if (inb(port) != 0xaa)
271         // Not present
272         return 0;
273     bda->port_lpt[count] = port;
274     bda->lpt_timeout[count] = timeout;
275     return 1;
276 }
277
278 static void
279 lpt_setup()
280 {
281     u16 count = 0;
282     count += detect_parport(0x378, 0x14, count);
283     count += detect_parport(0x278, 0x14, count);
284
285     // Equipment word bits 14..15 determing # parallel ports
286     u16 eqb = bda->equipment_list_flags;
287     bda->equipment_list_flags = (eqb & 0x3fff) | (count << 14);
288 }
289
290 static u16
291 detect_serial(u16 port, u8 timeout, u8 count)
292 {
293     outb(0x02, port+1);
294     if (inb(port+1) != 0x02)
295         return 0;
296     if (inb(port+2) != 0x02)
297         return 0;
298     outb(0x00, port+1);
299     bda->port_com[count] = port;
300     bda->com_timeout[count] = timeout;
301     return 1;
302 }
303
304 static void
305 serial_setup()
306 {
307     u16 count = 0;
308     count += detect_serial(0x3f8, 0x0a, count);
309     count += detect_serial(0x2f8, 0x0a, count);
310     count += detect_serial(0x3e8, 0x0a, count);
311     count += detect_serial(0x2e8, 0x0a, count);
312
313     // Equipment word bits 9..11 determing # serial ports
314     u16 eqb = bda->equipment_list_flags;
315     bda->equipment_list_flags = (eqb & 0xf1ff) | (count << 9);
316 }
317
318 static u32
319 bcd2bin(u8 val)
320 {
321     return (val & 0xf) + ((val >> 4) * 10);
322 }
323
324 static void
325 timer_setup()
326 {
327     u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
328     u32 ticks = (seconds * 18206507) / 1000000;
329     u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
330     ticks += (minutes * 10923904) / 10000;
331     u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
332     ticks += (hours * 65543427) / 1000;
333     bda->timer_counter = ticks;
334     bda->timer_rollover = 0;
335 }
336
337 static void
338 pic_setup()
339 {
340     outb(0x11, PORT_PIC1);
341     outb(0x11, PORT_PIC2);
342     outb(0x08, PORT_PIC1_DATA);
343     outb(0x70, PORT_PIC2_DATA);
344     outb(0x04, PORT_PIC1_DATA);
345     outb(0x02, PORT_PIC2_DATA);
346     outb(0x01, PORT_PIC1_DATA);
347     outb(0x01, PORT_PIC2_DATA);
348     outb(0xb8, PORT_PIC1_DATA);
349     if (CONFIG_PS2_MOUSE)
350         outb(0x8f, PORT_PIC2_DATA);
351     else
352         outb(0x9f, PORT_PIC2_DATA);
353 }
354
355 static void
356 floppy_drive_post()
357 {
358     u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
359     u8 out = 0;
360     if (type & 0xf0)
361         out |= 0x07;
362     if (type & 0x0f)
363         out |= 0x70;
364     bda->floppy_harddisk_info = out;
365     outb(0x02, PORT_DMA1_MASK_REG);
366
367     bda->ivecs[0x1E].offset = OFFSET_diskette_param_table2;
368 }
369
370 static void
371 ata_init()
372 {
373     // hdidmap  and cdidmap init.
374     u8 device;
375     for (device=0; device < CONFIG_MAX_ATA_DEVICES; device++) {
376         ebda->ata.idmap[0][device] = CONFIG_MAX_ATA_DEVICES;
377         ebda->ata.idmap[1][device] = CONFIG_MAX_ATA_DEVICES;
378     }
379 }
380
381 static void
382 fill_hdinfo(struct fdpt_s *info, u8 typecmos, u8 basecmos)
383 {
384     u8 type = inb_cmos(typecmos);
385     if (type != 47)
386         // XXX - halt
387         return;
388
389     info->precompensation = (inb_cmos(basecmos+4) << 8) | inb_cmos(basecmos+3);
390     info->drive_control_byte = inb_cmos(basecmos+5);
391     info->landing_zone = (inb_cmos(basecmos+7) << 8) | inb_cmos(basecmos+6);
392     u16 cyl = (inb_cmos(basecmos+1) << 8) | inb_cmos(basecmos+0);
393     u8 heads = inb_cmos(basecmos+2);
394     u8 sectors = inb_cmos(basecmos+8);
395     if (cyl < 1024) {
396         // no logical CHS mapping used, just physical CHS
397         // use Standard Fixed Disk Parameter Table (FDPT)
398         info->cylinders = cyl;
399         info->heads = heads;
400         info->sectors = sectors;
401         return;
402     }
403
404     // complies with Phoenix style Translated Fixed Disk Parameter
405     // Table (FDPT)
406     info->phys_cylinders = cyl;
407     info->phys_heads = heads;
408     info->phys_sectors = sectors;
409     info->sectors = sectors;
410     info->a0h_signature = 0xa0;
411     if (cyl > 8192) {
412         cyl >>= 4;
413         heads <<= 4;
414     } else if (cyl > 4096) {
415         cyl >>= 3;
416         heads <<= 3;
417     } else if (cyl > 2048) {
418         cyl >>= 2;
419         heads <<= 2;
420     }
421     info->cylinders = cyl;
422     info->heads = heads;
423     info->checksum = ~checksum((u8*)info, sizeof(*info)-1) + 1;
424 }
425
426 static void
427 hard_drive_post()
428 {
429     outb(0x0a, 0x03f6); // 0000 1010 = reserved, disable IRQ 14
430     bda->disk_count = 1;
431     bda->disk_control_byte = 0xc0;
432
433     // move disk geometry data from CMOS to EBDA disk parameter table(s)
434     u8 diskinfo = inb_cmos(CMOS_DISK_DATA);
435     if ((diskinfo & 0xf0) == 0xf0)
436         // Fill EBDA table for hard disk 0.
437         fill_hdinfo(&ebda->fdpt0, CMOS_DISK_DRIVE1_TYPE, CMOS_DISK_DRIVE1_CYL);
438     if ((diskinfo & 0x0f) == 0x0f)
439         // XXX - bochs halts on any other type
440         // Fill EBDA table for hard disk 1.
441         fill_hdinfo(&ebda->fdpt0, CMOS_DISK_DRIVE2_TYPE, CMOS_DISK_DRIVE2_CYL);
442 }
443
444
445 static void
446 init_boot_vectors()
447 {
448     // Clear out the IPL table.
449     memset(ipl, 0, sizeof(*ipl));
450
451     // Floppy drive
452     struct ipl_entry_s *ip = &ipl->table[0];
453     ip->type = IPL_TYPE_FLOPPY;
454     ip++;
455
456     // First HDD
457     ip->type = IPL_TYPE_HARDDISK;
458     ip++;
459
460     // CDROM
461     if (CONFIG_CDROM_BOOT) {
462         ip->type = IPL_TYPE_CDROM;
463         ip++;
464     }
465
466     ipl->count = ip - ipl->table;
467     ipl->sequence = 0xffff;
468 }
469
470 static void
471 callrom(u16 seg, u16 offset)
472 {
473     struct bregs br;
474     memset(&br, 0, sizeof(br));
475     br.es = SEG_BIOS;
476     br.di = OFFSET_pnp_string + 1; // starts 1 past for alignment
477     br.cs = seg;
478     br.ip = offset;
479     call16(&br);
480 }
481
482 static void
483 rom_scan(u32 start, u32 end)
484 {
485     u8 *p = (u8*)start;
486     for (; p <= (u8*)end; p += 2048) {
487         u8 *rom = p;
488         if (*(u16*)rom != 0xaa55)
489             continue;
490         u32 len = rom[2] * 512;
491         if (checksum(rom, len) != 0)
492             continue;
493         p = (u8*)(((u32)p + len) / 2048 * 2048);
494         callrom(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
495
496         // Look at the ROM's PnP Expansion header.  Properly, we're supposed
497         // to init all the ROMs and then go back and build an IPL table of
498         // all the bootable devices, but we can get away with one pass.
499         if (rom[0x1a] != '$' || rom[0x1b] != 'P'
500             || rom[0x1c] != 'n' || rom[0x1d] != 'P')
501             continue;
502         // 0x1A is also the offset into the expansion header of...
503         // the Bootstrap Entry Vector, or zero if there is none.
504         u16 entry = *(u16*)&rom[0x1a+0x1a];
505         if (!entry)
506             continue;
507         // Found a device that thinks it can boot the system.  Record
508         // its BEV and product name string.
509
510         if (ipl->count >= ARRAY_SIZE(ipl->table))
511             continue;
512
513         struct ipl_entry_s *ip = &ipl->table[ipl->count];
514         ip->type = IPL_TYPE_BEV;
515         ip->vector = (PTR_TO_SEG(rom) << 16) | entry;
516
517         u16 desc = *(u16*)&rom[0x1a+0x10];
518         if (desc)
519             ip->description = (PTR_TO_SEG(rom) << 16) | desc;
520
521         ipl->count++;
522     }
523 }
524
525 static void
526 ram_probe(void)
527 {
528     if (inb_cmos(0x34) | inb_cmos(0x35))
529         ram_size = (inb_cmos(0x34) | (inb_cmos(0x35) << 8)) * 65536 +
530                    16 * 1024 * 1024;
531     else
532         ram_size = (inb_cmos(0x17) | (inb_cmos(0x18) << 8)) * 1024;
533 #ifdef CONFIG_USE_EBDA_TABLES
534     ebda_cur_addr = ((*(u16 *)(0x40e)) << 4) + 0x380;
535 #endif
536
537     BX_INFO("ram_size=0x%08lx\n", ram_size);
538 }
539
540 static void
541 cpu_probe(void)
542 {
543     u32 eax, ebx, ecx, edx;
544
545     cpuid(1, eax, ebx, ecx, edx);
546     cpuid_signature = eax;
547     cpuid_features = edx;
548     cpuid_ext_features = ecx;
549 }
550
551 static void
552 post()
553 {
554     BX_INFO("Start bios\n");
555
556     init_bda();
557     init_handlers();
558     init_ebda();
559
560     pit_setup();
561     kbd_setup();
562     lpt_setup();
563     serial_setup();
564     timer_setup();
565     pic_setup();
566
567     rom_scan(0xc0000, 0xc7800);
568
569     printf("BIOS - begin\n\n");
570
571     // XXX - need to do pci stuff
572     //pci_setup();
573     init_boot_vectors();
574
575     floppy_drive_post();
576     hard_drive_post();
577     if (CONFIG_ATA)
578         ata_init();
579
580     init_boot_vectors();
581     rom_scan(0xc8000, 0xe0000);
582
583     ram_probe();
584     cpu_probe();
585     smp_probe();
586     //pci_bios_init();
587
588     if (bios_table_cur_addr != 0) {
589         mptable_init();
590
591         smbios_init();
592
593         if (acpi_enabled)
594             acpi_bios_init();
595
596         bios_lock_shadow_ram();
597
598         BX_INFO("bios_table_cur_addr: 0x%08lx\n", bios_table_cur_addr);
599         if (bios_table_cur_addr > bios_table_end_addr)
600             BX_PANIC("bios_table_end_addr overflow!\n");
601     }
602
603     callrom(SEG_BIOS, OFFSET_begin_boot);
604 }
605
606 static void
607 init_dma()
608 {
609     // first reset the DMA controllers
610     outb(0, PORT_DMA1_MASTER_CLEAR);
611     outb(0, PORT_DMA2_MASTER_CLEAR);
612
613     // then initialize the DMA controllers
614     outb(0xc0, PORT_DMA2_MODE_REG);
615     outb(0x00, PORT_DMA2_MASK_REG);
616 }
617
618 static void
619 check_restart_status()
620 {
621     // Get and then clear CMOS shutdown status.
622     u8 status = inb_cmos(CMOS_RESET_CODE);
623     outb_cmos(0, CMOS_RESET_CODE);
624
625     if (status == 0x00 || status == 0x09 || status >= 0x0d)
626         // Normal post
627         return;
628
629     if (status != 0x05) {
630         BX_PANIC("Unimplemented shutdown status: %02x\n", status);
631         return;
632     }
633
634     // XXX - this is supposed to jump without changing any memory -
635     // but the stack has been altered by the time the code gets here.
636     eoi_both_pics();
637     struct bregs br;
638     memset(&br, 0, sizeof(br));
639     br.cs = bda->jump_cs_ip >> 16;
640     br.ip = bda->jump_cs_ip;
641     call16(&br);
642 }
643
644 void VISIBLE32
645 _start()
646 {
647     init_dma();
648     check_restart_status();
649
650     post();
651 }