VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/logo.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1/* $Id: logo.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Stuff for drawing the BIOS logo.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <stdint.h>
29#include "biosint.h"
30#include "inlines.h"
31#include "ebda.h"
32
33#define WAIT_HZ 64
34#define WAIT_MS 16
35
36#define F12_SCAN_CODE 0x86
37#define F12_WAIT_TIME (3 * WAIT_HZ) /* 3 seconds. Used only if logo disabled. */
38
39#include <VBox/bioslogo.h>
40
41/**
42 * Set video mode (VGA).
43 * @param mode New video mode.
44 */
45void set_mode(uint8_t mode);
46#pragma aux set_mode = \
47 "mov ah, 0" \
48 "int 10h" \
49 parm [al] modify [ax] nomemory;
50
51
52/**
53 * Set VESA video mode.
54 * @param mode New video mode.
55 */
56uint16_t vesa_set_mode(uint16_t mode);
57#pragma aux vesa_set_mode = \
58 "mov ax, 4F02h" \
59 "int 10h" \
60 parm [bx] modify [ax] nomemory;
61
62/**
63 * Get current VESA video mode.
64 * @param mode New video mode.
65 */
66uint16_t vesa_get_mode(uint16_t __far *mode);
67#pragma aux vesa_get_mode = \
68 "mov ax, 4F03h" \
69 "int 10h" \
70 "mov es:[di], bx" \
71 parm [es di] modify [ax bx] nomemory;
72
73
74/**
75 * Set custom video mode.
76 * @param xres Requested width
77 * @param yres Requested height
78 * @param bpp Requested bits per pixel
79 */
80uint16_t custom_set_mode(uint16_t xres, uint16_t yres, uint8_t bpp);
81#pragma aux custom_set_mode = \
82 "mov ax, 5642h" \
83 "mov bl, 0" \
84 "int 10h" \
85 parm [cx] [dx] [bh] modify [ax] nomemory;
86
87/**
88 * Check for keystroke.
89 * @returns True if keystroke available, False if not.
90 */
91/// @todo INT 16h should already be returning the right value in al; could also use setz
92uint8_t check_for_keystroke(void);
93#pragma aux check_for_keystroke = \
94 "mov ax, 100h" \
95 "int 16h" \
96 "jz no_key" \
97 "mov al, 1" \
98 "jmp done" \
99 "no_key:" \
100 "xor al, al" \
101 "done:" \
102 modify [ax] nomemory;
103
104
105/**
106 * Get keystroke.
107 * @returns BIOS scan code.
108 */
109uint8_t get_keystroke(void);
110#pragma aux get_keystroke = \
111 "xor ax, ax" \
112 "int 16h" \
113 "xchg ah, al" \
114 modify [ax] nomemory;
115
116
117/// @todo This whole business with reprogramming the PIT is rather suspect.
118// The BIOS already has waiting facilities in INT 15h (fn 83h, 86h) which
119// should be utilized instead.
120
121// Set the timer to 16ms ticks (64K / (Hz / (PIT_HZ / 64K)) = count).
122void wait_init(void);
123#pragma aux wait_init = \
124 "mov al, 34h" \
125 "out 43h, al" \
126 "mov al, 0D3h" \
127 "out 40h, al" \
128 "mov al, 048h" \
129 "out 40h, al" \
130 modify [ax] nomemory;
131
132/// @todo using this private interface is not great
133extern void rtc_post(void);
134#pragma aux rtc_post "*";
135
136/* Restore the timer to the default 18.2Hz. Reinitialize the tick
137 * and rollover counts since we've screwed them up by running the
138 * timer at WAIT_HZ for a while.
139 */
140void wait_uninit(void);
141#if VBOX_BIOS_CPU >= 80386
142# pragma aux wait_uninit = \
143 ".386" \
144 "mov al, 34h" \
145 "out 43h, al" \
146 "xor ax, ax" \
147 "out 40h, al" \
148 "out 40h, al" \
149 "pushad" \
150 "push ds" \
151 "mov ds, ax" \
152 "call rtc_post" \
153 "pop ds" \
154 "popad" \
155 modify [ax] nomemory;
156#else
157# pragma aux wait_uninit = \
158 "mov al, 34h" \
159 "out 43h, al" \
160 "xor ax, ax" \
161 "out 40h, al" \
162 "out 40h, al" \
163 "push bp" \
164 "push ds" \
165 "mov ds, ax" \
166 "call rtc_post" \
167 "pop ds" \
168 "pop bp" \
169 modify [ax bx cx dx si di];
170#endif
171
172
173/**
174 * Waits (sleeps) for the given number of ticks.
175 * Checks for keystroke.
176 *
177 * @returns BIOS scan code if available, 0 if not.
178 * @param ticks Number of ticks to sleep.
179 * @param stop_on_key Whether to stop immediately upon keypress.
180 */
181uint8_t wait(uint16_t ticks, uint8_t stop_on_key)
182{
183 long ticks_to_wait, delta;
184 uint16_t old_flags;
185 uint32_t prev_ticks, t;
186 uint8_t scan_code = 0;
187
188 /*
189 * We may or may not be called with interrupts disabled. For the duration
190 * of this function, interrupts must be enabled.
191 */
192 old_flags = int_query();
193 int_enable();
194
195 /*
196 * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
197 * We also have to be careful about interrupt storms.
198 */
199 ticks_to_wait = ticks;
200 prev_ticks = read_dword(0x0, 0x46c);
201 do
202 {
203 halt();
204 t = read_dword(0x0, 0x46c);
205 if (t > prev_ticks)
206 {
207 delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
208 ticks_to_wait -= delta;
209 }
210 else if (t < prev_ticks)
211 ticks_to_wait -= t; /* wrapped */
212 prev_ticks = t;
213
214 if (check_for_keystroke())
215 {
216 scan_code = get_keystroke();
217 bios_printf(BIOS_PRINTF_INFO, "Key pressed: %x\n", scan_code);
218 if (stop_on_key)
219 return scan_code;
220 }
221 } while (ticks_to_wait > 0);
222 int_restore(old_flags);
223 return scan_code;
224}
225
226uint8_t read_logo_byte(uint8_t offset)
227{
228 outw(LOGO_IO_PORT, LOGO_CMD_SET_OFFSET | offset);
229 return inb(LOGO_IO_PORT);
230}
231
232uint16_t read_logo_word(uint8_t offset)
233{
234 outw(LOGO_IO_PORT, LOGO_CMD_SET_OFFSET | offset);
235 return inw(LOGO_IO_PORT);
236}
237
238// Hide cursor, clear screen and move cursor to starting position
239void clear_screen(void);
240#pragma aux clear_screen = \
241 "mov ax, 100h" \
242 "mov cx, 1000h" \
243 "int 10h" \
244 "mov ax, 700h" \
245 "mov bh, 7" \
246 "xor cx, cx" \
247 "mov dx, 184Fh" \
248 "int 10h" \
249 "mov ax, 200h" \
250 "xor bx, bx" \
251 "xor dx, dx" \
252 "int 10h" \
253 modify [ax bx cx dx] nomemory;
254
255void print_detected_harddisks(void)
256{
257 uint16_t ebda_seg=read_word(0x0040,0x000E);
258 uint8_t hd_count;
259 uint8_t hd_curr = 0;
260 uint8_t ide_ctrl_printed = 0;
261 uint8_t sata_ctrl_printed = 0;
262 uint8_t scsi_ctrl_printed = 0;
263 uint8_t device;
264
265 hd_count = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount);
266
267 for (hd_curr = 0; hd_curr < hd_count; hd_curr++)
268 {
269 device = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdidmap[hd_curr]);
270
271#ifdef VBOX_WITH_AHCI
272 if (VBOX_IS_AHCI_DEVICE(device))
273 {
274 if (sata_ctrl_printed == 0)
275 {
276 printf("\n\n AHCI controller:");
277 sata_ctrl_printed = 1;
278 }
279
280 printf("\n %d) Hard disk", hd_curr+1);
281
282 }
283 else
284#endif
285#ifdef VBOX_WITH_SCSI
286 if (VBOX_IS_SCSI_DEVICE(device))
287 {
288 if (scsi_ctrl_printed == 0)
289 {
290 printf("\n\n SCSI controller:");
291 scsi_ctrl_printed = 1;
292 }
293
294 printf("\n %d) Hard disk", hd_curr+1);
295
296 }
297 else
298#endif
299 {
300
301 if ((device < 4) && (ide_ctrl_printed == 0))
302 {
303 printf(" IDE controller:");
304 ide_ctrl_printed = 1;
305 }
306 else if ((device >= 4) && (sata_ctrl_printed == 0))
307 {
308 printf("\n\nAHCI controller:\n");
309 sata_ctrl_printed = 1;
310 }
311
312 printf("\n %d) ", hd_curr+1);
313
314 /*
315 * If actual_device is bigger than or equal 4
316 * this is the next controller and
317 * the positions start at the beginning.
318 */
319 if (device >= 4)
320 device -= 4;
321
322 if (device / 2)
323 printf("Secondary ");
324 else
325 printf("Primary ");
326
327 if (device % 2)
328 printf("Slave");
329 else
330 printf("Master");
331 }
332 }
333
334 if ( (ide_ctrl_printed == 0)
335 && (sata_ctrl_printed == 0)
336 && (scsi_ctrl_printed == 0))
337 printf("No hard disks found");
338
339 printf("\n");
340}
341
342uint8_t get_boot_drive(uint8_t scode)
343{
344 uint16_t ebda_seg=read_word(0x0040,0x000E);
345
346 /* Check that the scan code is in the range of detected hard disks. */
347 uint8_t hd_count = read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount);
348
349 /* The key '1' has scancode 0x02 which represents the first disk */
350 scode -= 2;
351
352 if (scode < hd_count)
353 return scode;
354
355 /* Scancode is higher than number of available devices */
356 return 0xff;
357}
358
359void show_logo(void)
360{
361 uint16_t ebda_seg = read_word(0x0040,0x000E);
362 uint8_t f12_pressed = 0;
363 uint8_t scode;
364 uint16_t tmp, i;
365
366 LOGOHDR *logo_hdr = 0;
367 uint8_t is_fade_in, is_fade_out, uBootMenu;
368 uint16_t logo_time;
369 uint16_t old_mode;
370
371
372 // Set PIT to 64hz.
373 wait_init();
374
375 // Get main signature
376 tmp = read_logo_word((uint8_t)&logo_hdr->u16Signature);
377 if (tmp != 0x66BB)
378 goto done;
379
380 // If there is no VBE, just skip this
381 if (vesa_get_mode(&old_mode) != 0x004f )
382 goto done;
383
384 // Get options
385 is_fade_in = read_logo_byte((uint8_t)&logo_hdr->fu8FadeIn);
386 is_fade_out = read_logo_byte((uint8_t)&logo_hdr->fu8FadeOut);
387 logo_time = read_logo_word((uint8_t)&logo_hdr->u16LogoMillies);
388 uBootMenu = read_logo_byte((uint8_t)&logo_hdr->fu8ShowBootMenu);
389
390 // Is Logo disabled?
391 if (!is_fade_in && !is_fade_out && !logo_time)
392 goto done;
393
394 /* Set video mode using private video BIOS interface. */
395 tmp = custom_set_mode(640, 480, 32);
396 /* If custom mode set failed, fall back to VBE. */
397 if (tmp != 0x4F)
398 vesa_set_mode(0x142);
399
400 if (is_fade_in)
401 {
402 for (i = 0; i <= LOGO_SHOW_STEPS; i++)
403 {
404 outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | i);
405 scode = wait(16 / WAIT_MS, 0);
406 if (scode == F12_SCAN_CODE)
407 {
408 f12_pressed = 1;
409 break;
410 }
411 }
412 }
413 else
414 outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | LOGO_SHOW_STEPS);
415
416 // Wait (interval in milliseconds)
417 if (!f12_pressed)
418 {
419 scode = wait(logo_time / WAIT_MS, 1);
420 if (scode == F12_SCAN_CODE)
421 f12_pressed = 1;
422 }
423
424 // Fade out (only if F12 was not pressed)
425 if (is_fade_out && !f12_pressed)
426 {
427 for (i = LOGO_SHOW_STEPS; i > 0 ; i--)
428 {
429 outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | i);
430 scode = wait(16 / WAIT_MS, 0);
431 if (scode == F12_SCAN_CODE)
432 {
433 f12_pressed = 1;
434 break;
435 }
436 }
437 }
438 else if (!f12_pressed)
439 outw(LOGO_IO_PORT, LOGO_CMD_SHOW_BMP | 0);
440
441done:
442 // Clear forced boot drive setting.
443 write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDevice, 0);
444
445 // Don't restore previous video mode
446 // The default text mode should be set up. (defect @bugref{1235})
447 set_mode(0x0003);
448
449 // If Setup menu enabled
450 if (uBootMenu)
451 {
452 // If the graphics logo disabled
453 if (!is_fade_in && !is_fade_out && !logo_time)
454 {
455 if (uBootMenu == 2)
456 printf("Press F12 to select boot device.\n");
457
458 // if the user has pressed F12 don't wait here
459 if (!f12_pressed)
460 {
461 // Wait for timeout or keystroke
462 scode = wait(F12_WAIT_TIME, 1);
463 if (scode == F12_SCAN_CODE)
464 f12_pressed = 1;
465 }
466 }
467
468 // If F12 pressed, show boot menu
469 if (f12_pressed)
470 {
471 uint8_t boot_device = 0;
472 uint8_t boot_drive = 0;
473
474 clear_screen();
475
476 // Show menu. Note that some versions of bcc freak out if we split these strings.
477 printf("\nVirtualBox temporary boot device selection\n\nDetected Hard disks:\n\n");
478 print_detected_harddisks();
479 printf("\nOther boot devices:\n f) Floppy\n c) CD-ROM\n l) LAN\n\n b) Continue booting\n");
480
481
482
483 // Wait for keystroke
484 for (;;)
485 {
486 do
487 {
488 scode = wait(WAIT_HZ, 1);
489 } while (scode == 0);
490
491 if (scode == 0x30)
492 {
493 // 'b' ... continue
494 break;
495 }
496
497 // Check if hard disk was selected
498 if ((scode >= 0x02) && (scode <= 0x09))
499 {
500 boot_drive = get_boot_drive(scode);
501
502 /*
503 * 0xff indicates that there is no mapping
504 * from the scan code to a hard drive.
505 * Wait for next keystroke.
506 */
507 if (boot_drive == 0xff)
508 continue;
509
510 write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDrive, boot_drive);
511 boot_device = 0x02;
512 break;
513 }
514
515 switch (scode)
516 {
517 case 0x21:
518 // Floppy
519 boot_device = 0x01;
520 break;
521 case 0x2e:
522 // CD-ROM
523 boot_device = 0x03;
524 break;
525 case 0x26:
526 // LAN
527 boot_device = 0x04;
528 break;
529 }
530
531 if (boot_device != 0)
532 break;
533 }
534
535 write_byte(ebda_seg, (uint16_t)&EbdaData->uForceBootDevice, boot_device);
536
537 // Switch to text mode. Clears screen and enables cursor again.
538 set_mode(0x0003);
539 }
540 }
541
542 // Restore PIT ticks
543 wait_uninit();
544
545 return;
546}
547
548
549void delay_boot(uint16_t secs)
550{
551 uint16_t i;
552
553 if (!secs)
554 return;
555
556 // Set PIT to 1ms ticks
557 wait_init();
558
559 printf("Delaying boot for %d seconds:", secs);
560 for (i = secs; i > 0; i--)
561 {
562 printf(" %d", i);
563 wait(WAIT_HZ, 0);
564 }
565 printf("\n");
566 // Restore PIT ticks
567 wait_uninit();
568}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use