diff --git a/SetupDataPkg/ConfApp/BootOptions.c b/SetupDataPkg/ConfApp/BootOptions.c index 3ab5eac4..c224f6c6 100644 --- a/SetupDataPkg/ConfApp/BootOptions.c +++ b/SetupDataPkg/ConfApp/BootOptions.c @@ -20,13 +20,14 @@ #include "ConfApp.h" -#define STATIC_BOOT_OPTIONS 2 +#define STATIC_BOOT_OPTIONS 2 +#define MAX_BOOT_OPTION_INPUT_DIGITS 5 CONST ConfAppKeyOptions StaticBootOptions[STATIC_BOOT_OPTIONS] = { { .KeyName = NULL, .KeyNameTextAttr = EFI_TEXT_ATTR (EFI_YELLOW, EFI_BLACK), - .Description = L"\n\tSelect Index to boot to the corresponding option.\n", + .Description = L"\n\tType index number and press Enter to boot to the corresponding option.\n", .DescriptionTextAttr = EFI_TEXT_ATTR (EFI_WHITE, EFI_BLACK), .UnicodeChar = CHAR_NULL, .ScanCode = SCAN_NULL, @@ -47,9 +48,11 @@ BootOptState_t mBootOptState = BootOptInit; EFI_BOOT_MANAGER_LOAD_OPTION *mBootOptions = NULL; UINTN mBootOptionCount = 0; UINT16 mOpCandidate; -ConfAppKeyOptions *mKeyOptions = NULL; -UINTN mOptionCount = 0; -CHAR16 *mKeyNames = NULL; +ConfAppKeyOptions *mKeyOptions = NULL; +UINTN mOptionCount = 0; +CHAR16 *mKeyNames = NULL; +CHAR16 mInputBuffer[MAX_BOOT_OPTION_INPUT_DIGITS + 1] = { CHAR_NULL }; +UINTN mInputLen = 0; /** Helper internal function to reset all local variable in this file. @@ -65,6 +68,9 @@ ResetGlobals ( mBootOptionCount = 0; mOpCandidate = 0; + mInputBuffer[0] = CHAR_NULL; + mInputLen = 0; + mOptionCount = 0; if (mKeyOptions != NULL) { FreePool (mKeyOptions); @@ -105,9 +111,9 @@ PrintBootOptions ( mKeyOptions[OptionIndex].KeyNameTextAttr = EFI_TEXT_ATTR (EFI_YELLOW, EFI_BLACK); mKeyOptions[OptionIndex].Description = mBootOptions[OptionIndex].Description; mKeyOptions[OptionIndex].DescriptionTextAttr = EFI_TEXT_ATTR (EFI_WHITE, EFI_BLACK); - mKeyOptions[OptionIndex].UnicodeChar = (CHAR16)(OptionIndex + '1'); + mKeyOptions[OptionIndex].UnicodeChar = CHAR_NULL; mKeyOptions[OptionIndex].ScanCode = SCAN_NULL; - mKeyOptions[OptionIndex].EndState = BootOptBootNow; + mKeyOptions[OptionIndex].EndState = MAX_UINT32; } CopyMem (&mKeyOptions[OptionIndex], StaticBootOptions, sizeof (ConfAppKeyOptions) * STATIC_BOOT_OPTIONS); @@ -162,14 +168,42 @@ BootOptionMgr ( DEBUG ((DEBUG_ERROR, "%a Error occurred waiting for key stroke - %r\n", __func__, Status)); ASSERT (FALSE); } else { - Status = CheckSupportedOptions (&KeyData, mKeyOptions, mOptionCount, (UINT32 *)&mBootOptState); - if (Status == EFI_NOT_FOUND) { - Status = EFI_SUCCESS; - } else if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "%a Error processing incoming keystroke - %r\n", __func__, Status)); - ASSERT (FALSE); - } else { - mOpCandidate = KeyData.Key.UnicodeChar - '1'; + if ((KeyData.Key.ScanCode == SCAN_ESC) && (KeyData.Key.UnicodeChar == CHAR_NULL)) { + mInputLen = 0; + mInputBuffer[0] = CHAR_NULL; + mBootOptState = BootOptExit; + } else if (KeyData.Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + if (mInputLen > 0) { + UINTN Selection; + UINTN DigitIndex; + + Selection = 0; + for (DigitIndex = 0; DigitIndex < mInputLen; DigitIndex++) { + Selection = Selection * 10 + (mInputBuffer[DigitIndex] - '0'); + } + + mInputLen = 0; + mInputBuffer[0] = CHAR_NULL; + if ((Selection >= 1) && (Selection <= mBootOptionCount)) { + mOpCandidate = (UINT16)(Selection - 1); + mBootOptState = BootOptBootNow; + } + } + } else if (KeyData.Key.UnicodeChar == CHAR_BACKSPACE) { + if (mInputLen > 0) { + mInputLen--; + mInputBuffer[mInputLen] = CHAR_NULL; + Print (L"%c %c", CHAR_BACKSPACE, CHAR_BACKSPACE); + } + } else if ((KeyData.Key.UnicodeChar >= '0') && (KeyData.Key.UnicodeChar <= '9')) { + // Ignore a leading zero + if (!((mInputLen == 0) && (KeyData.Key.UnicodeChar == '0'))) { + if (mInputLen < MAX_BOOT_OPTION_INPUT_DIGITS) { + mInputBuffer[mInputLen++] = KeyData.Key.UnicodeChar; + mInputBuffer[mInputLen] = CHAR_NULL; + Print (L"%c", KeyData.Key.UnicodeChar); + } + } } } diff --git a/SetupDataPkg/ConfApp/UnitTest/ConfAppBootOptionUnitTest.c b/SetupDataPkg/ConfApp/UnitTest/ConfAppBootOptionUnitTest.c index f8ea14b6..41d44fb2 100644 --- a/SetupDataPkg/ConfApp/UnitTest/ConfAppBootOptionUnitTest.c +++ b/SetupDataPkg/ConfApp/UnitTest/ConfAppBootOptionUnitTest.c @@ -480,6 +480,14 @@ ConfAppBootOptSelectOne ( KeyData1.Key.ScanCode = SCAN_NULL; will_return (MockReadKey, &KeyData1); + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptWait); + + KeyData1.Key.UnicodeChar = CHAR_CARRIAGE_RETURN; + KeyData1.Key.ScanCode = SCAN_NULL; + will_return (MockReadKey, &KeyData1); + Status = BootOptionMgr (); UT_ASSERT_NOT_EFI_ERROR (Status); UT_ASSERT_EQUAL (mBootOptState, BootOptBootNow); @@ -555,6 +563,14 @@ ConfAppBootOptSelectMore ( KeyData1.Key.ScanCode = SCAN_NULL; will_return (MockReadKey, &KeyData1); + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptWait); + + KeyData1.Key.UnicodeChar = CHAR_CARRIAGE_RETURN; + KeyData1.Key.ScanCode = SCAN_NULL; + will_return (MockReadKey, &KeyData1); + Status = BootOptionMgr (); UT_ASSERT_NOT_EFI_ERROR (Status); UT_ASSERT_EQUAL (mBootOptState, BootOptBootNow); @@ -572,6 +588,98 @@ ConfAppBootOptSelectMore ( return UNIT_TEST_PASSED; } +/** + Unit test for BootOptions page when selecting boot option 10 (two-digit input). + + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that the + contents are well understood by all test cases that may + consume it. + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +ConfAppBootOptSelect10 ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_KEY_DATA KeyData1; + UINTN Index; + + EFI_BOOT_MANAGER_LOAD_OPTION BootOptions[10]; + + for (Index = 0; Index < 10; Index++) { + BootOptions[Index].Description = L"TestOption"; + BootOptions[Index].Attributes = 0; + } + + BootOptions[9].Description = L"Test10"; + BootOptions[9].Attributes = 0xDEADBEEF; + + will_return (MockClearScreen, EFI_SUCCESS); + will_return_always (MockSetAttribute, EFI_SUCCESS); + + expect_any (MockSetCursorPosition, Column); + expect_any (MockSetCursorPosition, Row); + will_return (MockSetCursorPosition, EFI_SUCCESS); + + will_return (EfiBootManagerGetLoadOptions, 10); + will_return (EfiBootManagerGetLoadOptions, BootOptions); + + // Initial run + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptWait); + + mSimpleTextInEx = &MockSimpleInput; + + // Press '1' + KeyData1.Key.UnicodeChar = '1'; + KeyData1.Key.ScanCode = SCAN_NULL; + will_return (MockReadKey, &KeyData1); + + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptWait); + + // Press '0' + KeyData1.Key.UnicodeChar = '0'; + KeyData1.Key.ScanCode = SCAN_NULL; + will_return (MockReadKey, &KeyData1); + + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptWait); + + // Press Enter to confirm + KeyData1.Key.UnicodeChar = CHAR_CARRIAGE_RETURN; + KeyData1.Key.ScanCode = SCAN_NULL; + will_return (MockReadKey, &KeyData1); + + Status = BootOptionMgr (); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_EQUAL (mBootOptState, BootOptBootNow); + + expect_memory (EfiBootManagerBoot, BootOption, &BootOptions[9], sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); + will_return (EfiBootManagerBoot, EFI_SUCCESS); + + gResetCalled = FALSE; + expect_value (ResetSystemWithSubtype, ResetType, EfiResetCold); + expect_value (ResetSystemWithSubtype, ResetSubtype, &gConfAppResetGuid); + + BootOptionMgr (); + UT_ASSERT_TRUE (gResetCalled); + + return UNIT_TEST_PASSED; +} + /** Initialize the unit test framework, suite, and unit tests for the ConfApp and run the ConfApp unit test. @@ -622,6 +730,7 @@ UnitTestingEntry ( AddTestCase (MiscTests, "Boot Options page select others should do nothing", "SelectOther", ConfAppBootOptSelectOther, NULL, BootOptionsCleanup, NULL); AddTestCase (MiscTests, "Boot Options page should boot to single option", "BootOptionSingle", ConfAppBootOptSelectOne, NULL, BootOptionsCleanup, NULL); AddTestCase (MiscTests, "Boot Options page should boot to multiple options", "BootOptionMultiple", ConfAppBootOptSelectMore, NULL, BootOptionsCleanup, NULL); + AddTestCase (MiscTests, "Boot Options page should boot to option 10 with two-digit input", "BootOption10", ConfAppBootOptSelect10, NULL, BootOptionsCleanup, NULL); // // Execute the tests.