blob: 5d10cc8e8e867e48dd1f2d66a55f4ca3ca3355a0 [file] [log] [blame]
Kevin O'Connor30853762009-01-17 18:49:20 -05001// Handler for int 0x15 "system" calls
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05002//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connor392d2aa2013-09-15 00:14:28 -04008#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "bregs.h" // struct bregs
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "hw/pic.h" // pic_reset
Kevin O'Connor4ade5232013-09-18 21:41:48 -040011#include "hw/ps2port.h" // PORT_A20
Kevin O'Connor9dea5902013-09-14 20:23:54 -040012#include "malloc.h" // LegacyRamSize
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "memmap.h" // E820_RAM
14#include "output.h" // debug_enter
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040015#include "string.h" // memcpy_far
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040016#include "util.h" // handle_1553
Kevin O'Connor4ade5232013-09-18 21:41:48 -040017#include "x86.h" // inb
Kevin O'Connore2e5f012008-03-08 10:27:39 -050018
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050019// Use PS2 System Control port A to set A20 enable
20static inline u8
21set_a20(u8 cond)
22{
23 // get current setting first
24 u8 newval, oldval = inb(PORT_A20);
25 if (cond)
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050026 newval = oldval | A20_ENABLE_BIT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050027 else
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050028 newval = oldval & ~A20_ENABLE_BIT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050029 outb(newval, PORT_A20);
30
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050031 return (oldval & A20_ENABLE_BIT) != 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050032}
33
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050034static void
35handle_152400(struct bregs *regs)
36{
37 set_a20(0);
Kevin O'Connor6c781222008-03-09 12:19:23 -040038 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050039}
40
41static void
42handle_152401(struct bregs *regs)
43{
44 set_a20(1);
Kevin O'Connor6c781222008-03-09 12:19:23 -040045 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050046}
47
48static void
49handle_152402(struct bregs *regs)
50{
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050051 regs->al = (inb(PORT_A20) & A20_ENABLE_BIT) != 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -040052 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050053}
54
55static void
56handle_152403(struct bregs *regs)
57{
58 regs->bx = 3;
Kevin O'Connor6c781222008-03-09 12:19:23 -040059 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050060}
61
62static void
63handle_1524XX(struct bregs *regs)
64{
Kevin O'Connordfefeb52009-12-13 13:04:17 -050065 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050066}
67
Kevin O'Connoradb6b372008-03-01 13:38:38 -050068static void
69handle_1524(struct bregs *regs)
70{
71 switch (regs->al) {
72 case 0x00: handle_152400(regs); break;
73 case 0x01: handle_152401(regs); break;
74 case 0x02: handle_152402(regs); break;
75 case 0x03: handle_152403(regs); break;
76 default: handle_1524XX(regs); break;
77 }
78}
79
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050080// removable media eject
81static void
82handle_1552(struct bregs *regs)
83{
Kevin O'Connor6c781222008-03-09 12:19:23 -040084 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050085}
86
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050087static void
88handle_1587(struct bregs *regs)
89{
90 // +++ should probably have descriptor checks
91 // +++ should have exception handlers
92
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050093 u8 prev_a20_enable = set_a20(1); // enable A20 line
94
95 // 128K max of transfer on 386+ ???
96 // source == destination ???
97
98 // ES:SI points to descriptor table
99 // offset use initially comments
100 // ==============================================
101 // 00..07 Unused zeros Null descriptor
102 // 08..0f GDT zeros filled in by BIOS
103 // 10..17 source ssssssss source of data
104 // 18..1f dest dddddddd destination of data
105 // 20..27 CS zeros filled in by BIOS
106 // 28..2f SS zeros filled in by BIOS
107
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500108// check for access rights of source & dest here
109
110 // Initialize GDT descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400111 u32 si = regs->si;
112 u64 *gdt_far = (void*)si;
113 u16 gdt_seg = regs->es;
114 u32 loc = (u32)MAKE_FLATPTR(gdt_seg, gdt_far);
115 SET_FARVAR(gdt_seg, gdt_far[1], GDT_DATA | GDT_LIMIT((6*sizeof(u64))-1)
116 | GDT_BASE(loc));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500117 // Initialize CS descriptor
Kevin O'Connor643062f2010-01-04 20:48:20 -0500118 SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1)
119 | GDT_BASE(BUILD_BIOS_ADDR));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500120 // Initialize SS descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400121 loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0);
122 SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | GDT_LIMIT(0x0ffff)
123 | GDT_BASE(loc));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500124
Kevin O'Connor2cdd8b62008-03-09 23:37:04 -0400125 u16 count = regs->cx;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500126 asm volatile(
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500127 // Load new descriptor tables
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500128 " lgdtw %%es:(1<<3)(%%si)\n"
129 " lidtw %%cs:pmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500130
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500131 // Enable protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500132 " movl %%cr0, %%eax\n"
133 " orl $" __stringify(CR0_PE) ", %%eax\n"
134 " movl %%eax, %%cr0\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500135
136 // far jump to flush CPU queue after transition to protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500137 " ljmpw $(4<<3), $1f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500138
139 // GDT points to valid descriptor table, now load DS, ES
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500140 "1:movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00
141 " movw %%ax, %%ds\n"
142 " movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00
143 " movw %%ax, %%es\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500144
145 // move CX words from DS:SI to ES:DI
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500146 " xorw %%si, %%si\n"
147 " xorw %%di, %%di\n"
148 " rep movsw\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500149
Kevin O'Connorc35e1e52010-03-15 19:58:55 -0400150 // Restore DS and ES segment limits to 0xffff
151 " movw $(5<<3), %%ax\n" // 5th descriptor in table (SS)
152 " movw %%ax, %%ds\n"
153 " movw %%ax, %%es\n"
154
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500155 // Disable protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500156 " movl %%cr0, %%eax\n"
157 " andl $~" __stringify(CR0_PE) ", %%eax\n"
158 " movl %%eax, %%cr0\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500159
160 // far jump to flush CPU queue after transition to real mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500161 " ljmpw $" __stringify(SEG_BIOS) ", $2f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500162
163 // restore IDT to normal real-mode defaults
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500164 "2:lidtw %%cs:rmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500165
Kevin O'Connore20ed9f2008-03-01 14:25:44 -0500166 // Restore %ds (from %ss)
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500167 " movw %%ss, %%ax\n"
168 " movw %%ax, %%ds\n"
Kevin O'Connor41966da2012-05-28 18:57:06 -0400169 : "+c"(count), "+S"(si), "+m" (__segment_ES)
170 : : "eax", "di", "cc");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500171
172 set_a20(prev_a20_enable);
173
Kevin O'Connor6c781222008-03-09 12:19:23 -0400174 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500175}
176
177// Get the amount of extended memory (above 1M)
178static void
179handle_1588(struct bregs *regs)
180{
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500181 u32 rs = GET_GLOBAL(LegacyRamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400182
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500183 // According to Ralf Brown's interrupt the limit should be 15M,
184 // but real machines mostly return max. 63M.
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400185 if (rs > 64*1024*1024)
186 regs->ax = 63 * 1024;
187 else
188 regs->ax = (rs - 1*1024*1024) / 1024;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400189 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500190}
191
Kevin O'Connor643062f2010-01-04 20:48:20 -0500192// Switch to protected mode
193static void
194handle_1589(struct bregs *regs)
195{
196 set_a20(1);
197
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400198 pic_reset(regs->bl, regs->bh);
Kevin O'Connor643062f2010-01-04 20:48:20 -0500199
200 u64 *gdt_far = (void*)(regs->si + 0);
201 u16 gdt_seg = regs->es;
202 SET_FARVAR(gdt_seg, gdt_far[7], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1)
203 | GDT_BASE(BUILD_BIOS_ADDR));
204
205 regs->ds = 3<<3; // 3rd gdt descriptor is %ds
206 regs->es = 4<<3; // 4th gdt descriptor is %es
207 regs->code.seg = 6<<3; // 6th gdt descriptor is %cs
208
209 set_code_success(regs);
210
211 asm volatile(
212 // Load new descriptor tables
213 " lgdtw %%es:(1<<3)(%%si)\n"
214 " lidtw %%es:(2<<3)(%%si)\n"
215
216 // Enable protected mode
217 " movl %%cr0, %%eax\n"
218 " orl $" __stringify(CR0_PE) ", %%eax\n"
219 " movl %%eax, %%cr0\n"
220
221 // far jump to flush CPU queue after transition to protected mode
222 " ljmpw $(7<<3), $1f\n"
223
224 // GDT points to valid descriptor table, now load SS
225 "1:movw $(5<<3), %%ax\n"
226 " movw %%ax, %%ds\n"
227 " movw %%ax, %%ss\n"
228 :
Kevin O'Connor41966da2012-05-28 18:57:06 -0400229 : "S"(gdt_far), "m" (__segment_ES)
Kevin O'Connor643062f2010-01-04 20:48:20 -0500230 : "eax", "cc");
231}
232
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500233// Device busy interrupt. Called by Int 16h when no key available
234static void
235handle_1590(struct bregs *regs)
236{
237}
238
239// Interrupt complete. Called by Int 16h when key becomes available
240static void
241handle_1591(struct bregs *regs)
242{
243}
244
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500245// keyboard intercept
246static void
247handle_154f(struct bregs *regs)
248{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500249 set_invalid_silent(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500250}
251
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500252static void
253handle_15c0(struct bregs *regs)
254{
255 regs->es = SEG_BIOS;
Kevin O'Connor117fc212008-04-13 18:17:02 -0400256 regs->bx = (u32)&BIOS_CONFIG_TABLE;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400257 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500258}
259
260static void
261handle_15c1(struct bregs *regs)
262{
Kevin O'Connor08815372008-12-29 21:16:31 -0500263 regs->es = get_ebda_seg();
Kevin O'Connor6c781222008-03-09 12:19:23 -0400264 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500265}
266
267static void
268handle_15e801(struct bregs *regs)
269{
270 // my real system sets ax and bx to 0
271 // this is confirmed by Ralph Brown list
272 // but syslinux v1.48 is known to behave
273 // strangely if ax is set to 0
274 // regs.u.r16.ax = 0;
275 // regs.u.r16.bx = 0;
276
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500277 u32 rs = GET_GLOBAL(LegacyRamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400278
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500279 // Get the amount of extended memory (above 1M)
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400280 if (rs > 16*1024*1024) {
281 // limit to 15M
282 regs->cx = 15*1024;
283 // Get the amount of extended memory above 16M in 64k blocks
284 regs->dx = (rs - 16*1024*1024) / (64*1024);
285 } else {
286 regs->cx = (rs - 1*1024*1024) / 1024;
287 regs->dx = 0;
288 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500289
290 // Set configured memory equal to extended memory
291 regs->ax = regs->cx;
292 regs->bx = regs->dx;
293
Kevin O'Connor6c781222008-03-09 12:19:23 -0400294 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500295}
296
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500297static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500298handle_15e820(struct bregs *regs)
299{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500300 int count = GET_GLOBAL(e820_count);
Kevin O'Connorf969a082009-05-06 23:21:13 -0400301 if (regs->edx != 0x534D4150 || regs->bx >= count
302 || regs->ecx < sizeof(e820_list[0])) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500303 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500304 return;
305 }
306
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500307 memcpy_far(regs->es, (void*)(regs->di+0)
308 , get_global_seg(), &e820_list[regs->bx]
309 , sizeof(e820_list[0]));
Kevin O'Connorc7812932008-06-08 23:08:12 -0400310 if (regs->bx == count-1)
311 regs->ebx = 0;
312 else
313 regs->ebx++;
314 regs->eax = 0x534D4150;
Kevin O'Connordf2c19a2009-01-17 20:07:09 -0500315 regs->ecx = sizeof(e820_list[0]);
Kevin O'Connorc7812932008-06-08 23:08:12 -0400316 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500317}
318
319static void
320handle_15e8XX(struct bregs *regs)
321{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500322 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500323}
324
325static void
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500326handle_15e8(struct bregs *regs)
327{
328 switch (regs->al) {
329 case 0x01: handle_15e801(regs); break;
330 case 0x20: handle_15e820(regs); break;
331 default: handle_15e8XX(regs); break;
332 }
333}
334
335static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500336handle_15XX(struct bregs *regs)
337{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500338 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500339}
340
341// INT 15h System Services Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500342void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500343handle_15(struct bregs *regs)
344{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400345 debug_enter(regs, DEBUG_HDL_15);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500346 switch (regs->ah) {
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500347 case 0x24: handle_1524(regs); break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500348 case 0x4f: handle_154f(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500349 case 0x52: handle_1552(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500350 case 0x53: handle_1553(regs); break;
Kevin O'Connorcbffa8e2008-08-17 11:11:07 -0400351 case 0x5f: handle_155f(regs); break;
Jonathan A. Kollasche9a3e122013-10-20 12:48:56 -0500352 case 0x7f: handle_157f(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500353 case 0x83: handle_1583(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500354 case 0x86: handle_1586(regs); break;
355 case 0x87: handle_1587(regs); break;
356 case 0x88: handle_1588(regs); break;
Kevin O'Connor643062f2010-01-04 20:48:20 -0500357 case 0x89: handle_1589(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500358 case 0x90: handle_1590(regs); break;
359 case 0x91: handle_1591(regs); break;
360 case 0xc0: handle_15c0(regs); break;
361 case 0xc1: handle_15c1(regs); break;
362 case 0xc2: handle_15c2(regs); break;
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500363 case 0xe8: handle_15e8(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500364 default: handle_15XX(regs); break;
365 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500366}