VirtualBox

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

Last change on this file since 69496 was 69496, checked in by vboxsync, 8 years ago

*: scm --update-copyright-year

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