]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/boot/efi/console.c
codespell: fix spelling errors
[thirdparty/systemd.git] / src / boot / efi / console.c
index 2151d3443202bf828bd00a95d1b951fc23a3c9df..7f6bad1538e22d93d789b963137b3775cd0b01b7 100644 (file)
@@ -1,19 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * Copyright (C) 2012-2013 Kay Sievers <kay@vrfy.org>
- * Copyright (C) 2012 Harald Hoyer <harald@redhat.com>
- */
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <efi.h>
 #include <efilib.h>
@@ -21,6 +6,9 @@
 #include "console.h"
 #include "util.h"
 
+#define SYSTEM_FONT_WIDTH 8
+#define SYSTEM_FONT_HEIGHT 19
+
 #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
         { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
 
@@ -95,12 +83,8 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
         }
 
         /* wait until key is pressed */
-        if (wait) {
-                if (TextInputEx)
-                        uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TextInputEx->WaitForKeyEx, &index);
-                else
-                        uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
-        }
+        if (wait)
+                uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
 
         if (TextInputEx) {
                 EFI_KEY_DATA keydata;
@@ -130,7 +114,7 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
         /* fallback for firmware which does not support SimpleTextInputExProtocol
          *
          * This is also called in case ReadKeyStrokeEx did not return a key, because
-         * some broken firmwares offer SimpleTextInputExProtocol, but never acually
+         * some broken firmwares offer SimpleTextInputExProtocol, but never actually
          * handle any key. */
         err  = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
         if (EFI_ERROR(err))
@@ -139,3 +123,105 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
         *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
         return 0;
 }
+
+static EFI_STATUS change_mode(UINTN mode) {
+        EFI_STATUS err;
+
+        err = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, mode);
+
+        /* Special case mode 1: when using OVMF and qemu, setting it returns error
+         * and breaks console output. */
+        if (EFI_ERROR(err) && mode == 1)
+                uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, (UINTN)0);
+
+        return err;
+}
+
+static UINT64 text_area_from_font_size(void) {
+        EFI_STATUS err;
+        UINT64 text_area;
+        UINTN rows, columns;
+
+        err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &columns, &rows);
+        if (EFI_ERROR(err)) {
+                columns = 80;
+                rows = 25;
+        }
+
+        text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)rows * (UINT64)columns;
+
+        return text_area;
+}
+
+static EFI_STATUS mode_auto(UINTN *mode) {
+        const UINT32 HORIZONTAL_MAX_OK = 1920;
+        const UINT32 VERTICAL_MAX_OK = 1080;
+        const UINT64 VIEWPORT_RATIO = 10;
+        UINT64 screen_area, text_area;
+        EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+        EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+        EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+        EFI_STATUS err;
+        BOOLEAN keep = FALSE;
+
+        err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
+        if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
+                Info = GraphicsOutput->Mode->Info;
+
+                /* Start verifying if we are in a resolution larger than Full HD
+                 * (1920x1080). If we're not, assume we're in a good mode and do not
+                 * try to change it. */
+                if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK)
+                        keep = TRUE;
+                /* For larger resolutions, calculate the ratio of the total screen
+                 * area to the text viewport area. If it's less than 10 times bigger,
+                 * then assume the text is readable and keep the text mode. */
+                else {
+                        screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
+                        text_area = text_area_from_font_size();
+
+                        if (text_area != 0 && screen_area/text_area < VIEWPORT_RATIO)
+                                keep = TRUE;
+                }
+        }
+
+        if (keep) {
+                /* Just clear the screen instead of changing the mode and return. */
+                *mode = ST->ConOut->Mode->Mode;
+                uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+                return EFI_SUCCESS;
+        }
+
+        /* If we reached here, then we have a high resolution screen and the text
+         * viewport is less than 10% the screen area, so the firmware developer
+         * screwed up. Try to switch to a better mode. Mode number 2 is first non
+         * standard mode, which is provided by the device manufacturer, so it should
+         * be a good mode.
+         * Note: MaxMode is the number of modes, not the last mode. */
+        if (ST->ConOut->Mode->MaxMode > 2)
+                *mode = 2;
+        /* Try again with mode different than zero (assume user requests
+         * auto mode due to some problem with mode zero). */
+        else if (ST->ConOut->Mode->MaxMode == 2)
+                *mode = 1;
+        /* Else force mode change to zero. */
+        else
+                *mode = 0;
+
+        return change_mode(*mode);
+}
+
+EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
+        if (how == CONSOLE_MODE_AUTO)
+                return mode_auto(mode);
+
+        if (how == CONSOLE_MODE_MAX) {
+                /* Note: MaxMode is the number of modes, not the last mode. */
+                if (ST->ConOut->Mode->MaxMode > 0)
+                        *mode = ST->ConOut->Mode->MaxMode-1;
+                else
+                        *mode = 0;
+        }
+
+        return change_mode(*mode);
+}