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