blob: e8ed5a731b84246b8377f1090a30c687264ae2ef [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'Connor10ad7992009-10-24 11:06:08 -04008#include "util.h" // memcpy_far
Kevin O'Connor2ad37442008-05-06 19:49:01 -04009#include "biosvar.h" // BIOS_CONFIG_TABLE
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050010#include "ioport.h" // inb
Kevin O'Connorc7812932008-06-08 23:08:12 -040011#include "memmap.h" // E820_RAM
Kevin O'Connorf54c1502008-06-14 15:56:16 -040012#include "pic.h" // eoi_pic2
Kevin O'Connor9521e262008-07-04 13:04:29 -040013#include "bregs.h" // struct bregs
Kevin O'Connore2e5f012008-03-08 10:27:39 -050014
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050015// Use PS2 System Control port A to set A20 enable
16static inline u8
17set_a20(u8 cond)
18{
19 // get current setting first
20 u8 newval, oldval = inb(PORT_A20);
21 if (cond)
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050022 newval = oldval | A20_ENABLE_BIT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050023 else
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050024 newval = oldval & ~A20_ENABLE_BIT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050025 outb(newval, PORT_A20);
26
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050027 return (oldval & A20_ENABLE_BIT) != 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050028}
29
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050030static void
31handle_152400(struct bregs *regs)
32{
33 set_a20(0);
Kevin O'Connor6c781222008-03-09 12:19:23 -040034 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050035}
36
37static void
38handle_152401(struct bregs *regs)
39{
40 set_a20(1);
Kevin O'Connor6c781222008-03-09 12:19:23 -040041 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050042}
43
44static void
45handle_152402(struct bregs *regs)
46{
Kevin O'Connord9a8b2d2008-11-16 09:17:02 -050047 regs->al = (inb(PORT_A20) & A20_ENABLE_BIT) != 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -040048 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050049}
50
51static void
52handle_152403(struct bregs *regs)
53{
54 regs->bx = 3;
Kevin O'Connor6c781222008-03-09 12:19:23 -040055 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050056}
57
58static void
59handle_1524XX(struct bregs *regs)
60{
Kevin O'Connor6c781222008-03-09 12:19:23 -040061 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050062}
63
Kevin O'Connoradb6b372008-03-01 13:38:38 -050064static void
65handle_1524(struct bregs *regs)
66{
67 switch (regs->al) {
68 case 0x00: handle_152400(regs); break;
69 case 0x01: handle_152401(regs); break;
70 case 0x02: handle_152402(regs); break;
71 case 0x03: handle_152403(regs); break;
72 default: handle_1524XX(regs); break;
73 }
74}
75
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050076// removable media eject
77static void
78handle_1552(struct bregs *regs)
79{
Kevin O'Connor6c781222008-03-09 12:19:23 -040080 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050081}
82
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050083static void
84handle_1587(struct bregs *regs)
85{
86 // +++ should probably have descriptor checks
87 // +++ should have exception handlers
88
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050089 u8 prev_a20_enable = set_a20(1); // enable A20 line
90
91 // 128K max of transfer on 386+ ???
92 // source == destination ???
93
94 // ES:SI points to descriptor table
95 // offset use initially comments
96 // ==============================================
97 // 00..07 Unused zeros Null descriptor
98 // 08..0f GDT zeros filled in by BIOS
99 // 10..17 source ssssssss source of data
100 // 18..1f dest dddddddd destination of data
101 // 20..27 CS zeros filled in by BIOS
102 // 28..2f SS zeros filled in by BIOS
103
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500104// check for access rights of source & dest here
105
106 // Initialize GDT descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400107 u32 si = regs->si;
108 u64 *gdt_far = (void*)si;
109 u16 gdt_seg = regs->es;
110 u32 loc = (u32)MAKE_FLATPTR(gdt_seg, gdt_far);
111 SET_FARVAR(gdt_seg, gdt_far[1], GDT_DATA | GDT_LIMIT((6*sizeof(u64))-1)
112 | GDT_BASE(loc));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500113 // Initialize CS descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400114 SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | GDT_LIMIT(0x0ffff)
115 | GDT_BASE(0xf0000));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500116 // Initialize SS descriptor
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400117 loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0);
118 SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | GDT_LIMIT(0x0ffff)
119 | GDT_BASE(loc));
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500120
Kevin O'Connor2cdd8b62008-03-09 23:37:04 -0400121 u16 count = regs->cx;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500122 asm volatile(
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500123 // Load new descriptor tables
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400124 "lgdtw %%es:(1<<3)(%%si)\n"
Kevin O'Connor67c00dd2008-03-09 16:10:19 -0400125 "lidtw %%cs:pmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500126
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500127 // Enable protected mode
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500128 "movl %%cr0, %%eax\n"
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500129 "orl $" __stringify(CR0_PE) ", %%eax\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500130 "movl %%eax, %%cr0\n"
131
132 // far jump to flush CPU queue after transition to protected mode
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400133 "ljmpw $(4<<3), $1f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500134 "1:\n"
135
136 // GDT points to valid descriptor table, now load DS, ES
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400137 "movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500138 "movw %%ax, %%ds\n"
Kevin O'Connoreaa2e552009-08-10 00:03:04 -0400139 "movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500140 "movw %%ax, %%es\n"
141
142 // move CX words from DS:SI to ES:DI
143 "xorw %%si, %%si\n"
144 "xorw %%di, %%di\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500145 "rep movsw\n"
146
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500147 // Disable protected mode
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500148 "movl %%cr0, %%eax\n"
Kevin O'Connor3eac0092008-11-16 09:59:32 -0500149 "andl $~" __stringify(CR0_PE) ", %%eax\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500150 "movl %%eax, %%cr0\n"
151
152 // far jump to flush CPU queue after transition to real mode
Kevin O'Connore3677b12008-07-04 15:29:23 -0400153 "ljmpw $" __stringify(SEG_BIOS) ", $2f\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500154 "2:\n"
155
156 // restore IDT to normal real-mode defaults
Kevin O'Connor9649a962008-12-10 20:53:35 -0500157 "lidtw %%cs:rmode_IDT_info\n"
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500158
Kevin O'Connore20ed9f2008-03-01 14:25:44 -0500159 // Restore %ds (from %ss)
160 "movw %%ss, %%ax\n"
161 "movw %%ax, %%ds\n"
Kevin O'Connor2cdd8b62008-03-09 23:37:04 -0400162 : "+c"(count), "+S"(si)
Kevin O'Connored128492008-03-11 11:14:59 -0400163 : : "eax", "di", "cc"); // XXX - also clobbers %es
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500164
165 set_a20(prev_a20_enable);
166
Kevin O'Connor6c781222008-03-09 12:19:23 -0400167 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500168}
169
170// Get the amount of extended memory (above 1M)
171static void
172handle_1588(struct bregs *regs)
173{
Kevin O'Connore7916362008-12-28 22:03:17 -0500174 u32 rs = GET_GLOBAL(RamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400175
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500176 // According to Ralf Brown's interrupt the limit should be 15M,
177 // but real machines mostly return max. 63M.
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400178 if (rs > 64*1024*1024)
179 regs->ax = 63 * 1024;
180 else
181 regs->ax = (rs - 1*1024*1024) / 1024;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400182 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500183}
184
185// Device busy interrupt. Called by Int 16h when no key available
186static void
187handle_1590(struct bregs *regs)
188{
189}
190
191// Interrupt complete. Called by Int 16h when key becomes available
192static void
193handle_1591(struct bregs *regs)
194{
195}
196
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500197// keyboard intercept
198static void
199handle_154f(struct bregs *regs)
200{
Kevin O'Connordb9e65e2008-06-07 14:41:21 -0400201 set_fail_silent(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500202}
203
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500204static void
205handle_15c0(struct bregs *regs)
206{
207 regs->es = SEG_BIOS;
Kevin O'Connor117fc212008-04-13 18:17:02 -0400208 regs->bx = (u32)&BIOS_CONFIG_TABLE;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400209 set_code_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500210}
211
212static void
213handle_15c1(struct bregs *regs)
214{
Kevin O'Connor08815372008-12-29 21:16:31 -0500215 regs->es = get_ebda_seg();
Kevin O'Connor6c781222008-03-09 12:19:23 -0400216 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500217}
218
219static void
220handle_15e801(struct bregs *regs)
221{
222 // my real system sets ax and bx to 0
223 // this is confirmed by Ralph Brown list
224 // but syslinux v1.48 is known to behave
225 // strangely if ax is set to 0
226 // regs.u.r16.ax = 0;
227 // regs.u.r16.bx = 0;
228
Kevin O'Connore7916362008-12-28 22:03:17 -0500229 u32 rs = GET_GLOBAL(RamSize);
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400230
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500231 // Get the amount of extended memory (above 1M)
Kevin O'Connor9571ac22008-05-17 22:20:27 -0400232 if (rs > 16*1024*1024) {
233 // limit to 15M
234 regs->cx = 15*1024;
235 // Get the amount of extended memory above 16M in 64k blocks
236 regs->dx = (rs - 16*1024*1024) / (64*1024);
237 } else {
238 regs->cx = (rs - 1*1024*1024) / 1024;
239 regs->dx = 0;
240 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500241
242 // Set configured memory equal to extended memory
243 regs->ax = regs->cx;
244 regs->bx = regs->dx;
245
Kevin O'Connor6c781222008-03-09 12:19:23 -0400246 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500247}
248
Kevin O'Connord995b3d2008-11-08 13:05:27 -0500249// Info on e820 map location and size.
Kevin O'Connor372e0712009-09-09 09:51:31 -0400250struct e820entry e820_list[CONFIG_MAX_E820] VAR16VISIBLE;
251int e820_count VAR16VISIBLE;
Kevin O'Connord995b3d2008-11-08 13:05:27 -0500252
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500253static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500254handle_15e820(struct bregs *regs)
255{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500256 int count = GET_GLOBAL(e820_count);
Kevin O'Connorf969a082009-05-06 23:21:13 -0400257 if (regs->edx != 0x534D4150 || regs->bx >= count
258 || regs->ecx < sizeof(e820_list[0])) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400259 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500260 return;
261 }
262
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500263 memcpy_far(regs->es, (void*)(regs->di+0)
264 , get_global_seg(), &e820_list[regs->bx]
265 , sizeof(e820_list[0]));
Kevin O'Connorc7812932008-06-08 23:08:12 -0400266 if (regs->bx == count-1)
267 regs->ebx = 0;
268 else
269 regs->ebx++;
270 regs->eax = 0x534D4150;
Kevin O'Connordf2c19a2009-01-17 20:07:09 -0500271 regs->ecx = sizeof(e820_list[0]);
Kevin O'Connorc7812932008-06-08 23:08:12 -0400272 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500273}
274
275static void
276handle_15e8XX(struct bregs *regs)
277{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400278 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500279}
280
281static void
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500282handle_15e8(struct bregs *regs)
283{
284 switch (regs->al) {
285 case 0x01: handle_15e801(regs); break;
286 case 0x20: handle_15e820(regs); break;
287 default: handle_15e8XX(regs); break;
288 }
289}
290
291static void
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500292handle_15XX(struct bregs *regs)
293{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400294 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500295}
296
297// INT 15h System Services Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500298void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500299handle_15(struct bregs *regs)
300{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400301 debug_enter(regs, DEBUG_HDL_15);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500302 switch (regs->ah) {
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500303 case 0x24: handle_1524(regs); break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500304 case 0x4f: handle_154f(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500305 case 0x52: handle_1552(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500306 case 0x53: handle_1553(regs); break;
Kevin O'Connorcbffa8e2008-08-17 11:11:07 -0400307 case 0x5f: handle_155f(regs); break;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500308 case 0x83: handle_1583(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500309 case 0x86: handle_1586(regs); break;
310 case 0x87: handle_1587(regs); break;
311 case 0x88: handle_1588(regs); break;
312 case 0x90: handle_1590(regs); break;
313 case 0x91: handle_1591(regs); break;
314 case 0xc0: handle_15c0(regs); break;
315 case 0xc1: handle_15c1(regs); break;
316 case 0xc2: handle_15c2(regs); break;
Kevin O'Connoradb6b372008-03-01 13:38:38 -0500317 case 0xe8: handle_15e8(regs); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500318 default: handle_15XX(regs); break;
319 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500320}