blob: 3cb2228f520d572551a203fa8d101a44484aa0d0 [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'Connor6c68e7a2014-04-19 12:22:22 -0400118 u64 lim = GDT_LIMIT(0x0ffff);
119 if (in_post())
120 lim = GDT_GRANLIMIT(0xffffffff);
121 SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | lim | GDT_BASE(BUILD_BIOS_ADDR));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500122 // Initialize SS descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400123 loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0);
Kevin O'Connor6c68e7a2014-04-19 12:22:22 -0400124 SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | lim | GDT_BASE(loc));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500125
Kevin O'Connor2cdd8b62008-03-09 23:37:04 -0400126 u16 count = regs->cx;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500127 asm volatile(
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500128 // Load new descriptor tables
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500129 " lgdtw %%es:(1<<3)(%%si)\n"
130 " lidtw %%cs:pmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500131
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500132 // Enable protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500133 " movl %%cr0, %%eax\n"
134 " orl $" __stringify(CR0_PE) ", %%eax\n"
135 " movl %%eax, %%cr0\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500136
137 // far jump to flush CPU queue after transition to protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500138 " ljmpw $(4<<3), $1f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500139
140 // GDT points to valid descriptor table, now load DS, ES
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500141 "1:movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00
142 " movw %%ax, %%ds\n"
143 " movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00
144 " movw %%ax, %%es\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500145
146 // move CX words from DS:SI to ES:DI
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500147 " xorw %%si, %%si\n"
148 " xorw %%di, %%di\n"
149 " rep movsw\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500150
Kevin O'Connorc35e1e52010-03-15 19:58:55 -0400151 // Restore DS and ES segment limits to 0xffff
152 " movw $(5<<3), %%ax\n" // 5th descriptor in table (SS)
153 " movw %%ax, %%ds\n"
154 " movw %%ax, %%es\n"
155
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500156 // Disable protected mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500157 " movl %%cr0, %%eax\n"
158 " andl $~" __stringify(CR0_PE) ", %%eax\n"
159 " movl %%eax, %%cr0\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500160
161 // far jump to flush CPU queue after transition to real mode
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500162 " ljmpw $" __stringify(SEG_BIOS) ", $2f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500163
164 // restore IDT to normal real-mode defaults
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500165 "2:lidtw %%cs:rmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500166
Kevin O'Connore20ed9f2008-03-01 14:25:44 -0500167 // Restore %ds (from %ss)
Kevin O'Connor7e6bd3e2010-01-04 21:04:04 -0500168 " movw %%ss, %%ax\n"
169 " movw %%ax, %%ds\n"
Kevin O'Connor41966da2012-05-28 18:57:06 -0400170 : "+c"(count), "+S"(si), "+m" (__segment_ES)
171 : : "eax", "di", "cc");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500172
173 set_a20(prev_a20_enable);
174
Kevin O'Connor6c781222008-03-09 12:19:23 -0400175 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500176}
177
178// Get the amount of extended memory (above 1M)
179static void
180handle_1588(struct bregs *regs)
181{
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500182 u32 rs = GET_GLOBAL(LegacyRamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400183
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500184 // According to Ralf Brown's interrupt the limit should be 15M,
185 // but real machines mostly return max. 63M.
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400186 if (rs > 64*1024*1024)
187 regs->ax = 63 * 1024;
188 else
189 regs->ax = (rs - 1*1024*1024) / 1024;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400190 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500191}
192
Kevin O'Connor643062f2010-01-04 20:48:20 -0500193// Switch to protected mode
194static void
195handle_1589(struct bregs *regs)
196{
197 set_a20(1);
198
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400199 pic_reset(regs->bl, regs->bh);
Kevin O'Connor643062f2010-01-04 20:48:20 -0500200
201 u64 *gdt_far = (void*)(regs->si + 0);
202 u16 gdt_seg = regs->es;
203 SET_FARVAR(gdt_seg, gdt_far[7], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1)
204 | GDT_BASE(BUILD_BIOS_ADDR));
205
206 regs->ds = 3<<3; // 3rd gdt descriptor is %ds
207 regs->es = 4<<3; // 4th gdt descriptor is %es
208 regs->code.seg = 6<<3; // 6th gdt descriptor is %cs
209
210 set_code_success(regs);
211
212 asm volatile(
213 // Load new descriptor tables
214 " lgdtw %%es:(1<<3)(%%si)\n"
215 " lidtw %%es:(2<<3)(%%si)\n"
216
217 // Enable protected mode
218 " movl %%cr0, %%eax\n"
219 " orl $" __stringify(CR0_PE) ", %%eax\n"
220 " movl %%eax, %%cr0\n"
221
222 // far jump to flush CPU queue after transition to protected mode
223 " ljmpw $(7<<3), $1f\n"
224
225 // GDT points to valid descriptor table, now load SS
226 "1:movw $(5<<3), %%ax\n"
227 " movw %%ax, %%ds\n"
228 " movw %%ax, %%ss\n"
229 :
Kevin O'Connor41966da2012-05-28 18:57:06 -0400230 : "S"(gdt_far), "m" (__segment_ES)
Kevin O'Connor643062f2010-01-04 20:48:20 -0500231 : "eax", "cc");
232}
233
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500234// Device busy interrupt. Called by Int 16h when no key available
235static void
236handle_1590(struct bregs *regs)
237{
238}
239
240// Interrupt complete. Called by Int 16h when key becomes available
241static void
242handle_1591(struct bregs *regs)
243{
244}
245
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500246// keyboard intercept
247static void
248handle_154f(struct bregs *regs)
249{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500250 set_invalid_silent(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500251}
252
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500253static void
254handle_15c0(struct bregs *regs)
255{
256 regs->es = SEG_BIOS;
Kevin O'Connor117fc212008-04-13 18:17:02 -0400257 regs->bx = (u32)&BIOS_CONFIG_TABLE;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400258 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500259}
260
261static void
262handle_15c1(struct bregs *regs)
263{
Kevin O'Connor08815372008-12-29 21:16:31 -0500264 regs->es = get_ebda_seg();
Kevin O'Connor6c781222008-03-09 12:19:23 -0400265 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500266}
267
268static void
269handle_15e801(struct bregs *regs)
270{
271 // my real system sets ax and bx to 0
272 // this is confirmed by Ralph Brown list
273 // but syslinux v1.48 is known to behave
274 // strangely if ax is set to 0
275 // regs.u.r16.ax = 0;
276 // regs.u.r16.bx = 0;
277
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500278 u32 rs = GET_GLOBAL(LegacyRamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400279
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500280 // Get the amount of extended memory (above 1M)
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400281 if (rs > 16*1024*1024) {
282 // limit to 15M
283 regs->cx = 15*1024;
284 // Get the amount of extended memory above 16M in 64k blocks
285 regs->dx = (rs - 16*1024*1024) / (64*1024);
286 } else {
287 regs->cx = (rs - 1*1024*1024) / 1024;
288 regs->dx = 0;
289 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500290
291 // Set configured memory equal to extended memory
292 regs->ax = regs->cx;
293 regs->bx = regs->dx;
294
Kevin O'Connor6c781222008-03-09 12:19:23 -0400295 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500296}
297
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500298static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500299handle_15e820(struct bregs *regs)
300{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500301 int count = GET_GLOBAL(e820_count);
Kevin O'Connorf969a082009-05-06 23:21:13 -0400302 if (regs->edx != 0x534D4150 || regs->bx >= count
303 || regs->ecx < sizeof(e820_list[0])) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500304 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500305 return;
306 }
307
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500308 memcpy_far(regs->es, (void*)(regs->di+0)
309 , get_global_seg(), &e820_list[regs->bx]
310 , sizeof(e820_list[0]));
Kevin O'Connorc7812932008-06-08 23:08:12 -0400311 if (regs->bx == count-1)
312 regs->ebx = 0;
313 else
314 regs->ebx++;
315 regs->eax = 0x534D4150;
Kevin O'Connordf2c19a2009-01-17 20:07:09 -0500316 regs->ecx = sizeof(e820_list[0]);
Kevin O'Connorc7812932008-06-08 23:08:12 -0400317 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500318}
319
320static void
321handle_15e8XX(struct bregs *regs)
322{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500323 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500324}
325
326static void
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500327handle_15e8(struct bregs *regs)
328{
329 switch (regs->al) {
330 case 0x01: handle_15e801(regs); break;
331 case 0x20: handle_15e820(regs); break;
332 default: handle_15e8XX(regs); break;
333 }
334}
335
336static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500337handle_15XX(struct bregs *regs)
338{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500339 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500340}
341
342// INT 15h System Services Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500343void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500344handle_15(struct bregs *regs)
345{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400346 debug_enter(regs, DEBUG_HDL_15);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500347 switch (regs->ah) {
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500348 case 0x24: handle_1524(regs); break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500349 case 0x4f: handle_154f(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500350 case 0x52: handle_1552(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500351 case 0x53: handle_1553(regs); break;
Kevin O'Connorcbffa8e2008-08-17 11:11:07 -0400352 case 0x5f: handle_155f(regs); break;
Jonathan A. Kollasche9a3e122013-10-20 12:48:56 -0500353 case 0x7f: handle_157f(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500354 case 0x83: handle_1583(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500355 case 0x86: handle_1586(regs); break;
356 case 0x87: handle_1587(regs); break;
357 case 0x88: handle_1588(regs); break;
Kevin O'Connor643062f2010-01-04 20:48:20 -0500358 case 0x89: handle_1589(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500359 case 0x90: handle_1590(regs); break;
360 case 0x91: handle_1591(regs); break;
361 case 0xc0: handle_15c0(regs); break;
362 case 0xc1: handle_15c1(regs); break;
363 case 0xc2: handle_15c2(regs); break;
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500364 case 0xe8: handle_15e8(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500365 default: handle_15XX(regs); break;
366 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500367}