Implement usleep using real time clock.
[seabios.git] / src / system.c
1 // 16bit system callbacks
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 "util.h" // irq_restore
9 #include "biosvar.h" // BIOS_CONFIG_TABLE
10 #include "ioport.h" // inb
11
12 #define E820_RAM          1
13 #define E820_RESERVED     2
14 #define E820_ACPI         3
15 #define E820_NVS          4
16 #define E820_UNUSABLE     5
17
18 // Use PS2 System Control port A to set A20 enable
19 static inline u8
20 set_a20(u8 cond)
21 {
22     // get current setting first
23     u8 newval, oldval = inb(PORT_A20);
24     if (cond)
25         newval = oldval | 0x02;
26     else
27         newval = oldval & ~0x02;
28     outb(newval, PORT_A20);
29
30     return (newval & 0x02) != 0;
31 }
32
33 static void
34 handle_152400(struct bregs *regs)
35 {
36     set_a20(0);
37     set_code_success(regs);
38 }
39
40 static void
41 handle_152401(struct bregs *regs)
42 {
43     set_a20(1);
44     set_code_success(regs);
45 }
46
47 static void
48 handle_152402(struct bregs *regs)
49 {
50     regs->al = !!(inb(PORT_A20) & 0x20);
51     set_code_success(regs);
52 }
53
54 static void
55 handle_152403(struct bregs *regs)
56 {
57     regs->bx = 3;
58     set_code_success(regs);
59 }
60
61 static void
62 handle_1524XX(struct bregs *regs)
63 {
64     set_code_fail(regs, RET_EUNSUPPORTED);
65 }
66
67 static void
68 handle_1524(struct bregs *regs)
69 {
70     switch (regs->al) {
71     case 0x00: handle_152400(regs); break;
72     case 0x01: handle_152401(regs); break;
73     case 0x02: handle_152402(regs); break;
74     case 0x03: handle_152403(regs); break;
75     default:   handle_1524XX(regs); break;
76     }
77 }
78
79 // removable media eject
80 static void
81 handle_1552(struct bregs *regs)
82 {
83     set_code_success(regs);
84 }
85
86 static void
87 handle_1587(struct bregs *regs)
88 {
89     // +++ should probably have descriptor checks
90     // +++ should have exception handlers
91
92     u8 prev_a20_enable = set_a20(1); // enable A20 line
93
94     // 128K max of transfer on 386+ ???
95     // source == destination ???
96
97     // ES:SI points to descriptor table
98     // offset   use     initially  comments
99     // ==============================================
100     // 00..07   Unused  zeros      Null descriptor
101     // 08..0f   GDT     zeros      filled in by BIOS
102     // 10..17   source  ssssssss   source of data
103     // 18..1f   dest    dddddddd   destination of data
104     // 20..27   CS      zeros      filled in by BIOS
105     // 28..2f   SS      zeros      filled in by BIOS
106
107     //es:si
108     //eeee0
109     //0ssss
110     //-----
111
112 // check for access rights of source & dest here
113
114     // Initialize GDT descriptor
115     SET_SEG(ES, regs->es);
116     u16 si = regs->si;
117     u16 base15_00 = (regs->es << 4) + si;
118     u16 base23_16 = regs->es >> 12;
119     if (base15_00 < (u16)(regs->es<<4))
120         base23_16++;
121     SET_VAR(ES, *(u16*)(si+0x08+0), 47);       // limit 15:00 = 6 * 8bytes/descriptor
122     SET_VAR(ES, *(u16*)(si+0x08+2), base15_00);// base 15:00
123     SET_VAR(ES, *(u8 *)(si+0x08+4), base23_16);// base 23:16
124     SET_VAR(ES, *(u8 *)(si+0x08+5), 0x93);     // access
125     SET_VAR(ES, *(u16*)(si+0x08+6), 0x0000);   // base 31:24/reserved/limit 19:16
126
127     // Initialize CS descriptor
128     SET_VAR(ES, *(u16*)(si+0x20+0), 0xffff);// limit 15:00 = normal 64K limit
129     SET_VAR(ES, *(u16*)(si+0x20+2), 0x0000);// base 15:00
130     SET_VAR(ES, *(u8 *)(si+0x20+4), 0x000f);// base 23:16
131     SET_VAR(ES, *(u8 *)(si+0x20+5), 0x9b);  // access
132     SET_VAR(ES, *(u16*)(si+0x20+6), 0x0000);// base 31:24/reserved/limit 19:16
133
134     // Initialize SS descriptor
135     u16 ss = GET_SEG(SS);
136     base15_00 = ss << 4;
137     base23_16 = ss >> 12;
138     SET_VAR(ES, *(u16*)(si+0x28+0), 0xffff);   // limit 15:00 = normal 64K limit
139     SET_VAR(ES, *(u16*)(si+0x28+2), base15_00);// base 15:00
140     SET_VAR(ES, *(u8 *)(si+0x28+4), base23_16);// base 23:16
141     SET_VAR(ES, *(u8 *)(si+0x28+5), 0x93);     // access
142     SET_VAR(ES, *(u16*)(si+0x28+6), 0x0000);   // base 31:24/reserved/limit 19:16
143
144     u16 count = regs->cx;
145     asm volatile(
146         // Load new descriptor tables
147         "lgdtw %%es:0x8(%%si)\n"
148         "lidtw %%cs:pmode_IDT_info\n"
149
150         // set PE bit in CR0
151         "movl %%cr0, %%eax\n"
152         "orb $0x01, %%al\n"
153         "movl %%eax, %%cr0\n"
154
155         // far jump to flush CPU queue after transition to protected mode
156         "ljmpw $0x0020, $1f\n"
157         "1:\n"
158
159         // GDT points to valid descriptor table, now load DS, ES
160         "movw $0x10, %%ax\n" // 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
161         "movw %%ax, %%ds\n"
162         "movw $0x18, %%ax\n" // 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
163         "movw %%ax, %%es\n"
164
165         // move CX words from DS:SI to ES:DI
166         "xorw %%si, %%si\n"
167         "xorw %%di, %%di\n"
168         "rep movsw\n"
169
170         // reset PG bit in CR0 ???
171         "movl %%cr0, %%eax\n"
172         "andb $0xfe, %%al\n"
173         "movl %%eax, %%cr0\n"
174
175         // far jump to flush CPU queue after transition to real mode
176         "ljmpw $0xf000, $2f\n"
177         "2:\n"
178
179         // restore IDT to normal real-mode defaults
180         "lidt %%cs:rmode_IDT_info\n"
181
182         // Restore %ds (from %ss)
183         "movw %%ss, %%ax\n"
184         "movw %%ax, %%ds\n"
185         : "+c"(count), "+S"(si)
186         : : "eax", "di", "cc"); // XXX - also clobbers %es
187
188     set_a20(prev_a20_enable);
189
190     set_code_success(regs);
191 }
192
193 // Get the amount of extended memory (above 1M)
194 static void
195 handle_1588(struct bregs *regs)
196 {
197     u32 rs = GET_EBDA(ram_size);
198
199     // According to Ralf Brown's interrupt the limit should be 15M,
200     // but real machines mostly return max. 63M.
201     if (rs > 64*1024*1024)
202         regs->ax = 63 * 1024;
203     else
204         regs->ax = (rs - 1*1024*1024) / 1024;
205     set_success(regs);
206 }
207
208 // Device busy interrupt.  Called by Int 16h when no key available
209 static void
210 handle_1590(struct bregs *regs)
211 {
212 }
213
214 // Interrupt complete.  Called by Int 16h when key becomes available
215 static void
216 handle_1591(struct bregs *regs)
217 {
218 }
219
220 // keyboard intercept
221 static void
222 handle_154f(struct bregs *regs)
223 {
224     // set_fail(regs);  -- don't report this failure.
225     set_cf(regs, 1);
226 }
227
228 static void
229 handle_15c0(struct bregs *regs)
230 {
231     regs->es = SEG_BIOS;
232     regs->bx = (u32)&BIOS_CONFIG_TABLE;
233     set_code_success(regs);
234 }
235
236 static void
237 handle_15c1(struct bregs *regs)
238 {
239     regs->es = GET_BDA(ebda_seg);
240     set_success(regs);
241 }
242
243 static void
244 handle_15e801(struct bregs *regs)
245 {
246     // my real system sets ax and bx to 0
247     // this is confirmed by Ralph Brown list
248     // but syslinux v1.48 is known to behave
249     // strangely if ax is set to 0
250     // regs.u.r16.ax = 0;
251     // regs.u.r16.bx = 0;
252
253     u32 rs = GET_EBDA(ram_size);
254
255     // Get the amount of extended memory (above 1M)
256     if (rs > 16*1024*1024) {
257         // limit to 15M
258         regs->cx = 15*1024;
259         // Get the amount of extended memory above 16M in 64k blocks
260         regs->dx = (rs - 16*1024*1024) / (64*1024);
261     } else {
262         regs->cx = (rs - 1*1024*1024) / 1024;
263         regs->dx = 0;
264     }
265
266     // Set configured memory equal to extended memory
267     regs->ax = regs->cx;
268     regs->bx = regs->dx;
269
270     set_success(regs);
271 }
272
273 static void
274 set_e820_range(struct bregs *regs, u32 start, u32 end, u16 type, int last)
275 {
276     u32 size = end - start;
277     SET_FARVAR(regs->es, *(u64*)(regs->di+0), start);
278     SET_FARVAR(regs->es, *(u64*)(regs->di+8), size);
279     SET_FARVAR(regs->es, *(u16*)(regs->di+16), type);
280     SET_FARVAR(regs->es, *(u16*)(regs->di+18), 0x0);
281
282     if (last)
283         regs->ebx = 0;
284     else
285         regs->ebx++;
286     regs->eax = 0x534D4150;
287     regs->ecx = 0x14;
288     set_success(regs);
289 }
290
291 // XXX - should create e820 memory map in post and just copy it here.
292 static void
293 handle_15e820(struct bregs *regs)
294 {
295     if (regs->edx != 0x534D4150) {
296         set_code_fail(regs, RET_EUNSUPPORTED);
297         return;
298     }
299
300     u32 extended_memory_size = GET_EBDA(ram_size);
301     // greater than EFF00000???
302     if (extended_memory_size > 0xf0000000)
303         // everything after this is reserved memory until we get to 0x100000000
304         extended_memory_size = 0xf0000000;
305
306     switch (regs->bx) {
307     case 0:
308         set_e820_range(regs, 0x0000000L, 0x0009fc00L, E820_RAM, 0);
309         break;
310     case 1:
311         set_e820_range(regs, 0x0009fc00L, 0x000a0000L, E820_RESERVED, 0);
312         break;
313     case 2:
314         set_e820_range(regs, 0x000e8000L, 0x00100000L, E820_RESERVED, 0);
315         break;
316     case 3:
317         set_e820_range(regs, 0x00100000L
318                        , extended_memory_size - CONFIG_ACPI_DATA_SIZE
319                        , E820_RAM, 0);
320         break;
321     case 4:
322         set_e820_range(regs,
323                        extended_memory_size - CONFIG_ACPI_DATA_SIZE,
324                        extended_memory_size, E820_ACPI, 0);
325         break;
326     case 5:
327         /* 256KB BIOS area at the end of 4 GB */
328         set_e820_range(regs, 0xfffc0000L, 0x00000000L, E820_RESERVED, 1);
329         break;
330     default:  /* AX=E820, DX=534D4150, BX unrecognized */
331         set_code_fail(regs, RET_EUNSUPPORTED);
332     }
333 }
334
335 static void
336 handle_15e8XX(struct bregs *regs)
337 {
338     set_code_fail(regs, RET_EUNSUPPORTED);
339 }
340
341 static void
342 handle_15e8(struct bregs *regs)
343 {
344     switch (regs->al) {
345     case 0x01: handle_15e801(regs); break;
346     case 0x20: handle_15e820(regs); break;
347     default:   handle_15e8XX(regs); break;
348     }
349 }
350
351 static void
352 handle_15XX(struct bregs *regs)
353 {
354     set_code_fail(regs, RET_EUNSUPPORTED);
355 }
356
357 // INT 15h System Services Entry Point
358 void VISIBLE16
359 handle_15(struct bregs *regs)
360 {
361     //debug_enter(regs);
362     switch (regs->ah) {
363     case 0x24: handle_1524(regs); break;
364     case 0x4f: handle_154f(regs); break;
365     case 0x52: handle_1552(regs); break;
366     case 0x53: handle_1553(regs); break;
367     case 0x83: handle_1583(regs); break;
368     case 0x86: handle_1586(regs); break;
369     case 0x87: handle_1587(regs); break;
370     case 0x88: handle_1588(regs); break;
371     case 0x90: handle_1590(regs); break;
372     case 0x91: handle_1591(regs); break;
373     case 0xc0: handle_15c0(regs); break;
374     case 0xc1: handle_15c1(regs); break;
375     case 0xc2: handle_15c2(regs); break;
376     case 0xe8: handle_15e8(regs); break;
377     default:   handle_15XX(regs); break;
378     }
379 }
380
381 // INT 12h Memory Size Service Entry Point
382 void VISIBLE16
383 handle_12(struct bregs *regs)
384 {
385     debug_enter(regs);
386     regs->ax = GET_BDA(mem_size_kb);
387 }
388
389 // INT 11h Equipment List Service Entry Point
390 void VISIBLE16
391 handle_11(struct bregs *regs)
392 {
393     debug_enter(regs);
394     regs->ax = GET_BDA(equipment_list_flags);
395 }
396
397 // INT 05h Print Screen Service Entry Point
398 void VISIBLE16
399 handle_05(struct bregs *regs)
400 {
401     debug_enter(regs);
402 }
403
404 // INT 10h Video Support Service Entry Point
405 void VISIBLE16
406 handle_10(struct bregs *regs)
407 {
408     debug_enter(regs);
409     // dont do anything, since the VGA BIOS handles int10h requests
410 }
411
412 void VISIBLE16
413 handle_nmi()
414 {
415     debug_isr();
416     BX_PANIC("NMI Handler called\n");
417 }
418
419 // INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION
420 void VISIBLE16
421 handle_75()
422 {
423     debug_isr();
424
425     // clear irq13
426     outb(0, PORT_MATH_CLEAR);
427     // clear interrupt
428     eoi_both_pics();
429     // legacy nmi call
430     struct bregs br;
431     memset(&br, 0, sizeof(br));
432     call16_int(0x02, &br);
433 }