VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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