diff --git a/stand/efi/include/eficon.h b/stand/efi/include/eficon.h index b5a387cb08fd..ba80114eb9be 100644 --- a/stand/efi/include/eficon.h +++ b/stand/efi/include/eficon.h @@ -1,527 +1,527 @@ /* $FreeBSD$ */ #ifndef _EFI_CON_H #define _EFI_CON_H /*++ Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. Module Name: eficon.h Abstract: EFI console protocols Revision History --*/ // // Text output protocol // #define SIMPLE_TEXT_OUTPUT_PROTOCOL \ { 0x387477c2, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} } INTERFACE_DECL(_SIMPLE_TEXT_OUTPUT_INTERFACE); typedef EFI_STATUS (EFIAPI *EFI_TEXT_RESET) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN BOOLEAN ExtendedVerification ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_OUTPUT_STRING) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN CHAR16 *String ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_TEST_STRING) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN CHAR16 *String ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_QUERY_MODE) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN UINTN ModeNumber, OUT UINTN *Columns, OUT UINTN *Rows ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_SET_MODE) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN UINTN ModeNumber ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_SET_ATTRIBUTE) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN UINTN Attribute ); #define EFI_BLACK 0x00 #define EFI_BLUE 0x01 #define EFI_GREEN 0x02 #define EFI_CYAN (EFI_BLUE | EFI_GREEN) #define EFI_RED 0x04 #define EFI_MAGENTA (EFI_BLUE | EFI_RED) #define EFI_BROWN (EFI_GREEN | EFI_RED) #define EFI_LIGHTGRAY (EFI_BLUE | EFI_GREEN | EFI_RED) #define EFI_BRIGHT 0x08 #define EFI_DARKGRAY (EFI_BRIGHT) #define EFI_LIGHTBLUE (EFI_BLUE | EFI_BRIGHT) #define EFI_LIGHTGREEN (EFI_GREEN | EFI_BRIGHT) #define EFI_LIGHTCYAN (EFI_CYAN | EFI_BRIGHT) #define EFI_LIGHTRED (EFI_RED | EFI_BRIGHT) #define EFI_LIGHTMAGENTA (EFI_MAGENTA | EFI_BRIGHT) #define EFI_YELLOW (EFI_BROWN | EFI_BRIGHT) #define EFI_WHITE (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT) #define EFI_TEXT_ATTR(f,b) ((f) | ((b) << 4)) #define EFI_BACKGROUND_BLACK 0x00 #define EFI_BACKGROUND_BLUE 0x10 #define EFI_BACKGROUND_GREEN 0x20 #define EFI_BACKGROUND_CYAN (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN) #define EFI_BACKGROUND_RED 0x40 #define EFI_BACKGROUND_MAGENTA (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_RED) #define EFI_BACKGROUND_BROWN (EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) #define EFI_BACKGROUND_LIGHTGRAY (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) typedef EFI_STATUS (EFIAPI *EFI_TEXT_CLEAR_SCREEN) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_SET_CURSOR_POSITION) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN UINTN Column, IN UINTN Row ); typedef EFI_STATUS (EFIAPI *EFI_TEXT_ENABLE_CURSOR) ( IN struct _SIMPLE_TEXT_OUTPUT_INTERFACE *This, IN BOOLEAN Enable ); typedef struct { INT32 MaxMode; // current settings INT32 Mode; INT32 Attribute; INT32 CursorColumn; INT32 CursorRow; BOOLEAN CursorVisible; } SIMPLE_TEXT_OUTPUT_MODE; typedef struct _SIMPLE_TEXT_OUTPUT_INTERFACE { EFI_TEXT_RESET Reset; EFI_TEXT_OUTPUT_STRING OutputString; EFI_TEXT_TEST_STRING TestString; EFI_TEXT_QUERY_MODE QueryMode; EFI_TEXT_SET_MODE SetMode; EFI_TEXT_SET_ATTRIBUTE SetAttribute; EFI_TEXT_CLEAR_SCREEN ClearScreen; EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition; EFI_TEXT_ENABLE_CURSOR EnableCursor; // Current mode SIMPLE_TEXT_OUTPUT_MODE *Mode; } SIMPLE_TEXT_OUTPUT_INTERFACE; // // Define's for required EFI Unicode Box Draw character // #define BOXDRAW_HORIZONTAL 0x2500 #define BOXDRAW_VERTICAL 0x2502 #define BOXDRAW_DOWN_RIGHT 0x250c #define BOXDRAW_DOWN_LEFT 0x2510 #define BOXDRAW_UP_RIGHT 0x2514 #define BOXDRAW_UP_LEFT 0x2518 #define BOXDRAW_VERTICAL_RIGHT 0x251c #define BOXDRAW_VERTICAL_LEFT 0x2524 #define BOXDRAW_DOWN_HORIZONTAL 0x252c #define BOXDRAW_UP_HORIZONTAL 0x2534 #define BOXDRAW_VERTICAL_HORIZONTAL 0x253c #define BOXDRAW_DOUBLE_HORIZONTAL 0x2550 #define BOXDRAW_DOUBLE_VERTICAL 0x2551 #define BOXDRAW_DOWN_RIGHT_DOUBLE 0x2552 #define BOXDRAW_DOWN_DOUBLE_RIGHT 0x2553 #define BOXDRAW_DOUBLE_DOWN_RIGHT 0x2554 #define BOXDRAW_DOWN_LEFT_DOUBLE 0x2555 #define BOXDRAW_DOWN_DOUBLE_LEFT 0x2556 #define BOXDRAW_DOUBLE_DOWN_LEFT 0x2557 #define BOXDRAW_UP_RIGHT_DOUBLE 0x2558 #define BOXDRAW_UP_DOUBLE_RIGHT 0x2559 #define BOXDRAW_DOUBLE_UP_RIGHT 0x255a #define BOXDRAW_UP_LEFT_DOUBLE 0x255b #define BOXDRAW_UP_DOUBLE_LEFT 0x255c #define BOXDRAW_DOUBLE_UP_LEFT 0x255d #define BOXDRAW_VERTICAL_RIGHT_DOUBLE 0x255e #define BOXDRAW_VERTICAL_DOUBLE_RIGHT 0x255f #define BOXDRAW_DOUBLE_VERTICAL_RIGHT 0x2560 #define BOXDRAW_VERTICAL_LEFT_DOUBLE 0x2561 #define BOXDRAW_VERTICAL_DOUBLE_LEFT 0x2562 #define BOXDRAW_DOUBLE_VERTICAL_LEFT 0x2563 #define BOXDRAW_DOWN_HORIZONTAL_DOUBLE 0x2564 #define BOXDRAW_DOWN_DOUBLE_HORIZONTAL 0x2565 #define BOXDRAW_DOUBLE_DOWN_HORIZONTAL 0x2566 #define BOXDRAW_UP_HORIZONTAL_DOUBLE 0x2567 #define BOXDRAW_UP_DOUBLE_HORIZONTAL 0x2568 #define BOXDRAW_DOUBLE_UP_HORIZONTAL 0x2569 #define BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE 0x256a #define BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL 0x256b #define BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL 0x256c // // EFI Required Block Elements Code Chart // #define BLOCKELEMENT_FULL_BLOCK 0x2588 #define BLOCKELEMENT_LIGHT_SHADE 0x2591 // // EFI Required Geometric Shapes Code Chart // #define GEOMETRICSHAPE_UP_TRIANGLE 0x25b2 #define GEOMETRICSHAPE_RIGHT_TRIANGLE 0x25ba #define GEOMETRICSHAPE_DOWN_TRIANGLE 0x25bc #define GEOMETRICSHAPE_LEFT_TRIANGLE 0x25c4 // // EFI Required Arrow shapes // #define ARROW_UP 0x2191 #define ARROW_DOWN 0x2193 // // Text input protocol // #define SIMPLE_TEXT_INPUT_PROTOCOL \ { 0x387477c1, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} } INTERFACE_DECL(_SIMPLE_INPUT_INTERFACE); typedef struct { UINT16 ScanCode; CHAR16 UnicodeChar; } EFI_INPUT_KEY; // // Baseline unicode control chars // #define CHAR_NULL 0x0000 #define CHAR_BACKSPACE 0x0008 #define CHAR_TAB 0x0009 #define CHAR_LINEFEED 0x000A #define CHAR_CARRIAGE_RETURN 0x000D // // Scan codes for base line keys // #define SCAN_NULL 0x0000 #define SCAN_UP 0x0001 #define SCAN_DOWN 0x0002 #define SCAN_RIGHT 0x0003 #define SCAN_LEFT 0x0004 #define SCAN_HOME 0x0005 #define SCAN_END 0x0006 #define SCAN_INSERT 0x0007 #define SCAN_DELETE 0x0008 #define SCAN_PAGE_UP 0x0009 #define SCAN_PAGE_DOWN 0x000A #define SCAN_F1 0x000B #define SCAN_F2 0x000C #define SCAN_F3 0x000D #define SCAN_F4 0x000E #define SCAN_F5 0x000F #define SCAN_F6 0x0010 #define SCAN_F7 0x0011 #define SCAN_F8 0x0012 #define SCAN_F9 0x0013 #define SCAN_F10 0x0014 #define SCAN_ESC 0x0017 // // EFI Scan code Ex // #define SCAN_F11 0x0015 #define SCAN_F12 0x0016 #define SCAN_F13 0x0068 #define SCAN_F14 0x0069 #define SCAN_F15 0x006A #define SCAN_F16 0x006B #define SCAN_F17 0x006C #define SCAN_F18 0x006D #define SCAN_F19 0x006E #define SCAN_F20 0x006F #define SCAN_F21 0x0070 #define SCAN_F22 0x0071 #define SCAN_F23 0x0072 #define SCAN_F24 0x0073 #define SCAN_MUTE 0x007F #define SCAN_VOLUME_UP 0x0080 #define SCAN_VOLUME_DOWN 0x0081 #define SCAN_BRIGHTNESS_UP 0x0100 #define SCAN_BRIGHTNESS_DOWN 0x0101 #define SCAN_SUSPEND 0x0102 #define SCAN_HIBERNATE 0x0103 #define SCAN_TOGGLE_DISPLAY 0x0104 #define SCAN_RECOVERY 0x0105 #define SCAN_EJECT 0x0106 typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET) ( IN struct _SIMPLE_INPUT_INTERFACE *This, IN BOOLEAN ExtendedVerification ); typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY) ( IN struct _SIMPLE_INPUT_INTERFACE *This, OUT EFI_INPUT_KEY *Key ); typedef struct _SIMPLE_INPUT_INTERFACE { EFI_INPUT_RESET Reset; EFI_INPUT_READ_KEY ReadKeyStroke; EFI_EVENT WaitForKey; } SIMPLE_INPUT_INTERFACE; #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \ {0xdd9e7534, 0x7762, 0x4698, {0x8c, 0x14, 0xf5, 0x85, \ 0x17, 0xa6, 0x25, 0xaa} } INTERFACE_DECL(_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL); typedef UINT8 EFI_KEY_TOGGLE_STATE; // // Any Shift or Toggle State that is valid should have // high order bit set. // typedef struct EFI_KEY_STATE { UINT32 KeyShiftState; EFI_KEY_TOGGLE_STATE KeyToggleState; } EFI_KEY_STATE; typedef struct { EFI_INPUT_KEY Key; EFI_KEY_STATE KeyState; } EFI_KEY_DATA; // // Shift state // #define EFI_SHIFT_STATE_VALID 0x80000000 #define EFI_RIGHT_SHIFT_PRESSED 0x00000001 #define EFI_LEFT_SHIFT_PRESSED 0x00000002 #define EFI_RIGHT_CONTROL_PRESSED 0x00000004 #define EFI_LEFT_CONTROL_PRESSED 0x00000008 #define EFI_RIGHT_ALT_PRESSED 0x00000010 #define EFI_LEFT_ALT_PRESSED 0x00000020 #define EFI_RIGHT_LOGO_PRESSED 0x00000040 #define EFI_LEFT_LOGO_PRESSED 0x00000080 #define EFI_MENU_KEY_PRESSED 0x00000100 #define EFI_SYS_REQ_PRESSED 0x00000200 // // Toggle state // #define EFI_TOGGLE_STATE_VALID 0x80 #define EFI_KEY_STATE_EXPOSED 0x40 #define EFI_SCROLL_LOCK_ACTIVE 0x01 #define EFI_NUM_LOCK_ACTIVE 0x02 #define EFI_CAPS_LOCK_ACTIVE 0x04 // // EFI Key Notfication Function // typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION) ( IN EFI_KEY_DATA *KeyData ); typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX) ( IN struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) /*++ Routine Description: Reset the input device and optionaly run diagnostics Arguments: This - Protocol instance pointer. ExtendedVerification - Driver may perform diagnostics on reset. Returns: EFI_SUCCESS - The device was reset. EFI_DEVICE_ERROR - The device is not functioning properly and could not be reset. --*/ ; typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX) ( IN struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, OUT EFI_KEY_DATA *KeyData ) /*++ Routine Description: Reads the next keystroke from the input device. The WaitForKey Event can - be used to test for existance of a keystroke via WaitForEvent () call. + be used to test for existence of a keystroke via WaitForEvent () call. Arguments: This - Protocol instance pointer. KeyData - A pointer to a buffer that is filled in with the keystroke state data for the key that was pressed. Returns: EFI_SUCCESS - The keystroke information was returned. EFI_NOT_READY - There was no keystroke data availiable. EFI_DEVICE_ERROR - The keystroke information was not returned due to hardware errors. EFI_INVALID_PARAMETER - KeyData is NULL. --*/ ; typedef EFI_STATUS (EFIAPI *EFI_SET_STATE) ( IN struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_KEY_TOGGLE_STATE *KeyToggleState ) /*++ Routine Description: Set certain state for the input device. Arguments: This - Protocol instance pointer. KeyToggleState - A pointer to the EFI_KEY_TOGGLE_STATE to set the state for the input device. Returns: EFI_SUCCESS - The device state was set successfully. EFI_DEVICE_ERROR - The device is not functioning correctly and could not have the setting adjusted. EFI_UNSUPPORTED - The device does not have the ability to set its state. EFI_INVALID_PARAMETER - KeyToggleState is NULL. --*/ ; typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY) ( IN struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_KEY_DATA *KeyData, IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, OUT EFI_HANDLE *NotifyHandle ) /*++ Routine Description: Register a notification function for a particular keystroke for the input device. Arguments: This - Protocol instance pointer. KeyData - A pointer to a buffer that is filled in with the keystroke information data for the key that was pressed. KeyNotificationFunction - Points to the function to be called when the key sequence is typed specified by KeyData. NotifyHandle - Points to the unique handle assigned to the registered notification. Returns: EFI_SUCCESS - The notification function was registered successfully. EFI_OUT_OF_RESOURCES - Unable to allocate resources for necesssary data structures. EFI_INVALID_PARAMETER - KeyData or NotifyHandle is NULL. --*/ ; typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY) ( IN struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, IN EFI_HANDLE NotificationHandle ) /*++ Routine Description: Remove a registered notification function from a particular keystroke. Arguments: This - Protocol instance pointer. NotificationHandle - The handle of the notification function being unregistered. Returns: EFI_SUCCESS - The notification function was unregistered successfully. EFI_INVALID_PARAMETER - The NotificationHandle is invalid. EFI_NOT_FOUND - Can not find the matching entry in database. --*/ ; typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL { EFI_INPUT_RESET_EX Reset; EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx; EFI_EVENT WaitForKeyEx; EFI_SET_STATE SetState; EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify; EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify; } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; #endif diff --git a/stand/i386/libi386/vbe.c b/stand/i386/libi386/vbe.c index cd90e5d6daed..3caba3081f06 100644 --- a/stand/i386/libi386/vbe.c +++ b/stand/i386/libi386/vbe.c @@ -1,1272 +1,1272 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Jared D. McNeill * All rights reserved. * Copyright 2020 Toomas Soome * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "libi386.h" #include "vbe.h" /* * VESA BIOS Extensions routines */ static struct vbeinfoblock *vbe; static struct modeinfoblock *vbe_mode; static uint16_t *vbe_mode_list; static size_t vbe_mode_list_size; struct vesa_edid_info *edid_info = NULL; /* The default VGA color palette format is 6 bits per primary color. */ int palette_format = 6; #define VESA_MODE_BASE 0x100 /* * palette array for 8-bit indexed colors. In this case, cmap does store * index and pe8 does store actual RGB. This is needed because we may * not be able to read palette data from hardware. */ struct paletteentry *pe8 = NULL; static struct named_resolution { const char *name; const char *alias; unsigned int width; unsigned int height; } resolutions[] = { { .name = "480p", .width = 640, .height = 480, }, { .name = "720p", .width = 1280, .height = 720, }, { .name = "1080p", .width = 1920, .height = 1080, }, { .name = "2160p", .alias = "4k", .width = 3840, .height = 2160, }, { .name = "5k", .width = 5120, .height = 2880, } }; static bool vbe_resolution_compare(struct named_resolution *res, const char *cmp) { if (strcasecmp(res->name, cmp) == 0) return (true); if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0) return (true); return (false); } static void vbe_get_max_resolution(int *width, int *height) { struct named_resolution *res; char *maxres; char *height_start, *width_start; int idx; *width = *height = 0; maxres = getenv("vbe_max_resolution"); /* No max_resolution set? Bail out; choose highest resolution */ if (maxres == NULL) return; /* See if it matches one of our known resolutions */ for (idx = 0; idx < nitems(resolutions); ++idx) { res = &resolutions[idx]; if (vbe_resolution_compare(res, maxres)) { *width = res->width; *height = res->height; return; } } /* Not a known resolution, try to parse it; make a copy we can modify */ maxres = strdup(maxres); if (maxres == NULL) return; height_start = strchr(maxres, 'x'); if (height_start == NULL) { free(maxres); return; } width_start = maxres; *height_start++ = 0; /* Errors from this will effectively mean "no max" */ *width = (int)strtol(width_start, NULL, 0); *height = (int)strtol(height_start, NULL, 0); free(maxres); } int vga_get_reg(int reg, int index) { return (inb(reg + index)); } int vga_get_atr(int reg, int i) { int ret; (void) inb(reg + VGA_GEN_INPUT_STAT_1); outb(reg + VGA_AC_WRITE, i); ret = inb(reg + VGA_AC_READ); (void) inb(reg + VGA_GEN_INPUT_STAT_1); return (ret); } void vga_set_atr(int reg, int i, int v) { (void) inb(reg + VGA_GEN_INPUT_STAT_1); outb(reg + VGA_AC_WRITE, i); outb(reg + VGA_AC_WRITE, v); (void) inb(reg + VGA_GEN_INPUT_STAT_1); } void vga_set_indexed(int reg, int indexreg, int datareg, uint8_t index, uint8_t val) { outb(reg + indexreg, index); outb(reg + datareg, val); } int vga_get_indexed(int reg, int indexreg, int datareg, uint8_t index) { outb(reg + indexreg, index); return (inb(reg + datareg)); } int vga_get_crtc(int reg, int i) { return (vga_get_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i)); } void vga_set_crtc(int reg, int i, int v) { vga_set_indexed(reg, VGA_CRTC_ADDRESS, VGA_CRTC_DATA, i, v); } int vga_get_seq(int reg, int i) { return (vga_get_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i)); } void vga_set_seq(int reg, int i, int v) { vga_set_indexed(reg, VGA_SEQ_ADDRESS, VGA_SEQ_DATA, i, v); } int vga_get_grc(int reg, int i) { return (vga_get_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i)); } void vga_set_grc(int reg, int i, int v) { vga_set_indexed(reg, VGA_GC_ADDRESS, VGA_GC_DATA, i, v); } /* * Return true when this controller is VGA compatible. */ bool vbe_is_vga(void) { if (vbe == NULL) return (false); return ((vbe->Capabilities & VBE_CAP_NONVGA) == 0); } /* Actually assuming mode 3. */ void bios_set_text_mode(int mode) { int atr; if (vbe->Capabilities & VBE_CAP_DAC8) { int m; /* * The mode change should reset the palette format to * 6 bits, but apparently some systems do fail with 8-bit * palette, so we switch to 6-bit here. */ m = 0x0600; (void) biosvbe_palette_format(&m); palette_format = m; } v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = mode; /* set VGA text mode */ v86int(); atr = vga_get_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL); atr &= ~VGA_AC_MC_BI; atr &= ~VGA_AC_MC_ELG; vga_set_atr(VGA_REG_BASE, VGA_AC_MODE_CONTROL, atr); gfx_state.tg_mode = mode; gfx_state.tg_fb_type = FB_TEXT; gfx_state.tg_fb.fb_height = TEXT_ROWS; gfx_state.tg_fb.fb_width = TEXT_COLS; gfx_state.tg_fb.fb_mask_red = (1 << palette_format) - 1 << 16; gfx_state.tg_fb.fb_mask_green = (1 << palette_format) - 1 << 8; gfx_state.tg_fb.fb_mask_blue = (1 << palette_format) - 1 << 0; gfx_state.tg_ctype = CT_INDEXED; env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "1", NULL, NULL); } /* Function 00h - Return VBE Controller Information */ static int biosvbe_info(struct vbeinfoblock *vbep) { struct vbeinfoblock *rvbe; int ret; if (vbep == NULL) return (VBE_FAILED); rvbe = bio_alloc(sizeof(*rvbe)); if (rvbe == NULL) return (VBE_FAILED); /* Now check if we have vesa. */ memset(rvbe, 0, sizeof (*vbe)); memcpy(rvbe->VbeSignature, "VBE2", 4); v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f00; v86.es = VTOPSEG(rvbe); v86.edi = VTOPOFF(rvbe); v86int(); ret = v86.eax & 0xffff; if (ret != VBE_SUCCESS) goto done; if (memcmp(rvbe->VbeSignature, "VESA", 4) != 0) { ret = VBE_NOTSUP; goto done; } bcopy(rvbe, vbep, sizeof(*vbep)); done: bio_free(rvbe, sizeof(*rvbe)); return (ret); } /* Function 01h - Return VBE Mode Information */ static int biosvbe_get_mode_info(int mode, struct modeinfoblock *mi) { struct modeinfoblock *rmi; int ret; rmi = bio_alloc(sizeof(*rmi)); if (rmi == NULL) return (VBE_FAILED); v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f01; v86.ecx = mode; v86.es = VTOPSEG(rmi); v86.edi = VTOPOFF(rmi); v86int(); ret = v86.eax & 0xffff; if (ret != VBE_SUCCESS) goto done; bcopy(rmi, mi, sizeof(*rmi)); done: bio_free(rmi, sizeof(*rmi)); return (ret); } /* Function 02h - Set VBE Mode */ static int biosvbe_set_mode(int mode, struct crtciinfoblock *ci) { int rv; if (vbe->Capabilities & VBE_CAP_DAC8) { int m; /* * The mode change should reset the palette format to * 6 bits, but apparently some systems do fail with 8-bit * palette, so we switch to 6-bit here. */ m = 0x0600; if (biosvbe_palette_format(&m) == VBE_SUCCESS) palette_format = m; } v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f02; v86.ebx = mode | 0x4000; /* set linear FB bit */ v86.es = VTOPSEG(ci); v86.edi = VTOPOFF(ci); v86int(); rv = v86.eax & 0xffff; if (vbe->Capabilities & VBE_CAP_DAC8) { int m; /* Switch to 8-bits per primary color. */ m = 0x0800; if (biosvbe_palette_format(&m) == VBE_SUCCESS) palette_format = m; } env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, "0", NULL, NULL); return (rv); } /* Function 03h - Get VBE Mode */ static int biosvbe_get_mode(int *mode) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f03; v86int(); *mode = v86.ebx & 0x3fff; /* Bits 0-13 */ return (v86.eax & 0xffff); } /* Function 08h - Set/Get DAC Palette Format */ int biosvbe_palette_format(int *format) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f08; v86.ebx = *format; v86int(); *format = (v86.ebx >> 8) & 0xff; return (v86.eax & 0xffff); } /* Function 09h - Set/Get Palette Data */ static int biosvbe_palette_data(int mode, int reg, struct paletteentry *pe) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f09; v86.ebx = mode; v86.edx = reg; v86.ecx = 1; v86.es = VTOPSEG(pe); v86.edi = VTOPOFF(pe); v86int(); return (v86.eax & 0xffff); } /* * Function 15h BL=00h - Report VBE/DDC Capabilities * * int biosvbe_ddc_caps(void) * return: VBE/DDC capabilities */ static int biosvbe_ddc_caps(void) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f15; /* display identification extensions */ v86.ebx = 0; /* report DDC capabilities */ v86.ecx = 0; /* controller unit number (00h = primary) */ v86.es = 0; v86.edi = 0; v86int(); if (VBE_ERROR(v86.eax & 0xffff)) return (0); return (v86.ebx & 0xffff); } /* Function 11h BL=01h - Flat Panel status */ static int biosvbe_ddc_read_flat_panel_info(void *buf) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f11; /* Flat Panel Interface extensions */ v86.ebx = 1; /* Return Flat Panel Information */ v86.es = VTOPSEG(buf); v86.edi = VTOPOFF(buf); v86int(); return (v86.eax & 0xffff); } /* Function 15h BL=01h - Read EDID */ static int biosvbe_ddc_read_edid(int blockno, void *buf) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x4f15; /* display identification extensions */ v86.ebx = 1; /* read EDID */ v86.ecx = 0; /* controller unit number (00h = primary) */ v86.edx = blockno; v86.es = VTOPSEG(buf); v86.edi = VTOPOFF(buf); v86int(); return (v86.eax & 0xffff); } static int vbe_mode_is_supported(struct modeinfoblock *mi) { if ((mi->ModeAttributes & 0x01) == 0) return (0); /* mode not supported by hardware */ if ((mi->ModeAttributes & 0x08) == 0) return (0); /* linear fb not available */ if ((mi->ModeAttributes & 0x10) == 0) return (0); /* text mode */ if (mi->NumberOfPlanes != 1) return (0); /* planar mode not supported */ if (mi->MemoryModel != 0x04 /* Packed pixel */ && mi->MemoryModel != 0x06 /* Direct Color */) return (0); /* unsupported pixel format */ return (1); } static bool vbe_check(void) { if (vbe == NULL) { printf("VBE not available\n"); return (false); } return (true); } static int mode_set(struct env_var *ev, int flags __unused, const void *value) { int mode; if (strcmp(ev->ev_name, "screen.textmode") == 0) { unsigned long v; char *end; if (value == NULL) return (0); errno = 0; v = strtoul(value, &end, 0); if (errno != 0 || *(char *)value == '\0' || *end != '\0' || (v != 0 && v != 1)) return (EINVAL); env_setenv("screen.textmode", EV_VOLATILE | EV_NOHOOK, value, NULL, NULL); if (v == 1) { reset_font_flags(); bios_text_font(true); bios_set_text_mode(VGA_TEXT_MODE); (void) cons_update_mode(false); return (0); } } else if (strcmp(ev->ev_name, "vbe_max_resolution") == 0) { env_setenv("vbe_max_resolution", EV_VOLATILE | EV_NOHOOK, value, NULL, NULL); } else { return (EINVAL); } mode = vbe_default_mode(); if (gfx_state.tg_mode != mode) { reset_font_flags(); bios_text_font(false); vbe_set_mode(mode); cons_update_mode(true); } return (0); } static void * vbe_farptr(uint32_t farptr) { return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)))); } void vbe_init(void) { uint16_t *p, *ml; /* First set FB for text mode. */ gfx_state.tg_fb_type = FB_TEXT; gfx_state.tg_fb.fb_height = TEXT_ROWS; gfx_state.tg_fb.fb_width = TEXT_COLS; gfx_state.tg_ctype = CT_INDEXED; gfx_state.tg_mode = 3; env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set, env_nounset); env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set, env_nounset); if (vbe == NULL) { vbe = malloc(sizeof(*vbe)); if (vbe == NULL) return; } if (vbe_mode == NULL) { vbe_mode = malloc(sizeof(*vbe_mode)); if (vbe_mode == NULL) { free(vbe); vbe = NULL; } } if (biosvbe_info(vbe) != VBE_SUCCESS) { free(vbe); vbe = NULL; free(vbe_mode); vbe_mode = NULL; return; } /* * Copy mode list. We must do this because some systems do * corrupt the provided list (vbox 6.1 is one example). */ p = ml = vbe_farptr(vbe->VideoModePtr); while(*p++ != 0xFFFF) ; vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml; /* * Since vbe_init() is used only once at very start of the loader, * we assume malloc will not fail there, but in case it does, * we point vbe_mode_list to memory pointed by VideoModePtr. */ vbe_mode_list = malloc(vbe_mode_list_size); if (vbe_mode_list == NULL) vbe_mode_list = ml; else bcopy(ml, vbe_mode_list, vbe_mode_list_size); /* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */ vbe->VideoModePtr = 0; /* vbe_set_mode() will set up the rest. */ } bool vbe_available(void) { return (gfx_state.tg_fb_type == FB_VBE); } int vbe_set_palette(const struct paletteentry *entry, size_t slot) { struct paletteentry pe; int mode, ret; if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0) return (1); if (gfx_state.tg_ctype != CT_INDEXED) { return (1); } pe.Blue = entry->Blue; pe.Green = entry->Green; pe.Red = entry->Red; pe.Reserved = entry->Reserved; if (vbe->Capabilities & VBE_CAP_SNOW) mode = 0x80; else mode = 0; ret = biosvbe_palette_data(mode, slot, &pe); return (ret == VBE_SUCCESS ? 0 : 1); } int vbe_get_mode(void) { return (gfx_state.tg_mode); } int vbe_set_mode(int modenum) { struct modeinfoblock mi; int bpp, ret; if (!vbe_check()) return (1); ret = biosvbe_get_mode_info(modenum, &mi); if (VBE_ERROR(ret)) { printf("mode 0x%x invalid\n", modenum); return (1); } if (!vbe_mode_is_supported(&mi)) { printf("mode 0x%x not supported\n", modenum); return (1); } /* calculate bytes per pixel */ switch (mi.BitsPerPixel) { case 32: case 24: case 16: case 15: case 8: break; default: printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel); return (1); } ret = biosvbe_set_mode(modenum, NULL); if (VBE_ERROR(ret)) { printf("mode 0x%x could not be set\n", modenum); return (1); } gfx_state.tg_mode = modenum; gfx_state.tg_fb_type = FB_VBE; /* make sure we have current MI in vbestate */ memcpy(vbe_mode, &mi, sizeof (*vbe_mode)); gfx_state.tg_fb.fb_addr = (uint64_t)mi.PhysBasePtr & 0xffffffff; gfx_state.tg_fb.fb_height = mi.YResolution; gfx_state.tg_fb.fb_width = mi.XResolution; gfx_state.tg_fb.fb_bpp = mi.BitsPerPixel; free(gfx_state.tg_shadow_fb); gfx_state.tg_shadow_fb = malloc(mi.YResolution * mi.XResolution * sizeof(struct paletteentry)); /* Bytes per pixel */ bpp = roundup2(mi.BitsPerPixel, NBBY) / NBBY; /* vbe_mode_is_supported() excludes the rest */ switch (mi.MemoryModel) { case 0x4: gfx_state.tg_ctype = CT_INDEXED; break; case 0x6: gfx_state.tg_ctype = CT_RGB; break; } #define COLOR_MASK(size, pos) (((1 << size) - 1) << pos) if (gfx_state.tg_ctype == CT_INDEXED) { gfx_state.tg_fb.fb_mask_red = COLOR_MASK(palette_format, 16); gfx_state.tg_fb.fb_mask_green = COLOR_MASK(palette_format, 8); gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(palette_format, 0); } else if (vbe->VbeVersion >= 0x300) { gfx_state.tg_fb.fb_mask_red = COLOR_MASK(mi.LinRedMaskSize, mi.LinRedFieldPosition); gfx_state.tg_fb.fb_mask_green = COLOR_MASK(mi.LinGreenMaskSize, mi.LinGreenFieldPosition); gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(mi.LinBlueMaskSize, mi.LinBlueFieldPosition); } else { gfx_state.tg_fb.fb_mask_red = COLOR_MASK(mi.RedMaskSize, mi.RedFieldPosition); gfx_state.tg_fb.fb_mask_green = COLOR_MASK(mi.GreenMaskSize, mi.GreenFieldPosition); gfx_state.tg_fb.fb_mask_blue = COLOR_MASK(mi.BlueMaskSize, mi.BlueFieldPosition); } gfx_state.tg_fb.fb_mask_reserved = ~(gfx_state.tg_fb.fb_mask_red | gfx_state.tg_fb.fb_mask_green | gfx_state.tg_fb.fb_mask_blue); if (vbe->VbeVersion >= 0x300) gfx_state.tg_fb.fb_stride = mi.LinBytesPerScanLine / bpp; else gfx_state.tg_fb.fb_stride = mi.BytesPerScanLine / bpp; gfx_state.tg_fb.fb_size = mi.YResolution * gfx_state.tg_fb.fb_stride * bpp; return (0); } /* - * Verify existance of mode number or find mode by + * Verify existence of mode number or find mode by * dimensions. If depth is not given, walk values 32, 24, 16, 8. */ static int vbe_find_mode_xydm(int x, int y, int depth, int m) { struct modeinfoblock mi; uint16_t *farptr; uint16_t mode; int idx, nentries, i; memset(vbe, 0, sizeof (*vbe)); if (biosvbe_info(vbe) != VBE_SUCCESS) return (0); if (m != -1) i = 8; else if (depth == -1) i = 32; else i = depth; nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); while (i > 0) { for (idx = 0; idx < nentries; idx++) { mode = vbe_mode_list[idx]; if (mode == 0xffff) break; if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { continue; } /* we only care about linear modes here */ if (vbe_mode_is_supported(&mi) == 0) continue; if (m != -1) { if (m == mode) return (mode); else continue; } if (mi.XResolution == x && mi.YResolution == y && mi.BitsPerPixel == i) return (mode); } if (depth != -1) break; i -= 8; } return (0); } static int vbe_find_mode(char *str) { int x, y, depth; if (!gfx_parse_mode_str(str, &x, &y, &depth)) return (0); return (vbe_find_mode_xydm(x, y, depth, -1)); } static void vbe_dump_mode(int modenum, struct modeinfoblock *mi) { printf("0x%x=%dx%dx%d", modenum, mi->XResolution, mi->YResolution, mi->BitsPerPixel); } static bool vbe_get_edid(edid_res_list_t *res) { struct vesa_edid_info *edidp; const uint8_t magic[] = EDID_MAGIC; int ddc_caps; bool ret = false; if (edid_info != NULL) return (gfx_get_edid_resolution(edid_info, res)); ddc_caps = biosvbe_ddc_caps(); if (ddc_caps == 0) { return (ret); } edidp = bio_alloc(sizeof(*edidp)); if (edidp == NULL) return (ret); memset(edidp, 0, sizeof(*edidp)); if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp))) goto done; if (memcmp(edidp, magic, sizeof(magic)) != 0) goto done; /* Unknown EDID version. */ if (edidp->header.version != 1) goto done; ret = gfx_get_edid_resolution(edidp, res); edid_info = malloc(sizeof(*edid_info)); if (edid_info != NULL) memcpy(edid_info, edidp, sizeof (*edid_info)); done: bio_free(edidp, sizeof(*edidp)); return (ret); } static bool vbe_get_flatpanel(uint32_t *pwidth, uint32_t *pheight) { struct vesa_flat_panel_info *fp_info; bool ret = false; fp_info = bio_alloc(sizeof (*fp_info)); if (fp_info == NULL) return (ret); memset(fp_info, 0, sizeof (*fp_info)); if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info))) goto done; *pwidth = fp_info->HSize; *pheight = fp_info->VSize; ret = true; done: bio_free(fp_info, sizeof (*fp_info)); return (ret); } static void vbe_print_memory(unsigned vmem) { char unit = 'K'; vmem /= 1024; if (vmem >= 10240000) { vmem /= 1048576; unit = 'G'; } else if (vmem >= 10000) { vmem /= 1024; unit = 'M'; } printf("Total memory: %u%cB\n", vmem, unit); } static void vbe_print_vbe_info(struct vbeinfoblock *vbep) { char *oemstring = ""; char *oemvendor = "", *oemproductname = "", *oemproductrev = ""; if (vbep->OemStringPtr != 0) oemstring = vbe_farptr(vbep->OemStringPtr); if (vbep->OemVendorNamePtr != 0) oemvendor = vbe_farptr(vbep->OemVendorNamePtr); if (vbep->OemProductNamePtr != 0) oemproductname = vbe_farptr(vbep->OemProductNamePtr); if (vbep->OemProductRevPtr != 0) oemproductrev = vbe_farptr(vbep->OemProductRevPtr); printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8, vbep->VbeVersion & 0xF, oemstring); if (vbep->OemSoftwareRev != 0) { printf("OEM Version %d.%d, %s (%s, %s)\n", vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF, oemvendor, oemproductname, oemproductrev); } vbe_print_memory(vbep->TotalMemory << 16); printf("Number of Image Pages: %d\n", vbe_mode->LinNumberOfImagePages); } /* List available modes, filter by depth. If depth is -1, list all. */ void vbe_modelist(int depth) { struct modeinfoblock mi; uint16_t mode; int nmodes, idx, nentries; int ddc_caps; uint32_t width, height; bool edid = false; edid_res_list_t res; struct resolution *rp; if (!vbe_check()) return; ddc_caps = biosvbe_ddc_caps(); if (ddc_caps & 3) { printf("DDC"); if (ddc_caps & 1) printf(" [DDC1]"); if (ddc_caps & 2) printf(" [DDC2]"); TAILQ_INIT(&res); edid = vbe_get_edid(&res); if (edid) { printf(": EDID"); while ((rp = TAILQ_FIRST(&res)) != NULL) { printf(" %dx%d", rp->width, rp->height); TAILQ_REMOVE(&res, rp, next); free(rp); } printf("\n"); } else { printf(": no EDID information\n"); } } if (!edid) if (vbe_get_flatpanel(&width, &height)) printf(": Panel %dx%d\n", width, height); nmodes = 0; memset(vbe, 0, sizeof (*vbe)); memcpy(vbe->VbeSignature, "VBE2", 4); if (biosvbe_info(vbe) != VBE_SUCCESS) goto done; if (memcmp(vbe->VbeSignature, "VESA", 4) != 0) goto done; vbe_print_vbe_info(vbe); printf("Modes: "); nentries = vbe_mode_list_size / sizeof(*vbe_mode_list); for (idx = 0; idx < nentries; idx++) { mode = vbe_mode_list[idx]; if (mode == 0xffff) break; if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) continue; /* we only care about linear modes here */ if (vbe_mode_is_supported(&mi) == 0) continue; /* apply requested filter */ if (depth != -1 && mi.BitsPerPixel != depth) continue; if (nmodes % 4 == 0) printf("\n"); else printf(" "); vbe_dump_mode(mode, &mi); nmodes++; } done: if (nmodes == 0) printf("none found"); printf("\n"); } static void vbe_print_mode(bool verbose __unused) { int nc, mode, i, rc; nc = NCOLORS; memset(vbe, 0, sizeof (*vbe)); if (biosvbe_info(vbe) != VBE_SUCCESS) return; vbe_print_vbe_info(vbe); if (biosvbe_get_mode(&mode) != VBE_SUCCESS) { printf("Error getting current VBE mode\n"); return; } if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS || vbe_mode_is_supported(vbe_mode) == 0) { printf("VBE mode (0x%x) is not framebuffer mode\n", mode); return; } printf("\nCurrent VBE mode: "); vbe_dump_mode(mode, vbe_mode); printf("\n"); printf("%ux%ux%u, stride=%u\n", gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, gfx_state.tg_fb.fb_bpp, gfx_state.tg_fb.fb_stride * (roundup2(gfx_state.tg_fb.fb_bpp, NBBY) / NBBY)); printf(" frame buffer: address=%jx, size=%jx\n", (uintmax_t)gfx_state.tg_fb.fb_addr, (uintmax_t)gfx_state.tg_fb.fb_size); if (vbe_mode->MemoryModel == 0x6) { printf(" color mask: R=%08x, G=%08x, B=%08x\n", gfx_state.tg_fb.fb_mask_red, gfx_state.tg_fb.fb_mask_green, gfx_state.tg_fb.fb_mask_blue); pager_open(); for (i = 0; i < nc; i++) { printf("%d: R=%02x, G=%02x, B=%02x %08x", i, (cmap[i] & gfx_state.tg_fb.fb_mask_red) >> ffs(gfx_state.tg_fb.fb_mask_red) - 1, (cmap[i] & gfx_state.tg_fb.fb_mask_green) >> ffs(gfx_state.tg_fb.fb_mask_green) - 1, (cmap[i] & gfx_state.tg_fb.fb_mask_blue) >> ffs(gfx_state.tg_fb.fb_mask_blue) - 1, cmap[i]); if (pager_output("\n") != 0) break; } pager_close(); return; } mode = 1; /* get DAC palette width */ rc = biosvbe_palette_format(&mode); if (rc != VBE_SUCCESS) return; printf(" palette format: %x bits per primary\n", mode); if (pe8 == NULL) return; pager_open(); for (i = 0; i < nc; i++) { printf("%d: R=%02x, G=%02x, B=%02x", i, pe8[i].Red, pe8[i].Green, pe8[i].Blue); if (pager_output("\n") != 0) break; } pager_close(); } /* * Try EDID preferred mode, if EDID or the suggested mode is not available, * then try flat panel information. * Fall back to VBE_DEFAULT_MODE. */ int vbe_default_mode(void) { edid_res_list_t res; struct resolution *rp; int modenum; uint32_t width, height; modenum = 0; vbe_get_max_resolution(&width, &height); if (width != 0 && height != 0) modenum = vbe_find_mode_xydm(width, height, -1, -1); TAILQ_INIT(&res); if (vbe_get_edid(&res)) { while ((rp = TAILQ_FIRST(&res)) != NULL) { if (modenum == 0) { modenum = vbe_find_mode_xydm( rp->width, rp->height, -1, -1); } TAILQ_REMOVE(&res, rp, next); free(rp); } } if (modenum == 0 && vbe_get_flatpanel(&width, &height)) { modenum = vbe_find_mode_xydm(width, height, -1, -1); } /* Still no mode? Fall back to default. */ if (modenum == 0) modenum = vbe_find_mode(VBE_DEFAULT_MODE); return (modenum); } COMMAND_SET(vbe, "vbe", "vesa framebuffer mode management", command_vesa); int command_vesa(int argc, char *argv[]) { char *arg, *cp; int modenum = -1, n; if (!vbe_check()) return (CMD_OK); if (argc < 2) goto usage; if (strcmp(argv[1], "list") == 0) { n = -1; if (argc != 2 && argc != 3) goto usage; if (argc == 3) { arg = argv[2]; errno = 0; n = strtoul(arg, &cp, 0); if (errno != 0 || *arg == '\0' || cp[0] != '\0') { snprintf(command_errbuf, sizeof (command_errbuf), "depth should be an integer"); return (CMD_ERROR); } } vbe_modelist(n); return (CMD_OK); } if (strcmp(argv[1], "get") == 0) { bool verbose = false; if (argc != 2) { if (argc > 3 || strcmp(argv[2], "-v") != 0) goto usage; verbose = true; } vbe_print_mode(verbose); return (CMD_OK); } if (strcmp(argv[1], "off") == 0) { if (argc != 2) goto usage; if (gfx_state.tg_mode == VGA_TEXT_MODE) return (CMD_OK); reset_font_flags(); bios_text_font(true); bios_set_text_mode(VGA_TEXT_MODE); cons_update_mode(false); return (CMD_OK); } if (strcmp(argv[1], "on") == 0) { if (argc != 2) goto usage; modenum = vbe_default_mode(); if (modenum == 0) { snprintf(command_errbuf, sizeof (command_errbuf), "%s: no suitable VBE mode number found", argv[0]); return (CMD_ERROR); } } else if (strcmp(argv[1], "set") == 0) { if (argc != 3) goto usage; if (strncmp(argv[2], "0x", 2) == 0) { arg = argv[2]; errno = 0; n = strtoul(arg, &cp, 0); if (errno != 0 || *arg == '\0' || cp[0] != '\0') { snprintf(command_errbuf, sizeof (command_errbuf), "mode should be an integer"); return (CMD_ERROR); } modenum = vbe_find_mode_xydm(0, 0, 0, n); } else if (strchr(argv[2], 'x') != NULL) { modenum = vbe_find_mode(argv[2]); } } else { goto usage; } if (modenum == 0) { snprintf(command_errbuf, sizeof (command_errbuf), "%s: mode %s not supported by firmware\n", argv[0], argv[2]); return (CMD_ERROR); } if (modenum >= VESA_MODE_BASE) { if (gfx_state.tg_mode != modenum) { reset_font_flags(); bios_text_font(false); vbe_set_mode(modenum); cons_update_mode(true); } return (CMD_OK); } else { snprintf(command_errbuf, sizeof (command_errbuf), "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]); return (CMD_ERROR); } usage: snprintf(command_errbuf, sizeof (command_errbuf), "usage: %s on | off | get | list [depth] | " "set ", argv[0]); return (CMD_ERROR); } diff --git a/stand/libsa/geli/geliboot.c b/stand/libsa/geli/geliboot.c index 954a3ec34044..f7069e3887ee 100644 --- a/stand/libsa/geli/geliboot.c +++ b/stand/libsa/geli/geliboot.c @@ -1,400 +1,400 @@ /*- * Copyright (c) 2015 Allan Jude * Copyright (c) 2005-2011 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include "geliboot.h" #include "geliboot_internal.h" struct known_dev { char name[GELIDEV_NAMELEN]; struct geli_dev *gdev; SLIST_ENTRY(known_dev) entries; }; SLIST_HEAD(known_dev_list, known_dev) known_devs_head = SLIST_HEAD_INITIALIZER(known_devs_head); static geli_ukey saved_keys[GELI_MAX_KEYS]; static unsigned int nsaved_keys = 0; /* * Copy keys from local storage to the keybuf struct. * Destroy the local storage when finished. */ void geli_export_key_buffer(struct keybuf *fkeybuf) { unsigned int i; for (i = 0; i < nsaved_keys; i++) { fkeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_GELI; memcpy(fkeybuf->kb_ents[i].ke_data, saved_keys[i], G_ELI_USERKEYLEN); } fkeybuf->kb_nents = nsaved_keys; explicit_bzero(saved_keys, sizeof(saved_keys)); } /* * Copy keys from a keybuf struct into local storage. * Zero out the keybuf. */ void geli_import_key_buffer(struct keybuf *skeybuf) { unsigned int i; for (i = 0; i < skeybuf->kb_nents && i < GELI_MAX_KEYS; i++) { memcpy(saved_keys[i], skeybuf->kb_ents[i].ke_data, G_ELI_USERKEYLEN); explicit_bzero(skeybuf->kb_ents[i].ke_data, G_ELI_USERKEYLEN); skeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_NONE; } nsaved_keys = skeybuf->kb_nents; skeybuf->kb_nents = 0; } void geli_add_key(geli_ukey key) { /* * If we run out of key space, the worst that will happen is * it will ask the user for the password again. */ if (nsaved_keys < GELI_MAX_KEYS) { memcpy(saved_keys[nsaved_keys], key, G_ELI_USERKEYLEN); nsaved_keys++; } } static int geli_findkey(struct geli_dev *gdev, u_char *mkey) { u_int keynum; int i; if (gdev->keybuf_slot >= 0) { if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[gdev->keybuf_slot], mkey, &keynum) == 0) { return (0); } } for (i = 0; i < nsaved_keys; i++) { if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[i], mkey, &keynum) == 0) { gdev->keybuf_slot = i; return (0); } } return (1); } /* * Read the last sector of a drive or partition and see if it is GELI encrypted. */ struct geli_dev * geli_taste(geli_readfunc readfunc, void *readpriv, daddr_t lastsector, const char *namefmt, ...) { va_list args; struct g_eli_metadata md; struct known_dev *kdev; struct geli_dev *gdev; u_char *buf; char devname[GELIDEV_NAMELEN]; int error; off_t alignsector; /* * Format the name into a temp buffer and use that to search for an * existing known_dev instance. If not found, this has the side effect * of initializing kdev to NULL. */ va_start(args, namefmt); vsnprintf(devname, sizeof(devname), namefmt, args); va_end(args); SLIST_FOREACH(kdev, &known_devs_head, entries) { if (strcmp(kdev->name, devname) == 0) return (kdev->gdev); } /* Determine whether the new device is geli-encrypted... */ if ((buf = malloc(DEV_GELIBOOT_BSIZE)) == NULL) goto out; alignsector = rounddown2(lastsector * DEV_BSIZE, DEV_GELIBOOT_BSIZE); if (alignsector + DEV_GELIBOOT_BSIZE > ((lastsector + 1) * DEV_BSIZE)) { /* Don't read past the end of the disk */ alignsector = (lastsector * DEV_BSIZE) + DEV_BSIZE - DEV_GELIBOOT_BSIZE; } error = readfunc(NULL, readpriv, alignsector, buf, DEV_GELIBOOT_BSIZE); if (error != 0) { goto out; } /* * We have a new known_device. Whether it's geli-encrypted or not, - * record its existance so we can avoid doing IO to probe it next time. + * record its existence so we can avoid doing IO to probe it next time. */ if ((kdev = malloc(sizeof(*kdev))) == NULL) goto out; strlcpy(kdev->name, devname, sizeof(kdev->name)); kdev->gdev = NULL; SLIST_INSERT_HEAD(&known_devs_head, kdev, entries); /* Extract the last 4k sector of the disk. */ error = eli_metadata_decode(buf, &md); if (error != 0) { /* Try the last 512 byte sector instead. */ error = eli_metadata_decode(buf + (DEV_GELIBOOT_BSIZE - DEV_BSIZE), &md); if (error != 0) { goto out; } } if (!(md.md_flags & G_ELI_FLAG_GELIBOOT)) { /* The GELIBOOT feature is not activated */ goto out; } if ((md.md_flags & G_ELI_FLAG_ONETIME)) { /* Swap device, skip it. */ goto out; } /* * It's geli-encrypted, create a geli_dev for it and link it into the * known_dev instance. */ gdev = malloc(sizeof(struct geli_dev)); if (gdev == NULL) goto out; gdev->part_end = lastsector; gdev->keybuf_slot = -1; gdev->md = md; gdev->name = kdev->name; eli_metadata_softc(&gdev->sc, &md, DEV_BSIZE, (lastsector + DEV_BSIZE) * DEV_BSIZE); kdev->gdev = gdev; out: free(buf); if (kdev == NULL) return (NULL); return (kdev->gdev); } /* * Attempt to decrypt the device. This will try existing keys first, then will * prompt for a passphrase if there are no existing keys that work. */ static int geli_probe(struct geli_dev *gdev, const char *passphrase, u_char *mkeyp) { u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp; u_int keynum; struct hmac_ctx ctx; int error; if (mkeyp != NULL) { memcpy(&mkey, mkeyp, G_ELI_DATAIVKEYLEN); explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN); goto found_key; } if (geli_findkey(gdev, mkey) == 0) { goto found_key; } g_eli_crypto_hmac_init(&ctx, NULL, 0); /* * Prepare Derived-Key from the user passphrase. */ if (gdev->md.md_iterations < 0) { /* XXX TODO: Support loading key files. */ return (1); } else if (gdev->md.md_iterations == 0) { g_eli_crypto_hmac_update(&ctx, gdev->md.md_salt, sizeof(gdev->md.md_salt)); g_eli_crypto_hmac_update(&ctx, (const uint8_t *)passphrase, strlen(passphrase)); } else if (gdev->md.md_iterations > 0) { printf("Calculating GELI Decryption Key for %s %d" " iterations...\n", gdev->name, gdev->md.md_iterations); u_char dkey[G_ELI_USERKEYLEN]; pkcs5v2_genkey(dkey, sizeof(dkey), gdev->md.md_salt, sizeof(gdev->md.md_salt), passphrase, gdev->md.md_iterations); g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); explicit_bzero(dkey, sizeof(dkey)); } g_eli_crypto_hmac_final(&ctx, key, 0); error = g_eli_mkey_decrypt_any(&gdev->md, key, mkey, &keynum); if (error == -1) { explicit_bzero(mkey, sizeof(mkey)); explicit_bzero(key, sizeof(key)); printf("Bad GELI key: bad password?\n"); return (error); } else if (error != 0) { explicit_bzero(mkey, sizeof(mkey)); explicit_bzero(key, sizeof(key)); printf("Failed to decrypt GELI master key: %d\n", error); return (error); } else { /* Add key to keychain */ geli_add_key(key); explicit_bzero(&key, sizeof(key)); } found_key: /* Store the keys */ bcopy(mkey, gdev->sc.sc_mkey, sizeof(gdev->sc.sc_mkey)); bcopy(mkey, gdev->sc.sc_ivkey, sizeof(gdev->sc.sc_ivkey)); mkp = mkey + sizeof(gdev->sc.sc_ivkey); if ((gdev->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) { bcopy(mkp, gdev->sc.sc_ekey, G_ELI_DATAKEYLEN); } else { /* * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) */ g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, (const uint8_t *)"\x10", 1, gdev->sc.sc_ekey, 0); } explicit_bzero(mkey, sizeof(mkey)); /* Initialize the per-sector IV. */ switch (gdev->sc.sc_ealgo) { case CRYPTO_AES_XTS: break; default: SHA256_Init(&gdev->sc.sc_ivctx); SHA256_Update(&gdev->sc.sc_ivctx, gdev->sc.sc_ivkey, sizeof(gdev->sc.sc_ivkey)); break; } return (0); } int geli_io(struct geli_dev *gdev, geli_op_t enc, off_t offset, u_char *buf, size_t bytes) { u_char iv[G_ELI_IVKEYLEN]; u_char *pbuf; int error; off_t dstoff; uint64_t keyno; size_t n, nsec, secsize; struct g_eli_key gkey; pbuf = buf; secsize = gdev->sc.sc_sectorsize; nsec = bytes / secsize; if (nsec == 0) { /* * A read of less than the GELI sector size has been * requested. The caller provided destination buffer may * not be big enough to boost the read to a full sector, * so just attempt to decrypt the truncated sector. */ secsize = bytes; nsec = 1; } for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) { g_eli_crypto_ivgen(&gdev->sc, dstoff, iv, G_ELI_IVKEYLEN); /* Get the key that corresponds to this offset. */ keyno = (dstoff >> G_ELI_KEY_SHIFT) / secsize; g_eli_key_fill(&gdev->sc, &gkey, keyno); error = geliboot_crypt(gdev->sc.sc_ealgo, enc, pbuf, secsize, gkey.gek_key, gdev->sc.sc_ekeylen, iv); if (error != 0) { explicit_bzero(&gkey, sizeof(gkey)); printf("%s: Failed to %s!", __func__, enc ? "encrypt" : "decrypt"); return (error); } pbuf += secsize; } explicit_bzero(&gkey, sizeof(gkey)); return (0); } int geli_havekey(struct geli_dev *gdev) { u_char mkey[G_ELI_DATAIVKEYLEN]; int err; err = ENOENT; if (geli_findkey(gdev, mkey) == 0) { if (geli_probe(gdev, NULL, mkey) == 0) err = 0; explicit_bzero(mkey, sizeof(mkey)); } return (err); } int geli_passphrase(struct geli_dev *gdev, char *pw) { int i; /* TODO: Implement GELI keyfile(s) support */ for (i = 0; i < 3; i++) { /* Try cached passphrase */ if (i == 0 && pw[0] != '\0') { if (geli_probe(gdev, pw, NULL) == 0) { return (0); } } printf("GELI Passphrase for %s ", gdev->name); pwgets(pw, GELI_PW_MAXLEN, (gdev->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0); printf("\n"); if (geli_probe(gdev, pw, NULL) == 0) { return (0); } } return (1); }