VirtualBox

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

Last change on this file since 74612 was 74612, checked in by vboxsync, 7 years ago

BIOS: Slightly optimized INT 15h protected mode switching code.

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

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