Move floppy setup code from post.c to floppy.c.
[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 #include "ata.h" // ata_detect
15 #include "kbd.h" // kbd_setup
16 #include "disk.h" // floppy_drive_setup
17
18 #define bda ((struct bios_data_area_s *)MAKE_FARPTR(SEG_BDA, 0))
19 #define ebda ((struct extended_bios_data_area_s *)MAKE_FARPTR(SEG_EBDA, 0))
20
21 static void
22 init_bda()
23 {
24     memset(bda, 0, sizeof(*bda));
25
26     int i;
27     for (i=0; i<256; i++) {
28         SET_BDA(ivecs[i].seg, SEG_BIOS);
29         SET_BDA(ivecs[i].offset, OFFSET_dummy_iret_handler);
30     }
31
32     SET_BDA(mem_size_kb, BASE_MEM_IN_K);
33
34     // mov CMOS Equipment Byte to BDA Equipment Word
35     SET_BDA(equipment_list_flags, inb_cmos(CMOS_EQUIPMENT_INFO));
36 }
37
38 static void
39 init_handlers()
40 {
41     // set vector 0x79 to zero
42     // this is used by 'gardian angel' protection system
43     SET_BDA(ivecs[0x79].seg, 0);
44     SET_BDA(ivecs[0x79].offset, 0);
45
46     SET_BDA(ivecs[0x40].offset, OFFSET_entry_40);
47     SET_BDA(ivecs[0x0e].offset, OFFSET_entry_0e);
48     SET_BDA(ivecs[0x13].offset, OFFSET_entry_13);
49     SET_BDA(ivecs[0x76].offset, OFFSET_entry_76);
50     SET_BDA(ivecs[0x17].offset, OFFSET_entry_17);
51     SET_BDA(ivecs[0x18].offset, OFFSET_entry_18);
52     SET_BDA(ivecs[0x19].offset, OFFSET_entry_19);
53     SET_BDA(ivecs[0x1c].offset, OFFSET_entry_1c);
54     SET_BDA(ivecs[0x12].offset, OFFSET_entry_12);
55     SET_BDA(ivecs[0x11].offset, OFFSET_entry_11);
56     SET_BDA(ivecs[0x15].offset, OFFSET_entry_15);
57     SET_BDA(ivecs[0x08].offset, OFFSET_entry_08);
58     SET_BDA(ivecs[0x09].offset, OFFSET_entry_09);
59     SET_BDA(ivecs[0x16].offset, OFFSET_entry_16);
60     SET_BDA(ivecs[0x14].offset, OFFSET_entry_14);
61     SET_BDA(ivecs[0x1a].offset, OFFSET_entry_1a);
62     SET_BDA(ivecs[0x70].offset, OFFSET_entry_70);
63     SET_BDA(ivecs[0x74].offset, OFFSET_entry_74);
64     SET_BDA(ivecs[0x75].offset, OFFSET_entry_75);
65     SET_BDA(ivecs[0x10].offset, OFFSET_entry_10);
66
67     SET_BDA(ivecs[0x1E].offset, OFFSET_diskette_param_table2);
68 }
69
70 static void
71 init_ebda()
72 {
73     memset(ebda, 0, sizeof(*ebda));
74     ebda->size = EBDA_SIZE;
75     SET_BDA(ebda_seg, SEG_EBDA);
76     SET_BDA(ivecs[0x41].seg, SEG_EBDA);
77     SET_BDA(ivecs[0x41].offset
78             , offsetof(struct extended_bios_data_area_s, fdpt0));
79     SET_BDA(ivecs[0x46].seg, SEG_EBDA);
80     SET_BDA(ivecs[0x41].offset
81             , offsetof(struct extended_bios_data_area_s, fdpt1));
82 }
83
84 static void
85 pit_setup()
86 {
87     // timer0: binary count, 16bit count, mode 2
88     outb(0x34, PORT_PIT_MODE);
89     // maximum count of 0000H = 18.2Hz
90     outb(0x0, PORT_PIT_COUNTER0);
91     outb(0x0, PORT_PIT_COUNTER0);
92 }
93
94 static u16
95 detect_parport(u16 port, u8 timeout, u8 count)
96 {
97     // clear input mode
98     outb(inb(port+2) & 0xdf, port+2);
99
100     outb(0xaa, port);
101     if (inb(port) != 0xaa)
102         // Not present
103         return 0;
104     SET_BDA(port_lpt[count], port);
105     SET_BDA(lpt_timeout[count], timeout);
106     return 1;
107 }
108
109 static void
110 lpt_setup()
111 {
112     u16 count = 0;
113     count += detect_parport(0x378, 0x14, count);
114     count += detect_parport(0x278, 0x14, count);
115
116     // Equipment word bits 14..15 determing # parallel ports
117     u16 eqb = GET_BDA(equipment_list_flags);
118     SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
119 }
120
121 static u16
122 detect_serial(u16 port, u8 timeout, u8 count)
123 {
124     outb(0x02, port+1);
125     if (inb(port+1) != 0x02)
126         return 0;
127     if (inb(port+2) != 0x02)
128         return 0;
129     outb(0x00, port+1);
130     SET_BDA(port_com[count], port);
131     SET_BDA(com_timeout[count], timeout);
132     return 1;
133 }
134
135 static void
136 serial_setup()
137 {
138     u16 count = 0;
139     count += detect_serial(0x3f8, 0x0a, count);
140     count += detect_serial(0x2f8, 0x0a, count);
141     count += detect_serial(0x3e8, 0x0a, count);
142     count += detect_serial(0x2e8, 0x0a, count);
143
144     // Equipment word bits 9..11 determing # serial ports
145     u16 eqb = GET_BDA(equipment_list_flags);
146     SET_BDA(equipment_list_flags, (eqb & 0xf1ff) | (count << 9));
147 }
148
149 static u32
150 bcd2bin(u8 val)
151 {
152     return (val & 0xf) + ((val >> 4) * 10);
153 }
154
155 static void
156 timer_setup()
157 {
158     u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
159     u32 ticks = (seconds * 18206507) / 1000000;
160     u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
161     ticks += (minutes * 10923904) / 10000;
162     u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
163     ticks += (hours * 65543427) / 1000;
164     SET_BDA(timer_counter, ticks);
165     SET_BDA(timer_rollover, 0);
166 }
167
168 static void
169 pic_setup()
170 {
171     outb(0x11, PORT_PIC1);
172     outb(0x11, PORT_PIC2);
173     outb(0x08, PORT_PIC1_DATA);
174     outb(0x70, PORT_PIC2_DATA);
175     outb(0x04, PORT_PIC1_DATA);
176     outb(0x02, PORT_PIC2_DATA);
177     outb(0x01, PORT_PIC1_DATA);
178     outb(0x01, PORT_PIC2_DATA);
179     outb(0xb8, PORT_PIC1_DATA);
180     if (CONFIG_PS2_MOUSE)
181         outb(0x8f, PORT_PIC2_DATA);
182     else
183         outb(0x9f, PORT_PIC2_DATA);
184 }
185
186 static void
187 ata_init()
188 {
189     // hdidmap  and cdidmap init.
190     u8 device;
191     for (device=0; device < CONFIG_MAX_ATA_DEVICES; device++) {
192         ebda->ata.idmap[0][device] = CONFIG_MAX_ATA_DEVICES;
193         ebda->ata.idmap[1][device] = CONFIG_MAX_ATA_DEVICES;
194     }
195 }
196
197 static void
198 fill_hdinfo(struct fdpt_s *info, u8 typecmos, u8 basecmos)
199 {
200     u8 type = inb_cmos(typecmos);
201     if (type != 47)
202         // XXX - halt
203         return;
204
205     info->precompensation = (inb_cmos(basecmos+4) << 8) | inb_cmos(basecmos+3);
206     info->drive_control_byte = inb_cmos(basecmos+5);
207     info->landing_zone = (inb_cmos(basecmos+7) << 8) | inb_cmos(basecmos+6);
208     u16 cyl = (inb_cmos(basecmos+1) << 8) | inb_cmos(basecmos+0);
209     u8 heads = inb_cmos(basecmos+2);
210     u8 sectors = inb_cmos(basecmos+8);
211     if (cyl < 1024) {
212         // no logical CHS mapping used, just physical CHS
213         // use Standard Fixed Disk Parameter Table (FDPT)
214         info->cylinders = cyl;
215         info->heads = heads;
216         info->sectors = sectors;
217         return;
218     }
219
220     // complies with Phoenix style Translated Fixed Disk Parameter
221     // Table (FDPT)
222     info->phys_cylinders = cyl;
223     info->phys_heads = heads;
224     info->phys_sectors = sectors;
225     info->sectors = sectors;
226     info->a0h_signature = 0xa0;
227     if (cyl > 8192) {
228         cyl >>= 4;
229         heads <<= 4;
230     } else if (cyl > 4096) {
231         cyl >>= 3;
232         heads <<= 3;
233     } else if (cyl > 2048) {
234         cyl >>= 2;
235         heads <<= 2;
236     }
237     info->cylinders = cyl;
238     info->heads = heads;
239     info->checksum = -checksum((u8*)info, sizeof(*info)-1);
240 }
241
242 static void
243 hard_drive_post()
244 {
245     outb(0x0a, PORT_HD_DATA); // 0000 1010 = reserved, disable IRQ 14
246     SET_BDA(disk_count, 1);
247     SET_BDA(disk_control_byte, 0xc0);
248
249     // move disk geometry data from CMOS to EBDA disk parameter table(s)
250     u8 diskinfo = inb_cmos(CMOS_DISK_DATA);
251     if ((diskinfo & 0xf0) == 0xf0)
252         // Fill EBDA table for hard disk 0.
253         fill_hdinfo(&ebda->fdpt0, CMOS_DISK_DRIVE1_TYPE, CMOS_DISK_DRIVE1_CYL);
254     if ((diskinfo & 0x0f) == 0x0f)
255         // XXX - bochs halts on any other type
256         // Fill EBDA table for hard disk 1.
257         fill_hdinfo(&ebda->fdpt1, CMOS_DISK_DRIVE2_TYPE, CMOS_DISK_DRIVE2_CYL);
258 }
259
260 static void
261 init_boot_vectors()
262 {
263     // Floppy drive
264     struct ipl_entry_s *ip = &ebda->ipl.table[0];
265     ip->type = IPL_TYPE_FLOPPY;
266     ip++;
267
268     // First HDD
269     ip->type = IPL_TYPE_HARDDISK;
270     ip++;
271
272     // CDROM
273     if (CONFIG_CDROM_BOOT) {
274         ip->type = IPL_TYPE_CDROM;
275         ip++;
276     }
277
278     ebda->ipl.count = ip - ebda->ipl.table;
279     ebda->ipl.sequence = 0xffff;
280     ebda->ipl.bootfirst = 0xffff;
281 }
282
283 static void
284 callrom(u16 seg, u16 offset)
285 {
286     struct bregs br;
287     memset(&br, 0, sizeof(br));
288     br.es = SEG_BIOS;
289     br.di = OFFSET_pnp_string + 1; // starts 1 past for alignment
290     br.cs = seg;
291     br.ip = offset;
292     call16(&br);
293 }
294
295 static void
296 rom_scan(u32 start, u32 end)
297 {
298     u8 *p = (u8*)start;
299     for (; p <= (u8*)end; p += 2048) {
300         u8 *rom = p;
301         if (*(u16*)rom != 0xaa55)
302             continue;
303         u32 len = rom[2] * 512;
304         if (checksum(rom, len) != 0)
305             continue;
306         p = (u8*)(((u32)p + len) / 2048 * 2048);
307         callrom(FARPTR_TO_SEG(rom), FARPTR_TO_OFFSET(rom + 3));
308
309         // Look at the ROM's PnP Expansion header.  Properly, we're supposed
310         // to init all the ROMs and then go back and build an IPL table of
311         // all the bootable devices, but we can get away with one pass.
312         if (rom[0x1a] != '$' || rom[0x1b] != 'P'
313             || rom[0x1c] != 'n' || rom[0x1d] != 'P')
314             continue;
315         // 0x1A is also the offset into the expansion header of...
316         // the Bootstrap Entry Vector, or zero if there is none.
317         u16 entry = *(u16*)&rom[0x1a+0x1a];
318         if (!entry)
319             continue;
320         // Found a device that thinks it can boot the system.  Record
321         // its BEV and product name string.
322
323         if (ebda->ipl.count >= ARRAY_SIZE(ebda->ipl.table))
324             continue;
325
326         struct ipl_entry_s *ip = &ebda->ipl.table[ebda->ipl.count];
327         ip->type = IPL_TYPE_BEV;
328         ip->vector = (FARPTR_TO_SEG(rom) << 16) | entry;
329
330         u16 desc = *(u16*)&rom[0x1a+0x10];
331         if (desc)
332             ip->description = (u32)MAKE_FARPTR(FARPTR_TO_SEG(rom), desc);
333
334         ebda->ipl.count++;
335     }
336 }
337
338 static void
339 post()
340 {
341     BX_INFO("Start bios\n");
342
343     init_bda();
344     init_handlers();
345     init_ebda();
346
347     pit_setup();
348     kbd_setup();
349     lpt_setup();
350     serial_setup();
351     timer_setup();
352     pic_setup();
353
354     rom_scan(0xc0000, 0xc7800);
355
356     printf("BIOS - begin\n\n");
357
358     // clear bss section -- XXX - shouldn't use globals
359     extern char __bss_start[], __bss_end[];
360     memset(__bss_start, 0, __bss_end - __bss_start);
361
362     rombios32_init();
363
364     floppy_drive_setup();
365     hard_drive_post();
366     if (CONFIG_ATA) {
367         ata_init();
368         ata_detect();
369     }
370
371     init_boot_vectors();
372
373     rom_scan(0xc8000, 0xe0000);
374
375     interactive_bootmenu();
376
377     // reset the memory (some boot loaders such as syslinux suppose
378     // that the memory is set to zero)
379     memset((void*)0x40000, 0, 0x40000); // XXX - shouldn't use globals
380
381     // Invoke int 19 to start boot process.
382     struct bregs br;
383     memset(&br, 0, sizeof(br));
384     call16_int(0x19, &br);
385 }
386
387 static void
388 init_dma()
389 {
390     // first reset the DMA controllers
391     outb(0, PORT_DMA1_MASTER_CLEAR);
392     outb(0, PORT_DMA2_MASTER_CLEAR);
393
394     // then initialize the DMA controllers
395     outb(0xc0, PORT_DMA2_MODE_REG);
396     outb(0x00, PORT_DMA2_MASK_REG);
397 }
398
399 static void
400 check_restart_status()
401 {
402     // Get and then clear CMOS shutdown status.
403     u8 status = inb_cmos(CMOS_RESET_CODE);
404     outb_cmos(0, CMOS_RESET_CODE);
405
406     if (status == 0x00 || status == 0x09 || status >= 0x0d)
407         // Normal post
408         return;
409
410     if (status != 0x05) {
411         BX_PANIC("Unimplemented shutdown status: %02x\n", status);
412         return;
413     }
414
415     // XXX - this is supposed to jump without changing any memory -
416     // but the stack has been altered by the time the code gets here.
417     eoi_both_pics();
418     struct bregs br;
419     memset(&br, 0, sizeof(br));
420     br.cs = GET_BDA(jump_cs_ip) >> 16;
421     br.ip = GET_BDA(jump_cs_ip);
422     call16(&br);
423 }
424
425 void VISIBLE32
426 _start()
427 {
428     init_dma();
429     check_restart_status();
430
431     post();
432 }