diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/PC/BIOS/logo.c | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/PC/BIOS/logo.c')
-rw-r--r-- | src/VBox/Devices/PC/BIOS/logo.c | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/src/VBox/Devices/PC/BIOS/logo.c b/src/VBox/Devices/PC/BIOS/logo.c new file mode 100644 index 00000000..47f758d8 --- /dev/null +++ b/src/VBox/Devices/PC/BIOS/logo.c @@ -0,0 +1,568 @@ +/* $Id: logo.c $ */ +/** @file + * Stuff for drawing the BIOS logo. + */ + +/* + * Copyright (C) 2004-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <stdint.h> +#include "biosint.h" +#include "inlines.h" +#include "ebda.h" + +#define WAIT_HZ 64 +#define WAIT_MS 16 + +#define F12_SCAN_CODE 0x86 +#define F12_WAIT_TIME (3 * WAIT_HZ) /* 3 seconds. Used only if logo disabled. */ + +#include <VBox/bioslogo.h> + +/** + * Set video mode (VGA). + * @param mode New video mode. + */ +void set_mode(uint8_t mode); +#pragma aux set_mode = \ + "mov ah, 0" \ + "int 10h" \ + parm [al] modify [ax] nomemory; + + +/** + * Set VESA video mode. + * @param mode New video mode. + */ +uint16_t vesa_set_mode(uint16_t mode); +#pragma aux vesa_set_mode = \ + "mov ax, 4F02h" \ + "int 10h" \ + parm [bx] modify [ax] nomemory; + +/** + * Get current VESA video mode. + * @param mode New video mode. + */ +uint16_t vesa_get_mode(uint16_t __far *mode); +#pragma aux vesa_get_mode = \ + "mov ax, 4F03h" \ + "int 10h" \ + "mov es:[di], bx" \ + parm [es di] modify [ax bx] nomemory; + + +/** + * Set custom video mode. + * @param xres Requested width + * @param yres Requested height + * @param bpp Requested bits per pixel + */ +uint16_t custom_set_mode(uint16_t xres, uint16_t yres, uint8_t bpp); +#pragma aux custom_set_mode = \ + "mov ax, 5642h" \ + "mov bl, 0" \ + "int 10h" \ + parm [cx] [dx] [bh] modify [ax] nomemory; + +/** + * Check for keystroke. + * @returns True if keystroke available, False if not. + */ +/// @todo INT 16h should already be returning the right value in al; could also use setz +uint8_t check_for_keystroke(void); +#pragma aux check_for_keystroke = \ + "mov ax, 100h" \ + "int 16h" \ + "jz no_key" \ + "mov al, 1" \ + "jmp done" \ + "no_key:" \ + "xor al, al" \ + "done:" \ + modify [ax] nomemory; + + +/** + * Get keystroke. + * @returns BIOS scan code. + */ +uint8_t get_keystroke(void); +#pragma aux get_keystroke = \ + "xor ax, ax" \ + "int 16h" \ + "xchg ah, al" \ + modify [ax] nomemory; + + +/// @todo This whole business with reprogramming the PIT is rather suspect. +// The BIOS already has waiting facilities in INT 15h (fn 83h, 86h) which +// should be utilized instead. + +// Set the timer to 16ms ticks (64K / (Hz / (PIT_HZ / 64K)) = count). +void wait_init(void); +#pragma aux wait_init = \ + "mov al, 34h" \ + "out 43h, al" \ + "mov al, 0D3h" \ + "out 40h, al" \ + "mov al, 048h" \ + "out 40h, al" \ + modify [ax] nomemory; + +/// @todo using this private interface is not great +extern void rtc_post(void); +#pragma aux rtc_post "*"; + +/* Restore the timer to the default 18.2Hz. Reinitialize the tick + * and rollover counts since we've screwed them up by running the + * timer at WAIT_HZ for a while. + */ +void wait_uninit(void); +#if VBOX_BIOS_CPU >= 80386 +# pragma aux wait_uninit = \ + ".386" \ + "mov al, 34h" \ + "out 43h, al" \ + "xor ax, ax" \ + "out 40h, al" \ + "out 40h, al" \ + "pushad" \ + "push ds" \ + "mov ds, ax" \ + "call rtc_post" \ + "pop ds" \ + "popad" \ + modify [ax] nomemory; +#else +# pragma aux wait_uninit = \ + "mov al, 34h" \ + "out 43h, al" \ + "xor ax, ax" \ + "out 40h, al" \ + "out 40h, al" \ + "push bp" \ + "push ds" \ + "mov ds, ax" \ + "call rtc_post" \ + "pop ds" \ + "pop bp" \ + modify [ax bx cx dx si di]; +#endif + + +/** + * Waits (sleeps) for the given number of ticks. + * Checks for keystroke. + * + * @returns BIOS scan code if available, 0 if not. + * @param ticks Number of ticks to sleep. + * @param stop_on_key Whether to stop immediately upon keypress. + */ +uint8_t wait(uint16_t ticks, uint8_t stop_on_key) +{ + long ticks_to_wait, delta; + uint16_t old_flags; + uint32_t prev_ticks, t; + uint8_t scan_code = 0; + + /* + * We may or may not be called with interrupts disabled. For the duration + * of this function, interrupts must be enabled. + */ + old_flags = int_query(); + int_enable(); + + /* + * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock. + * We also have to be careful about interrupt storms. + */ + ticks_to_wait = ticks; + prev_ticks = read_dword(0x0, 0x46c); + do + { + halt(); + t = read_dword(0x0, 0x46c); + if (t > prev_ticks) + { + delta = t - prev_ticks; /* The temp var is required or bcc screws up. */ + ticks_to_wait -= delta; + } + else if (t < prev_ticks) + ticks_to_wait -= t; /* wrapped */ + prev_ticks = t; + + if (check_for_keystroke()) + { + scan_code = get_keystroke(); + bios_printf(BIOS_PRINTF_INFO, "Key pressed: %x\n", scan_code); + if (stop_on_key) + return scan_code; + } + } while (ticks_to_wait > 0); + int_restore(old_flags); + return scan_code; +} + +uint8_t read_logo_byte(uint8_t offset) +{ + outw(LOGO_IO_PORT, LOGO_CMD_SET_OFFSET | offset); + return inb(LOGO_IO_PORT); +} + +uint16_t read_logo_word(uint8_t offset) +{ + outw(LOGO_IO_PORT, LOGO_CMD_SET_OFFSET | offset); + return inw(LOGO_IO_PORT); +} + +// Hide cursor, clear screen and move cursor to starting position +void clear_screen(void); +#pragma aux clear_screen = \ + "mov ax, 100h" \ + "mov cx, 1000h" \ + "int 10h" \ + "mov ax, 700h" \ + "mov bh, 7" \ + "xor cx, cx" \ + "mov dx, 184Fh" \ + "int 10h" \ + "mov ax, 200h" \ + "xor bx, bx" \ + "xor dx, dx" \ + "int 10h" \ + modify [ax bx cx dx] nomemory; + +void print_detected_harddisks(void) +{ + uint16_t ebda_seg=read_word(0x0040,0x000E); + uint8_t hd_count; + uint8_t hd_curr = 0; + uint8_t ide_ctrl_printed = 0; + uint8_t sata_ctrl_printed = 0; + uint8_t scsi_ctrl_printed = 0; + uint8_t device; + + hd_count = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount); + + for (hd_curr = 0; hd_curr < hd_count; hd_curr++) + { + device = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdidmap[hd_curr]); + +#ifdef VBOX_WITH_AHCI + if (VBOX_IS_AHCI_DEVICE(device)) + { + if (sata_ctrl_printed == 0) + { + printf("\n\n AHCI controller:"); + sata_ctrl_printed = 1; + } + + printf("\n %d) Hard disk", hd_curr+1); + + } + else +#endif +#ifdef VBOX_WITH_SCSI + if (VBOX_IS_SCSI_DEVICE(device)) + { + if (scsi_ctrl_printed == 0) + { + printf("\n\n SCSI controller:"); + scsi_ctrl_printed = 1; + } + + printf("\n %d) Hard disk", hd_curr+1); + + } + else +#endif + { + + if ((device < 4) && (ide_ctrl_printed == 0)) + { + printf(" IDE controller:"); + ide_ctrl_printed = 1; + } + else if ((device >= 4) && (sata_ctrl_printed == 0)) + { + printf("\n\nAHCI controller:\n"); + sata_ctrl_printed = 1; + } + + printf("\n %d) ", hd_curr+1); + + /* + * If actual_device is bigger than or equal 4 + * this is the next controller and + * the positions start at the beginning. + */ + if (device >= 4) + device -= 4; + + if (device / 2) + printf("Secondary "); + else + printf("Primary "); + + if (device % 2) + printf("Slave"); + else + printf("Master"); + } + } + + if ( (ide_ctrl_printed == 0) + && (sata_ctrl_printed == 0) + && (scsi_ctrl_printed == 0)) + printf("No hard disks found"); + + printf("\n"); +} + +uint8_t get_boot_drive(uint8_t scode) +{ + uint16_t ebda_seg=read_word(0x0040,0x000E); + + /* Check that the scan code is in the range of detected hard disks. */ + uint8_t hd_count = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount); + + /* The key '1' has scancode 0x02 which represents the first disk */ + scode -= 2; + + if (scode < hd_count) + return scode; + + /* Scancode is higher than number of available devices */ + return 0xff; +} + +void show_logo(void) +{ + uint16_t ebda_seg = read_word(0x0040,0x000E); + uint8_t f12_pressed = 0; + uint8_t scode; + uint16_t tmp, i; + + LOGOHDR *logo_hdr = 0; + uint8_t is_fade_in, is_fade_out, uBootMenu; + uint16_t logo_time; + uint16_t old_mode; + + + // Set PIT to 64hz. + wait_init(); + + // Get main signature + tmp = read_logo_word((uint8_t)&logo_hdr->u16Signature); + if (tmp != 0x66BB) + goto done; + + // If there is no VBE, just skip this + if (vesa_get_mode(&old_mode) != 0x004f ) + goto done; + + // Get options + is_fade_in = read_logo_byte((uint8_t)&logo_hdr->fu8FadeIn); + is_fade_out = read_logo_byte((uint8_t)&logo_hdr->fu8FadeOut); + logo_time = read_logo_word((uint8_t)&logo_hdr->u16LogoMillies); + uBootMenu = read_logo_byte((uint8_t)&logo_hdr->fu8ShowBootMenu); + + // Is Logo disabled? + if (!is_fade_in && !is_fade_out && !logo_time) + goto done; + + /* Set video mode using private video BIOS interface. */ + tmp = custom_set_mode(640, 480, 32); + /* If custom mode set failed, fall back to VBE. */ + if (tmp != 0x4F) + vesa_set_mode(0x142); + + if (is_fade_in) + { + for (i = 0; i <= LOGO_SHOW_STEPS; i++) + { + outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | i); + scode = wait(16 / WAIT_MS, 0); + if (scode == F12_SCAN_CODE) + { + f12_pressed = 1; + break; + } + } + } + else + outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | LOGO_SHOW_STEPS); + + // Wait (interval in milliseconds) + if (!f12_pressed) + { + scode = wait(logo_time / WAIT_MS, 1); + if (scode == F12_SCAN_CODE) + f12_pressed = 1; + } + + // Fade out (only if F12 was not pressed) + if (is_fade_out && !f12_pressed) + { + for (i = LOGO_SHOW_STEPS; i > 0 ; i--) + { + outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | i); + scode = wait(16 / WAIT_MS, 0); + if (scode == F12_SCAN_CODE) + { + f12_pressed = 1; + break; + } + } + } + else if (!f12_pressed) + outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | 0); + +done: + // Clear forced boot drive setting. + write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDevice, 0); + + // Don't restore previous video mode + // The default text mode should be set up. (defect @bugref{1235}) + set_mode(0x0003); + + // If Setup menu enabled + if (uBootMenu) + { + // If the graphics logo disabled + if (!is_fade_in && !is_fade_out && !logo_time) + { + if (uBootMenu == 2) + printf("Press F12 to select boot device.\n"); + + // if the user has pressed F12 don't wait here + if (!f12_pressed) + { + // Wait for timeout or keystroke + scode = wait(F12_WAIT_TIME, 1); + if (scode == F12_SCAN_CODE) + f12_pressed = 1; + } + } + + // If F12 pressed, show boot menu + if (f12_pressed) + { + uint8_t boot_device = 0; + uint8_t boot_drive = 0; + + clear_screen(); + + // Show menu. Note that some versions of bcc freak out if we split these strings. + printf("\nVirtualBox temporary boot device selection\n\nDetected Hard disks:\n\n"); + print_detected_harddisks(); + printf("\nOther boot devices:\n f) Floppy\n c) CD-ROM\n l) LAN\n\n b) Continue booting\n"); + + + + // Wait for keystroke + for (;;) + { + do + { + scode = wait(WAIT_HZ, 1); + } while (scode == 0); + + if (scode == 0x30) + { + // 'b' ... continue + break; + } + + // Check if hard disk was selected + if ((scode >= 0x02) && (scode <= 0x09)) + { + boot_drive = get_boot_drive(scode); + + /* + * 0xff indicates that there is no mapping + * from the scan code to a hard drive. + * Wait for next keystroke. + */ + if (boot_drive == 0xff) + continue; + + write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDrive, boot_drive); + boot_device = 0x02; + break; + } + + switch (scode) + { + case 0x21: + // Floppy + boot_device = 0x01; + break; + case 0x2e: + // CD-ROM + boot_device = 0x03; + break; + case 0x26: + // LAN + boot_device = 0x04; + break; + } + + if (boot_device != 0) + break; + } + + write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDevice, boot_device); + + // Switch to text mode. Clears screen and enables cursor again. + set_mode(0x0003); + } + } + + // Restore PIT ticks + wait_uninit(); + + return; +} + + +void delay_boot(uint16_t secs) +{ + uint16_t i; + + if (!secs) + return; + + // Set PIT to 1ms ticks + wait_init(); + + printf("Delaying boot for %d seconds:", secs); + for (i = secs; i > 0; i--) + { + printf(" %d", i); + wait(WAIT_HZ, 0); + } + printf("\n"); + // Restore PIT ticks + wait_uninit(); +} |