Introduce set_fail_silent() helpers.
[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_silent(regs);
225 }
226
227 static void
228 handle_15c0(struct bregs *regs)
229 {
230     regs->es = SEG_BIOS;
231     regs->bx = (u32)&BIOS_CONFIG_TABLE;
232     set_code_success(regs);
233 }
234
235 static void
236 handle_15c1(struct bregs *regs)
237 {
238     regs->es = GET_BDA(ebda_seg);
239     set_success(regs);
240 }
241
242 static void
243 handle_15e801(struct bregs *regs)
244 {
245     // my real system sets ax and bx to 0
246     // this is confirmed by Ralph Brown list
247     // but syslinux v1.48 is known to behave
248     // strangely if ax is set to 0
249     // regs.u.r16.ax = 0;
250     // regs.u.r16.bx = 0;
251
252     u32 rs = GET_EBDA(ram_size);
253
254     // Get the amount of extended memory (above 1M)
255     if (rs > 16*1024*1024) {
256         // limit to 15M
257         regs->cx = 15*1024;
258         // Get the amount of extended memory above 16M in 64k blocks
259         regs->dx = (rs - 16*1024*1024) / (64*1024);
260     } else {
261         regs->cx = (rs - 1*1024*1024) / 1024;
262         regs->dx = 0;
263     }
264
265     // Set configured memory equal to extended memory
266     regs->ax = regs->cx;
267     regs->bx = regs->dx;
268
269     set_success(regs);
270 }
271
272 static void
273 set_e820_range(struct bregs *regs, u32 start, u32 end, u16 type, int last)
274 {
275     u32 size = end - start;
276     SET_FARVAR(regs->es, *(u64*)(regs->di+0), start);
277     SET_FARVAR(regs->es, *(u64*)(regs->di+8), size);
278     SET_FARVAR(regs->es, *(u16*)(regs->di+16), type);
279     SET_FARVAR(regs->es, *(u16*)(regs->di+18), 0x0);
280
281     if (last)
282         regs->ebx = 0;
283     else
284         regs->ebx++;
285     regs->eax = 0x534D4150;
286     regs->ecx = 0x14;
287     set_success(regs);
288 }
289
290 // XXX - should create e820 memory map in post and just copy it here.
291 static void
292 handle_15e820(struct bregs *regs)
293 {
294     if (regs->edx != 0x534D4150) {
295         set_code_fail(regs, RET_EUNSUPPORTED);
296         return;
297     }
298
299     u32 extended_memory_size = GET_EBDA(ram_size);
300     // greater than EFF00000???
301     if (extended_memory_size > 0xf0000000)
302         // everything after this is reserved memory until we get to 0x100000000
303         extended_memory_size = 0xf0000000;
304
305     switch (regs->bx) {
306     case 0:
307         set_e820_range(regs, 0x0000000L, 0x0009fc00L, E820_RAM, 0);
308         break;
309     case 1:
310         set_e820_range(regs, 0x0009fc00L, 0x000a0000L, E820_RESERVED, 0);
311         break;
312     case 2:
313         set_e820_range(regs, 0x000e8000L, 0x00100000L, E820_RESERVED, 0);
314         break;
315     case 3:
316         set_e820_range(regs, 0x00100000L
317                        , extended_memory_size - CONFIG_ACPI_DATA_SIZE
318                        , E820_RAM, 0);
319         break;
320     case 4:
321         set_e820_range(regs,
322                        extended_memory_size - CONFIG_ACPI_DATA_SIZE,
323                        extended_memory_size, E820_ACPI, 0);
324         break;
325     case 5:
326         /* 256KB BIOS area at the end of 4 GB */
327         set_e820_range(regs, 0xfffc0000L, 0x00000000L, E820_RESERVED, 1);
328         break;
329     default:  /* AX=E820, DX=534D4150, BX unrecognized */
330         set_code_fail(regs, RET_EUNSUPPORTED);
331     }
332 }
333
334 static void
335 handle_15e8XX(struct bregs *regs)
336 {
337     set_code_fail(regs, RET_EUNSUPPORTED);
338 }
339
340 static void
341 handle_15e8(struct bregs *regs)
342 {
343     switch (regs->al) {
344     case 0x01: handle_15e801(regs); break;
345     case 0x20: handle_15e820(regs); break;
346     default:   handle_15e8XX(regs); break;
347     }
348 }
349
350 static void
351 handle_15XX(struct bregs *regs)
352 {
353     set_code_fail(regs, RET_EUNSUPPORTED);
354 }
355
356 // INT 15h System Services Entry Point
357 void VISIBLE16
358 handle_15(struct bregs *regs)
359 {
360     //debug_enter(regs);
361     switch (regs->ah) {
362     case 0x24: handle_1524(regs); break;
363     case 0x4f: handle_154f(regs); break;
364     case 0x52: handle_1552(regs); break;
365     case 0x53: handle_1553(regs); break;
366     case 0x83: handle_1583(regs); break;
367     case 0x86: handle_1586(regs); break;
368     case 0x87: handle_1587(regs); break;
369     case 0x88: handle_1588(regs); break;
370     case 0x90: handle_1590(regs); break;
371     case 0x91: handle_1591(regs); break;
372     case 0xc0: handle_15c0(regs); break;
373     case 0xc1: handle_15c1(regs); break;
374     case 0xc2: handle_15c2(regs); break;
375     case 0xe8: handle_15e8(regs); break;
376     default:   handle_15XX(regs); break;
377     }
378 }
379
380 // INT 12h Memory Size Service Entry Point
381 void VISIBLE16
382 handle_12(struct bregs *regs)
383 {
384     debug_enter(regs);
385     regs->ax = GET_BDA(mem_size_kb);
386 }
387
388 // INT 11h Equipment List Service Entry Point
389 void VISIBLE16
390 handle_11(struct bregs *regs)
391 {
392     debug_enter(regs);
393     regs->ax = GET_BDA(equipment_list_flags);
394 }
395
396 // INT 05h Print Screen Service Entry Point
397 void VISIBLE16
398 handle_05(struct bregs *regs)
399 {
400     debug_enter(regs);
401 }
402
403 // INT 10h Video Support Service Entry Point
404 void VISIBLE16
405 handle_10(struct bregs *regs)
406 {
407     debug_enter(regs);
408     // dont do anything, since the VGA BIOS handles int10h requests
409 }
410
411 void VISIBLE16
412 handle_nmi()
413 {
414     debug_isr();
415     BX_PANIC("NMI Handler called\n");
416 }
417
418 // INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION
419 void VISIBLE16
420 handle_75()
421 {
422     debug_isr();
423
424     // clear irq13
425     outb(0, PORT_MATH_CLEAR);
426     // clear interrupt
427     eoi_both_pics();
428     // legacy nmi call
429     struct bregs br;
430     memset(&br, 0, sizeof(br));
431     call16_int(0x02, &br);
432 }