VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/system.c@ 60610

Last change on this file since 60610 was 60610, checked in by vboxsync, 9 years ago

BIOS: Added 286 compatible INT 15h/87h implementation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.6 KB
Line 
1/*
2 * Copyright (C) 2006-2016 Oracle Corporation
3 *
4 * This file is part of VirtualBox Open Source Edition (OSE), as
5 * available from http://www.virtualbox.org. This file is free software;
6 * you can redistribute it and/or modify it under the terms of the GNU
7 * General Public License (GPL) as published by the Free Software
8 * Foundation, in version 2 as it comes in the "COPYING" file of the
9 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
10 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
11 * --------------------------------------------------------------------
12 *
13 * This code is based on:
14 *
15 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
16 *
17 * Copyright (C) 2002 MandrakeSoft S.A.
18 *
19 * MandrakeSoft S.A.
20 * 43, rue d'Aboukir
21 * 75002 Paris - France
22 * http://www.linux-mandrake.com/
23 * http://www.mandrakesoft.com/
24 *
25 * This library is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU Lesser General Public
27 * License as published by the Free Software Foundation; either
28 * version 2 of the License, or (at your option) any later version.
29 *
30 * This library is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 * Lesser General Public License for more details.
34 *
35 * You should have received a copy of the GNU Lesser General Public
36 * License along with this library; if not, write to the Free Software
37 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
38 *
39 */
40
41
42#include <stdint.h>
43#include "biosint.h"
44#include "inlines.h"
45
46#if DEBUG_INT15
47# define BX_DEBUG_INT15(...) BX_DEBUG(__VA_ARGS__)
48#else
49# define BX_DEBUG_INT15(...)
50#endif
51
52
53#define UNSUPPORTED_FUNCTION 0x86 /* Specific to INT 15h. */
54
55#define BIOS_CONFIG_TABLE 0xe6f5 /* TODO: configurable? put elsewhere? */
56
57#define ACPI_DATA_SIZE 0x00010000L /* TODO: configurable? put elsewhere? */
58
59#define BX_CPU 3
60
61extern int pmode_IDT;
62extern int rmode_IDT;
63
64uint16_t read_ss(void);
65#pragma aux read_ss = "mov ax, ss" modify exact [ax] nomemory;
66
67#if VBOX_BIOS_CPU >= 80386
68
69/* The 386+ code uses CR0 to switch to/from protected mode.
70 * Quite straightforward.
71 */
72
73void pm_stack_save(uint16_t cx, uint16_t es, uint16_t si, uint16_t frame);
74#pragma aux pm_stack_save = \
75 ".386" \
76 "push ds" \
77 "push eax" \
78 "xor eax, eax" \
79 "mov ds, ax" \
80 "mov ds:[467h], sp" \
81 "mov ds:[469h], ss" \
82 parm [cx] [es] [si] [ax] modify nomemory;
83
84/* Uses position independent code... because it was too hard to figure
85 * out how to code the far call in inline assembler.
86 */
87void pm_enter(void);
88#pragma aux pm_enter = \
89 ".386p" \
90 "call pentry" \
91 "pentry:" \
92 "pop di" \
93 "add di, 1Bh" \
94 "push 20h" \
95 "push di" \
96 "lgdt fword ptr es:[si+8]" \
97 "lidt fword ptr cs:pmode_IDT" \
98 "mov eax, cr0" \
99 "or al, 1" \
100 "mov cr0, eax" \
101 "retf" \
102 "pm_pm:" \
103 "mov ax, 28h" \
104 "mov ss, ax" \
105 "mov ax, 10h" \
106 "mov ds, ax" \
107 "mov ax, 18h" \
108 "mov es, ax" \
109 modify nomemory;
110
111/* Restore segment limits to real mode compatible values and
112 * return to real mode.
113 */
114void pm_exit(void);
115#pragma aux pm_exit = \
116 ".386p" \
117 "call pexit" \
118 "pexit:" \
119 "pop ax" \
120 "push 0F000h" \
121 "add ax, 18h" \
122 "push ax" \
123 "mov ax, 28h" \
124 "mov ds, ax" \
125 "mov es, ax" \
126 "mov eax, cr0" \
127 "and al, 0FEh" \
128 "mov cr0, eax" \
129 "retf" \
130 "real_mode:" \
131 "lidt fword ptr cs:rmode_IDT" \
132 modify nomemory;
133
134/* Restore stack and reload segment registers in real mode to ensure
135 * real mode compatible selector+base.
136 */
137void pm_stack_restore(void);
138#pragma aux pm_stack_restore = \
139 ".386" \
140 "xor ax, ax" \
141 "mov ds, ax" \
142 "mov es, ax" \
143 "lss sp, ds:[467h]" \
144 "pop eax" \
145 "pop ds" \
146 modify nomemory;
147
148#elif VBOX_BIOS_CPU >= 80286
149
150/* The 286 code uses LMSW to switch to protected mode but it has to reset
151 * the CPU to get back to real mode. Ugly! See return_blkmove in orgs.asm
152 * for the other matching half.
153 */
154void pm_stack_save(uint16_t cx, uint16_t es, uint16_t si, uint16_t frame);
155#pragma aux pm_stack_save = \
156 "xor ax, ax" \
157 "mov ds, ax" \
158 "mov ds:[467h], bx" \
159 "mov ds:[469h], ss" \
160 parm [cx] [es] [si] [bx] modify nomemory;
161
162/* Uses position independent code... because it was too hard to figure
163 * out how to code the far call in inline assembler.
164 * NB: Trashes MSW bits but the CPU will be reset anyway.
165 */
166void pm_enter(void);
167#pragma aux pm_enter = \
168 ".286p" \
169 "call pentry" \
170 "pentry:" \
171 "pop di" \
172 "add di, 18h" \
173 "push 20h" \
174 "push di" \
175 "lgdt fword ptr es:[si+8]" \
176 "lidt fword ptr cs:pmode_IDT" \
177 "or al, 1" \
178 "lmsw ax" \
179 "retf" \
180 "pm_pm:" \
181 "mov ax, 28h" \
182 "mov ss, ax" \
183 "mov ax, 10h" \
184 "mov ds, ax" \
185 "mov ax, 18h" \
186 "mov es, ax" \
187 modify nomemory;
188
189/* Set up shutdown status and reset the CPU. The POST code
190 * will regain control. Port 80h is written with status.
191 * Code 9 is written to CMOS shutdown status byte (0Fh).
192 * CPU is triple faulted. .
193 */
194void pm_exit(void);
195#pragma aux pm_exit = \
196 "xor ax, ax" \
197 "out 80h, al" \
198 "mov al, 0Fh" \
199 "out 70h, al" \
200 "mov al, 09h" \
201 "out 71h, al" \
202 ".286p" \
203 "lidt fword ptr cs:pmode_IDT" \
204 "int 3" \
205 modify nomemory;
206
207/* Dummy. Actually done in return_blkmove. */
208void pm_stack_restore(void);
209#pragma aux pm_stack_restore = \
210 "rm_return:" \
211 modify nomemory;
212
213#endif
214
215void pm_copy(void);
216#pragma aux pm_copy = \
217 "xor si, si" \
218 "xor di, di" \
219 "cld" \
220 "rep movsw" \
221 modify nomemory;
222
223/* The pm_switch has a few crucial differences from pm_enter, hence
224 * it is replicated here. Uses LMSW to avoid trashing high word of eax.
225 */
226void pm_switch(uint16_t reg_si);
227#pragma aux pm_switch = \
228 ".286p" \
229 "call pentry" \
230 "pentry:" \
231 "pop di" \
232 "add di, 18h" \
233 "push 38h" \
234 "push di" \
235 "lgdt fword ptr es:[si+08h]" \
236 "lidt fword ptr es:[si+10h]" \
237 "mov ax, 1" \
238 "lmsw ax" \
239 "retf" \
240 "pm_pm:" \
241 "mov ax, 28h" \
242 "mov ss, ax" \
243 "mov ax, 18h" \
244 "mov ds, ax" \
245 "mov ax, 20h" \
246 "mov es, ax" \
247 parm [si] modify nomemory;
248
249/* Return to caller - we do not use IRET because we should not enable
250 * interrupts. Note that AH must be zero on exit.
251 * WARNING: Needs to be adapted if calling sequence is modified!
252 */
253void pm_unwind(uint16_t args);
254#pragma aux pm_unwind = \
255 ".286" \
256 "mov sp, ax" \
257 "popa" \
258 "add sp, 6" \
259 "pop cx" \
260 "pop ax" \
261 "pop ax" \
262 "mov ax, 30h" \
263 "push ax" \
264 "push cx" \
265 "retf" \
266 parm [ax] modify nomemory aborts;
267
268// @todo: This method is silly. The RTC should be programmed to fire an interrupt
269// instead of hogging the CPU with inaccurate code.
270void timer_wait(uint16_t lo, uint16_t hi);
271#pragma aux timer_wait = \
272 ".386" \
273 "shl eax, 16" \
274 "mov ax, dx" \
275 "mov ebx, 15" \
276 "xor edx, edx" \
277 "div ebx" \
278 "mov ecx, eax" \
279 "in al, 61h" \
280 "and al, 10h" \
281 "mov ah, al" \
282 "or ecx, ecx" \
283 "je int1586_tick_end" \
284 "int1586_tick:" \
285 "in al, 61h" \
286 "and al, 10h" \
287 "cmp al, ah" \
288 "je int1586_tick" \
289 "mov ah, al" \
290 "dec ecx" \
291 "jnz int1586_tick" \
292 "int1586_tick_end:" \
293 parm [dx] [ax] modify [bx cx] nomemory;
294
295
296bx_bool set_enable_a20(bx_bool val)
297{
298 uint8_t oldval;
299
300 // Use PS/2 System Control port A to set A20 enable
301
302 // get current setting first
303 oldval = inb(0x92);
304
305 // change A20 status
306 if (val)
307 outb(0x92, oldval | 0x02);
308 else
309 outb(0x92, oldval & 0xfd);
310
311 return((oldval & 0x02) != 0);
312}
313
314typedef struct {
315 uint32_t start;
316 uint32_t xstart;
317 uint32_t len;
318 uint32_t xlen;
319 uint32_t type;
320} mem_range_t;
321
322void set_e820_range(uint16_t ES, uint16_t DI, uint32_t start, uint32_t end,
323 uint8_t extra_start, uint8_t extra_end, uint16_t type)
324{
325 mem_range_t __far *range;
326
327 range = ES :> (mem_range_t *)DI;
328 range->start = start;
329 range->xstart = extra_start;
330 end -= start;
331 extra_end -= extra_start;
332 range->len = end;
333 range->xlen = extra_end;
334 range->type = type;
335}
336
337// @todo: move elsewhere?
338#define AX r.gr.u.r16.ax
339#define BX r.gr.u.r16.bx
340#define CX r.gr.u.r16.cx
341#define DX r.gr.u.r16.dx
342#define SI r.gr.u.r16.si
343#define DI r.gr.u.r16.di
344#define BP r.gr.u.r16.bp
345#define SP r.gr.u.r16.sp
346#define FLAGS r.fl.u.r16.flags
347#define EAX r.gr.u.r32.eax
348#define EBX r.gr.u.r32.ebx
349#define ECX r.gr.u.r32.ecx
350#define EDX r.gr.u.r32.edx
351#define ESI r.gr.u.r32.esi
352#define EDI r.gr.u.r32.edi
353#define ES r.es
354
355
356void BIOSCALL int15_function(sys_regs_t r)
357{
358 uint16_t bRegister;
359 uint8_t irqDisable;
360
361 BX_DEBUG_INT15("int15 AX=%04x\n",AX);
362
363 switch (GET_AH()) {
364 case 0x00: /* assorted functions */
365 if (GET_AL() != 0xc0)
366 goto undecoded;
367 /* GRUB calls int15 with ax=0x00c0 to get the ROM configuration table,
368 * which we don't support, but logging that event is annoying. In fact
369 * it is likely that they just misread some specs, because there is a
370 * int15 BIOS function AH=0xc0 which sounds quite similar to what GRUB
371 * wants to achieve. */
372 SET_CF();
373 SET_AH(UNSUPPORTED_FUNCTION);
374 break;
375 case 0x24: /* A20 Control */
376 switch (GET_AL()) {
377 case 0x00:
378 set_enable_a20(0);
379 CLEAR_CF();
380 SET_AH(0);
381 break;
382 case 0x01:
383 set_enable_a20(1);
384 CLEAR_CF();
385 SET_AH(0);
386 break;
387 case 0x02:
388 SET_AL( (inb(0x92) >> 1) & 0x01 );
389 CLEAR_CF();
390 SET_AH(0);
391 break;
392 case 0x03:
393 CLEAR_CF();
394 SET_AH(0);
395 BX = 3;
396 break;
397 default:
398 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) GET_AL());
399 SET_CF();
400 SET_AH(UNSUPPORTED_FUNCTION);
401 }
402 break;
403
404 case 0x41:
405 SET_CF();
406 SET_AH(UNSUPPORTED_FUNCTION);
407 break;
408
409 //@todo: Why does this need special handling? All we need is to set CF
410 // but not handle this as an unknown function (regardless of CPU type).
411 case 0x4f:
412 /* keyboard intercept */
413#if BX_CPU < 2
414 SET_AH(UNSUPPORTED_FUNCTION);
415#else
416 // nop
417#endif
418 SET_CF();
419 break;
420
421 case 0x52: // removable media eject
422 CLEAR_CF();
423 SET_AH(0); // "ok ejection may proceed"
424 break;
425
426 case 0x83: {
427 if( GET_AL() == 0 ) {
428 // Set Interval requested.
429 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
430 // Interval not already set.
431 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
432 write_word( 0x40, 0x98, ES ); // Byte location, segment
433 write_word( 0x40, 0x9A, BX ); // Byte location, offset
434 write_word( 0x40, 0x9C, DX ); // Low word, delay
435 write_word( 0x40, 0x9E, CX ); // High word, delay.
436 CLEAR_CF( );
437 irqDisable = inb( 0xA1 );
438 outb( 0xA1, irqDisable & 0xFE );
439 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
440 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
441 } else {
442 // Interval already set.
443 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
444 SET_CF();
445 SET_AH(UNSUPPORTED_FUNCTION);
446 }
447 } else if( GET_AL() == 1 ) {
448 // Clear Interval requested
449 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
450 CLEAR_CF( );
451 bRegister = inb_cmos( 0xB );
452 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
453 } else {
454 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
455 SET_CF();
456 SET_AH(UNSUPPORTED_FUNCTION);
457 SET_AL(GET_AL() - 1);
458 }
459
460 break;
461 }
462
463 case 0x88:
464 // Get the amount of extended memory (above 1M)
465#if BX_CPU < 2
466 SET_AH(UNSUPPORTED_FUNCTION);
467 SET_CF();
468#else
469 AX = (inb_cmos(0x31) << 8) | inb_cmos(0x30);
470
471 // According to Ralf Brown's interrupt the limit should be 15M,
472 // but real machines mostly return max. 63M.
473 if(AX > 0xffc0)
474 AX = 0xffc0;
475
476 CLEAR_CF();
477#endif
478 break;
479
480 case 0x89:
481 // Switch to Protected Mode.
482 // ES:DI points to user-supplied GDT
483 // BH/BL contains starting interrupt numbers for PIC0/PIC1
484 // This subfunction does not return!
485
486 // turn off interrupts
487 int_disable(); //@todo: aren't they off already?
488
489 set_enable_a20(1); // enable A20 line; we're supposed to fail if that fails
490
491 // Initialize CS descriptor for BIOS
492 write_word(ES, SI+0x38+0, 0xffff);// limit 15:00 = normal 64K limit
493 write_word(ES, SI+0x38+2, 0x0000);// base 15:00
494 write_byte(ES, SI+0x38+4, 0x000f);// base 23:16 (hardcoded to f000:0000)
495 write_byte(ES, SI+0x38+5, 0x9b); // access
496 write_word(ES, SI+0x38+6, 0x0000);// base 31:24/reserved/limit 19:16
497
498 /* Reprogram the PICs. */
499 outb(PIC_MASTER, PIC_CMD_INIT);
500 outb(PIC_SLAVE, PIC_CMD_INIT);
501 outb(PIC_MASTER + 1, GET_BH());
502 outb(PIC_SLAVE + 1, GET_BL());
503 outb(PIC_MASTER + 1, 4);
504 outb(PIC_SLAVE + 1, 2);
505 outb(PIC_MASTER + 1, 1);
506 outb(PIC_SLAVE + 1, 1);
507 /* Mask all IRQs, user must re-enable. */
508 outb(PIC_MASTER_MASK, 0xff);
509 outb(PIC_SLAVE_MASK, 0xff);
510
511 pm_switch(SI);
512 pm_unwind((uint16_t)&r);
513
514 break;
515
516 case 0x90:
517 /* Device busy interrupt. Called by Int 16h when no key available */
518 break;
519
520 case 0x91:
521 /* Interrupt complete. Called by Int 16h when key becomes available */
522 break;
523
524 case 0xbf:
525 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
526 SET_CF();
527 SET_AH(UNSUPPORTED_FUNCTION);
528 break;
529
530 case 0xC0:
531 CLEAR_CF();
532 SET_AH(0);
533 BX = BIOS_CONFIG_TABLE;
534 ES = 0xF000;
535 break;
536
537 case 0xc1:
538 ES = read_word(0x0040, 0x000E);
539 CLEAR_CF();
540 break;
541
542 case 0xd8:
543 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
544 SET_CF();
545 SET_AH(UNSUPPORTED_FUNCTION);
546 break;
547
548 /* Make the BIOS warning for pretty much every Linux kernel start
549 * disappear - it calls with ax=0xe980 to figure out SMI info. */
550 case 0xe9: /* SMI functions (SpeedStep and similar things) */
551 SET_CF();
552 SET_AH(UNSUPPORTED_FUNCTION);
553 break;
554 case 0xec: /* AMD64 target operating mode callback */
555 if (GET_AL() != 0)
556 goto undecoded;
557 SET_AH(0);
558 if (GET_BL() >= 1 && GET_BL() <= 3)
559 CLEAR_CF(); /* Accepted value. */
560 else
561 SET_CF(); /* Reserved, error. */
562 break;
563undecoded:
564 default:
565 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
566 (unsigned) AX, (unsigned) BX);
567 SET_CF();
568 SET_AH(UNSUPPORTED_FUNCTION);
569 break;
570 }
571}
572
573void BIOSCALL int15_function32(sys32_regs_t r)
574{
575 uint32_t extended_memory_size=0; // 64bits long
576 uint32_t extra_lowbits_memory_size=0;
577 uint8_t extra_highbits_memory_size=0;
578 uint32_t mcfgStart, mcfgSize;
579
580 BX_DEBUG_INT15("int15 AX=%04x\n",AX);
581
582 switch (GET_AH()) {
583 case 0x86:
584 // Wait for CX:DX microseconds. currently using the
585 // refresh request port 0x61 bit4, toggling every 15usec
586 int_enable();
587 timer_wait(DX, CX);
588 break;
589
590 case 0xd0:
591 if (GET_AL() != 0x4f)
592 goto int15_unimplemented;
593 if (EBX == 0x50524f43 && ECX == 0x4d4f4445 && ESI == 0 && EDI == 0)
594 {
595 CLEAR_CF();
596 ESI = EBX;
597 EDI = ECX;
598 EAX = 0x49413332;
599 }
600 else
601 goto int15_unimplemented;
602 break;
603
604 case 0xe8:
605 switch(GET_AL()) {
606 case 0x20: // coded by osmaker aka K.J.
607 if(EDX == 0x534D4150) {
608 extended_memory_size = inb_cmos(0x35);
609 extended_memory_size <<= 8;
610 extended_memory_size |= inb_cmos(0x34);
611 extended_memory_size *= 64;
612#ifndef VBOX /* The following excludes 0xf0000000 thru 0xffffffff. Trust DevPcBios.cpp to get this right. */
613 // greater than EFF00000???
614 if(extended_memory_size > 0x3bc000) {
615 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
616 }
617#endif /* !VBOX */
618 extended_memory_size *= 1024;
619 extended_memory_size += (16L * 1024 * 1024);
620
621 if(extended_memory_size <= (16L * 1024 * 1024)) {
622 extended_memory_size = inb_cmos(0x31);
623 extended_memory_size <<= 8;
624 extended_memory_size |= inb_cmos(0x30);
625 extended_memory_size *= 1024;
626 extended_memory_size += (1L * 1024 * 1024);
627 }
628
629#ifdef VBOX /* We've already used the CMOS entries for SATA.
630 BTW. This is the amount of memory above 4GB measured in 64KB units. */
631 extra_lowbits_memory_size = inb_cmos(0x62);
632 extra_lowbits_memory_size <<= 8;
633 extra_lowbits_memory_size |= inb_cmos(0x61);
634 extra_lowbits_memory_size <<= 16;
635 extra_highbits_memory_size = inb_cmos(0x63);
636 /* 0x64 and 0x65 can be used if we need to dig 1 TB or more at a later point. */
637#else
638 extra_lowbits_memory_size = inb_cmos(0x5c);
639 extra_lowbits_memory_size <<= 8;
640 extra_lowbits_memory_size |= inb_cmos(0x5b);
641 extra_lowbits_memory_size *= 64;
642 extra_lowbits_memory_size *= 1024;
643 extra_highbits_memory_size = inb_cmos(0x5d);
644#endif /* !VBOX */
645
646 mcfgStart = 0;
647 mcfgSize = 0;
648
649 switch(BX)
650 {
651 case 0:
652 set_e820_range(ES, DI,
653#ifndef VBOX /** @todo Upstream suggests the following, needs checking. (see next as well) */
654 0x0000000L, 0x0009f000L, 0, 0, 1);
655#else
656 0x0000000L, 0x0009fc00L, 0, 0, 1);
657#endif
658 EBX = 1;
659 break;
660 case 1:
661 set_e820_range(ES, DI,
662#ifndef VBOX /** @todo Upstream suggests the following, needs checking. (see next as well) */
663 0x0009f000L, 0x000a0000L, 0, 0, 2);
664#else
665 0x0009fc00L, 0x000a0000L, 0, 0, 2);
666#endif
667 EBX = 2;
668 break;
669 case 2:
670#ifdef VBOX
671 /* Mark the BIOS as reserved. VBox doesn't currently
672 * use the 0xe0000-0xeffff area. It does use the
673 * 0xd0000-0xdffff area for the BIOS logo, but it's
674 * not worth marking it as reserved. (this is not
675 * true anymore because the VGA adapter handles the logo stuff)
676 * The whole 0xe0000-0xfffff can be used for the BIOS.
677 * Note that various
678 * Windows versions don't accept (read: in debug builds
679 * they trigger the "Too many similar traps" assertion)
680 * a single reserved range from 0xd0000 to 0xffffff.
681 * A 128K area starting from 0xd0000 works. */
682 set_e820_range(ES, DI,
683 0x000f0000L, 0x00100000L, 0, 0, 2);
684#else /* !VBOX */
685 set_e820_range(ES, DI,
686 0x000e8000L, 0x00100000L, 0, 0, 2);
687#endif /* !VBOX */
688 EBX = 3;
689 break;
690 case 3:
691#if BX_ROMBIOS32 || defined(VBOX)
692 set_e820_range(ES, DI,
693 0x00100000L,
694 extended_memory_size - ACPI_DATA_SIZE, 0, 0, 1);
695 EBX = 4;
696#else
697 set_e820_range(ES, DI,
698 0x00100000L,
699 extended_memory_size, 1);
700 EBX = 5;
701#endif
702 break;
703 case 4:
704 set_e820_range(ES, DI,
705 extended_memory_size - ACPI_DATA_SIZE,
706 extended_memory_size, 0, 0, 3); // ACPI RAM
707 EBX = 5;
708 break;
709 case 5:
710 set_e820_range(ES, DI,
711 0xfec00000,
712 0xfec00000 + 0x1000, 0, 0, 2); // I/O APIC
713 EBX = 6;
714 break;
715 case 6:
716 set_e820_range(ES, DI,
717 0xfee00000,
718 0xfee00000 + 0x1000, 0, 0, 2); // Local APIC
719 EBX = 7;
720 break;
721 case 7:
722 /* 256KB BIOS area at the end of 4 GB */
723#ifdef VBOX
724 /* We don't set the end to 1GB here and rely on the 32-bit
725 unsigned wrap around effect (0-0xfffc0000L). */
726#endif
727 set_e820_range(ES, DI,
728 0xfffc0000L, 0x00000000L, 0, 0, 2);
729 if (mcfgStart != 0)
730 EBX = 8;
731 else
732 {
733 if (extra_highbits_memory_size || extra_lowbits_memory_size)
734 EBX = 9;
735 else
736 EBX = 0;
737 }
738 break;
739 case 8:
740 /* PCI MMIO config space (MCFG) */
741 set_e820_range(ES, DI,
742 mcfgStart, mcfgStart + mcfgSize, 0, 0, 2);
743
744 if (extra_highbits_memory_size || extra_lowbits_memory_size)
745 EBX = 9;
746 else
747 EBX = 0;
748 break;
749 case 9:
750#ifdef VBOX /* Don't succeeded if no memory above 4 GB. */
751 /* Mapping of memory above 4 GB if present.
752 Note1: set_e820_range needs do no borrowing in the
753 subtraction because of the nice numbers.
754 Note2* works only up to 1TB because of uint8_t for
755 the upper bits!*/
756 if (extra_highbits_memory_size || extra_lowbits_memory_size)
757 {
758 set_e820_range(ES, DI,
759 0x00000000L, extra_lowbits_memory_size,
760 1 /*x4GB*/, extra_highbits_memory_size + 1 /*x4GB*/, 1);
761 EBX = 0;
762 }
763 break;
764 /* fall thru */
765#else /* !VBOX */
766 /* Mapping of memory above 4 GB */
767 set_e820_range(ES, DI, 0x00000000L,
768 extra_lowbits_memory_size, 1, extra_highbits_memory_size
769 + 1, 1);
770 EBX = 0;
771 break;
772#endif /* !VBOX */
773 default: /* AX=E820, DX=534D4150, BX unrecognized */
774 goto int15_unimplemented;
775 break;
776 }
777 EAX = 0x534D4150;
778 ECX = 0x14;
779 CLEAR_CF();
780 } else {
781 // if DX != 0x534D4150)
782 goto int15_unimplemented;
783 }
784 break;
785
786 case 0x01:
787 // do we have any reason to fail here ?
788 CLEAR_CF();
789
790 // my real system sets ax and bx to 0
791 // this is confirmed by Ralph Brown list
792 // but syslinux v1.48 is known to behave
793 // strangely if ax is set to 0
794 // regs.u.r16.ax = 0;
795 // regs.u.r16.bx = 0;
796
797 // Get the amount of extended memory (above 1M)
798 CX = (inb_cmos(0x31) << 8) | inb_cmos(0x30);
799
800 // limit to 15M
801 if(CX > 0x3c00)
802 CX = 0x3c00;
803
804 // Get the amount of extended memory above 16M in 64k blocks
805 DX = (inb_cmos(0x35) << 8) | inb_cmos(0x34);
806
807 // Set configured memory equal to extended memory
808 AX = CX;
809 BX = DX;
810 break;
811 default: /* AH=0xE8?? but not implemented */
812 goto int15_unimplemented;
813 }
814 break;
815 int15_unimplemented:
816 // fall into the default case
817 default:
818 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
819 (unsigned) AX, (unsigned) BX);
820 SET_CF();
821 SET_AL(UNSUPPORTED_FUNCTION);
822 break;
823 }
824}
825
826#if VBOX_BIOS_CPU >= 80286
827
828#undef FLAGS
829#define FLAGS r.ra.flags.u.r16.flags
830
831/* Function 0x87 handled separately due to specific stack layout requirements. */
832void BIOSCALL int15_blkmove(disk_regs_t r)
833{
834 bx_bool prev_a20_enable;
835 uint16_t base15_00;
836 uint8_t base23_16;
837 uint16_t ss;
838
839 // +++ should probably have descriptor checks
840 // +++ should have exception handlers
841
842 // turn off interrupts
843 int_disable(); //@todo: aren't they disabled already?
844
845 prev_a20_enable = set_enable_a20(1); // enable A20 line
846
847 // 128K max of transfer on 386+ ???
848 // source == destination ???
849
850 // ES:SI points to descriptor table
851 // offset use initially comments
852 // ==============================================
853 // 00..07 Unused zeros Null descriptor
854 // 08..0f GDT zeros filled in by BIOS
855 // 10..17 source ssssssss source of data
856 // 18..1f dest dddddddd destination of data
857 // 20..27 CS zeros filled in by BIOS
858 // 28..2f SS zeros filled in by BIOS
859
860 //es:si
861 //eeee0
862 //0ssss
863 //-----
864
865 // check for access rights of source & dest here
866
867 // Initialize GDT descriptor
868 base15_00 = (ES << 4) + SI;
869 base23_16 = ES >> 12;
870 if (base15_00 < (ES<<4))
871 base23_16++;
872 write_word(ES, SI+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
873 write_word(ES, SI+0x08+2, base15_00);// base 15:00
874 write_byte(ES, SI+0x08+4, base23_16);// base 23:16
875 write_byte(ES, SI+0x08+5, 0x93); // access
876 write_word(ES, SI+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
877
878 // Initialize CS descriptor
879 write_word(ES, SI+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
880 write_word(ES, SI+0x20+2, 0x0000);// base 15:00
881 write_byte(ES, SI+0x20+4, 0x000f);// base 23:16
882 write_byte(ES, SI+0x20+5, 0x9b); // access
883 write_word(ES, SI+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
884
885 // Initialize SS descriptor
886 ss = read_ss();
887 base15_00 = ss << 4;
888 base23_16 = ss >> 12;
889 write_word(ES, SI+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
890 write_word(ES, SI+0x28+2, base15_00);// base 15:00
891 write_byte(ES, SI+0x28+4, base23_16);// base 23:16
892 write_byte(ES, SI+0x28+5, 0x93); // access
893 write_word(ES, SI+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
894
895 pm_stack_save(CX, ES, SI, (uint16_t)(void __near *)&r);
896 pm_enter();
897 pm_copy();
898 pm_exit();
899 pm_stack_restore();
900
901 set_enable_a20(prev_a20_enable);
902
903 // turn interrupts back on
904 int_enable();
905
906 SET_AH(0);
907 CLEAR_CF();
908}
909#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette