Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / system.c
1 // Handler for int 0x15 "system" calls
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 LGPLv3 license.
7
8 #include "util.h" // memcpy_far
9 #include "biosvar.h" // BIOS_CONFIG_TABLE
10 #include "ioport.h" // inb
11 #include "memmap.h" // E820_RAM
12 #include "pic.h" // eoi_pic2
13 #include "bregs.h" // struct bregs
14
15 // Use PS2 System Control port A to set A20 enable
16 static inline u8
17 set_a20(u8 cond)
18 {
19     // get current setting first
20     u8 newval, oldval = inb(PORT_A20);
21     if (cond)
22         newval = oldval | A20_ENABLE_BIT;
23     else
24         newval = oldval & ~A20_ENABLE_BIT;
25     outb(newval, PORT_A20);
26
27     return (oldval & A20_ENABLE_BIT) != 0;
28 }
29
30 static void
31 handle_152400(struct bregs *regs)
32 {
33     set_a20(0);
34     set_code_success(regs);
35 }
36
37 static void
38 handle_152401(struct bregs *regs)
39 {
40     set_a20(1);
41     set_code_success(regs);
42 }
43
44 static void
45 handle_152402(struct bregs *regs)
46 {
47     regs->al = (inb(PORT_A20) & A20_ENABLE_BIT) != 0;
48     set_code_success(regs);
49 }
50
51 static void
52 handle_152403(struct bregs *regs)
53 {
54     regs->bx = 3;
55     set_code_success(regs);
56 }
57
58 static void
59 handle_1524XX(struct bregs *regs)
60 {
61     set_code_unimplemented(regs, RET_EUNSUPPORTED);
62 }
63
64 static void
65 handle_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
76 // removable media eject
77 static void
78 handle_1552(struct bregs *regs)
79 {
80     set_code_success(regs);
81 }
82
83 static void
84 handle_1587(struct bregs *regs)
85 {
86     // +++ should probably have descriptor checks
87     // +++ should have exception handlers
88
89     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
104 // check for access rights of source & dest here
105
106     // Initialize GDT descriptor
107     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));
113     // Initialize CS descriptor
114     SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1)
115                | GDT_BASE(BUILD_BIOS_ADDR));
116     // Initialize SS descriptor
117     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));
120
121     u16 count = regs->cx;
122     asm volatile(
123         // Load new descriptor tables
124         "  lgdtw %%es:(1<<3)(%%si)\n"
125         "  lidtw %%cs:pmode_IDT_info\n"
126
127         // Enable protected mode
128         "  movl %%cr0, %%eax\n"
129         "  orl $" __stringify(CR0_PE) ", %%eax\n"
130         "  movl %%eax, %%cr0\n"
131
132         // far jump to flush CPU queue after transition to protected mode
133         "  ljmpw $(4<<3), $1f\n"
134
135         // GDT points to valid descriptor table, now load DS, ES
136         "1:movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00
137         "  movw %%ax, %%ds\n"
138         "  movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00
139         "  movw %%ax, %%es\n"
140
141         // move CX words from DS:SI to ES:DI
142         "  xorw %%si, %%si\n"
143         "  xorw %%di, %%di\n"
144         "  rep movsw\n"
145
146         // Restore DS and ES segment limits to 0xffff
147         "  movw $(5<<3), %%ax\n" // 5th descriptor in table (SS)
148         "  movw %%ax, %%ds\n"
149         "  movw %%ax, %%es\n"
150
151         // Disable protected mode
152         "  movl %%cr0, %%eax\n"
153         "  andl $~" __stringify(CR0_PE) ", %%eax\n"
154         "  movl %%eax, %%cr0\n"
155
156         // far jump to flush CPU queue after transition to real mode
157         "  ljmpw $" __stringify(SEG_BIOS) ", $2f\n"
158
159         // restore IDT to normal real-mode defaults
160         "2:lidtw %%cs:rmode_IDT_info\n"
161
162         // Restore %ds (from %ss)
163         "  movw %%ss, %%ax\n"
164         "  movw %%ax, %%ds\n"
165         : "+c"(count), "+S"(si)
166         : : "eax", "di", "cc"); // XXX - also clobbers %es
167
168     set_a20(prev_a20_enable);
169
170     set_code_success(regs);
171 }
172
173 // Get the amount of extended memory (above 1M)
174 static void
175 handle_1588(struct bregs *regs)
176 {
177     u32 rs = GET_GLOBAL(RamSize);
178
179     // According to Ralf Brown's interrupt the limit should be 15M,
180     // but real machines mostly return max. 63M.
181     if (rs > 64*1024*1024)
182         regs->ax = 63 * 1024;
183     else
184         regs->ax = (rs - 1*1024*1024) / 1024;
185     set_success(regs);
186 }
187
188 // Switch to protected mode
189 static void
190 handle_1589(struct bregs *regs)
191 {
192     set_a20(1);
193
194     set_pics(regs->bl, regs->bh);
195
196     u64 *gdt_far = (void*)(regs->si + 0);
197     u16 gdt_seg = regs->es;
198     SET_FARVAR(gdt_seg, gdt_far[7], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1)
199                | GDT_BASE(BUILD_BIOS_ADDR));
200
201     regs->ds = 3<<3; // 3rd gdt descriptor is %ds
202     regs->es = 4<<3; // 4th gdt descriptor is %es
203     regs->code.seg = 6<<3; // 6th gdt descriptor is %cs
204
205     set_code_success(regs);
206
207     asm volatile(
208         // Load new descriptor tables
209         "  lgdtw %%es:(1<<3)(%%si)\n"
210         "  lidtw %%es:(2<<3)(%%si)\n"
211
212         // Enable protected mode
213         "  movl %%cr0, %%eax\n"
214         "  orl $" __stringify(CR0_PE) ", %%eax\n"
215         "  movl %%eax, %%cr0\n"
216
217         // far jump to flush CPU queue after transition to protected mode
218         "  ljmpw $(7<<3), $1f\n"
219
220         // GDT points to valid descriptor table, now load SS
221         "1:movw $(5<<3), %%ax\n"
222         "  movw %%ax, %%ds\n"
223         "  movw %%ax, %%ss\n"
224         :
225         : "S"(gdt_far)
226         : "eax", "cc");
227 }
228
229 // Device busy interrupt.  Called by Int 16h when no key available
230 static void
231 handle_1590(struct bregs *regs)
232 {
233 }
234
235 // Interrupt complete.  Called by Int 16h when key becomes available
236 static void
237 handle_1591(struct bregs *regs)
238 {
239 }
240
241 // keyboard intercept
242 static void
243 handle_154f(struct bregs *regs)
244 {
245     set_invalid_silent(regs);
246 }
247
248 static void
249 handle_15c0(struct bregs *regs)
250 {
251     regs->es = SEG_BIOS;
252     regs->bx = (u32)&BIOS_CONFIG_TABLE;
253     set_code_success(regs);
254 }
255
256 static void
257 handle_15c1(struct bregs *regs)
258 {
259     regs->es = get_ebda_seg();
260     set_success(regs);
261 }
262
263 static void
264 handle_15e801(struct bregs *regs)
265 {
266     // my real system sets ax and bx to 0
267     // this is confirmed by Ralph Brown list
268     // but syslinux v1.48 is known to behave
269     // strangely if ax is set to 0
270     // regs.u.r16.ax = 0;
271     // regs.u.r16.bx = 0;
272
273     u32 rs = GET_GLOBAL(RamSize);
274
275     // Get the amount of extended memory (above 1M)
276     if (rs > 16*1024*1024) {
277         // limit to 15M
278         regs->cx = 15*1024;
279         // Get the amount of extended memory above 16M in 64k blocks
280         regs->dx = (rs - 16*1024*1024) / (64*1024);
281     } else {
282         regs->cx = (rs - 1*1024*1024) / 1024;
283         regs->dx = 0;
284     }
285
286     // Set configured memory equal to extended memory
287     regs->ax = regs->cx;
288     regs->bx = regs->dx;
289
290     set_success(regs);
291 }
292
293 // Info on e820 map location and size.
294 struct e820entry e820_list[CONFIG_MAX_E820] VAR16VISIBLE;
295 int e820_count VAR16VISIBLE;
296
297 static void
298 handle_15e820(struct bregs *regs)
299 {
300     int count = GET_GLOBAL(e820_count);
301     if (regs->edx != 0x534D4150 || regs->bx >= count
302         || regs->ecx < sizeof(e820_list[0])) {
303         set_code_invalid(regs, RET_EUNSUPPORTED);
304         return;
305     }
306
307     memcpy_far(regs->es, (void*)(regs->di+0)
308                , get_global_seg(), &e820_list[regs->bx]
309                , sizeof(e820_list[0]));
310     if (regs->bx == count-1)
311         regs->ebx = 0;
312     else
313         regs->ebx++;
314     regs->eax = 0x534D4150;
315     regs->ecx = sizeof(e820_list[0]);
316     set_success(regs);
317 }
318
319 static void
320 handle_15e8XX(struct bregs *regs)
321 {
322     set_code_unimplemented(regs, RET_EUNSUPPORTED);
323 }
324
325 static void
326 handle_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
335 static void
336 handle_15XX(struct bregs *regs)
337 {
338     set_code_unimplemented(regs, RET_EUNSUPPORTED);
339 }
340
341 // INT 15h System Services Entry Point
342 void VISIBLE16
343 handle_15(struct bregs *regs)
344 {
345     debug_enter(regs, DEBUG_HDL_15);
346     switch (regs->ah) {
347     case 0x24: handle_1524(regs); break;
348     case 0x4f: handle_154f(regs); break;
349     case 0x52: handle_1552(regs); break;
350     case 0x53: handle_1553(regs); break;
351     case 0x5f: handle_155f(regs); break;
352     case 0x83: handle_1583(regs); break;
353     case 0x86: handle_1586(regs); break;
354     case 0x87: handle_1587(regs); break;
355     case 0x88: handle_1588(regs); break;
356     case 0x89: handle_1589(regs); break;
357     case 0x90: handle_1590(regs); break;
358     case 0x91: handle_1591(regs); break;
359     case 0xc0: handle_15c0(regs); break;
360     case 0xc1: handle_15c1(regs); break;
361     case 0xc2: handle_15c2(regs); break;
362     case 0xe8: handle_15e8(regs); break;
363     default:   handle_15XX(regs); break;
364     }
365 }