From 0033f8e82d4d44c75439dafbca8616cecc665d42 Mon Sep 17 00:00:00 2001 From: John Schock Date: Tue, 31 Oct 2023 08:28:19 -0700 Subject: [PATCH] Add HiiKeyboardLayout crate to support UEFI HII Keyboard Layouts (#342) ## Description This crate provides a rust wrapper around UEFI HII Keyboard Layout structures. The relevant structures defined in the UEFI spec are not well-suited for direct definition in rust; so this crate defines analogous rust structures and provides serialization/deserialization support for converting the rust structures into byte buffers and vice versa. This crate uses the `scroll` crate (https://github.com/m4b/scroll) to facilitate serialization/deserialization of the Hii structures. ## Examples and Usage Retrieving a default (en-US) layout, writing it to a buffer, and then reading the buffer back into a rust structure: ``` use hii_keyboard_layout::{get_default_keyboard_pkg, HiiKeyboardPkg}; use scroll::{Pread, Pwrite}; let mut buffer = [0u8; 4096]; let package = get_default_keyboard_pkg(); buffer.pwrite(&package, 0).unwrap(); let package2: HiiKeyboardPkg = buffer.pread(0).unwrap(); assert_eq!(package, package2); ``` - [x] Impacts functionality? - Introduces a new crate providing support for HII layouts. - [ ] Impacts security? - [ ] Breaking change? - [x] Includes tests? - Includes standard rust unit tests. - [x] Includes documentation? - Includes standard rust documentation. ## How This Was Tested Verified by included unit tests. ## Integration Instructions This crate requires the "scroll," "num-traits" and "num-drive" crates, so platforms intending to use it will need to add these as dependencies in their workspace Cargo.toml files. This PR does this for the workspace Cargo.toml that is at the root of mu_plus. Sample: ``` scroll = { version = "0.11", default-features = false, features = ["derive"]} num-traits = { version = "0.2", default-features = false} num-derive = { version = "0.4", default-features = false} --- Cargo.toml | 6 +- HidPkg/Crates/HiiKeyboardLayout/Cargo.toml | 15 + .../resources/test/KeyboardLayout.bin | Bin 0 -> 1757 bytes .../resources/test/KeyboardLayout.txt | 130 +++ .../resources/test/KeyboardLayoutNs.bin | Bin 0 -> 1837 bytes .../resources/test/KeyboardLayoutNs.txt | 138 ++++ HidPkg/Crates/HiiKeyboardLayout/src/lib.rs | 775 ++++++++++++++++++ 7 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 HidPkg/Crates/HiiKeyboardLayout/Cargo.toml create mode 100644 HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.bin create mode 100644 HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.txt create mode 100644 HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayoutNs.bin create mode 100644 HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayoutNs.txt create mode 100644 HidPkg/Crates/HiiKeyboardLayout/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 5c2d7ebbb8..a920563c5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ # Add packages that generate binaries here members = [ "HidPkg/Crates/HidIo", + "HidPkg/Crates/HiiKeyboardLayout", "HidPkg/UefiHidDxe", "MsCorePkg/HelloWorldRustDxe" ] @@ -18,4 +19,7 @@ hidparser = {git = "https://github.com/microsoft/mu_rust_hid.git", branch = "mai r-efi = "4.3.0" rustversion = "1.0.14" spin = "0.9.8" -memoffset = "0.9.0" \ No newline at end of file +memoffset = "0.9.0" +scroll = { version = "0.11", default-features = false, features = ["derive"]} +num-traits = { version = "0.2", default-features = false} +num-derive = { version = "0.4", default-features = false} \ No newline at end of file diff --git a/HidPkg/Crates/HiiKeyboardLayout/Cargo.toml b/HidPkg/Crates/HiiKeyboardLayout/Cargo.toml new file mode 100644 index 0000000000..3923550bc3 --- /dev/null +++ b/HidPkg/Crates/HiiKeyboardLayout/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "HiiKeyboardLayout" +version = "0.1.0" +edition = "2021" + + +[lib] +name = "hii_keyboard_layout" +path = "src/lib.rs" + +[dependencies] +r-efi = {workspace=true} +scroll = {workspace=true} +num-traits = {workspace=true} +num-derive = {workspace=true} \ No newline at end of file diff --git a/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.bin b/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.bin new file mode 100644 index 0000000000000000000000000000000000000000..3fd65ce9ec34c907415aa4e8045ceb9065fed53a GIT binary patch literal 1757 zcmY+FS65R(6ovPQi3CJJ>@`-9UPBY5*bzH6K3El zk5d?kB;H0hjX~!lbTc^X+)6i#bIwQU=5XHm7~MQBI3K55z(wayx!+)noj zx1BraUSq_$lI{)eIJeNf#a-tLx_7weoTGb>`_AQbt9an7WSgP8kU~th1$~jkO12eB zT?#STHYjx|#AMr{)TI!U&Ea7l$x5~ZN?i&u*-q5tk*s99pwy)hlWoGuJd(@k%HlD9 zO21?u%cNwBS*9kM49iqRlVzE-QuTOLzz|oe7q!kkbffs-e2Q)yADw&Y#<1qBPbOJ+ z-UD@k6yh(fg1TA?v9l-eDUak`bal|TNCCTfY{2PA;yrYYIOBY%;52RFu$2_6S4U+E z(zVA7IP2Qu3D&i3rF42Phb1Fc$`djL?dLpc!1(~jRDuuEPbXOSI+Nfm{cM8Ob#n<; zO7jU;mn|e%U1mE+r_&s^bHpVawsXYl0!vc7iNlfzxH?@2qzg z;R)$S1 zDNWwYxglOvd8gtU#TR+d-bgAC)Waryc7p+D?Onacq-V9((4_aOXHCj#qd~!^5?oCm n*n!JTVT7HN<^ReuJ8p^p5G(AoET2}hd|FQNi9X4vw0id+tBO!r literal 0 HcmV?d00001 diff --git a/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.txt b/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.txt new file mode 100644 index 0000000000..8e35e0494e --- /dev/null +++ b/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayout.txt @@ -0,0 +1,130 @@ +USB_KEYBOARD_LAYOUT_PACK_BIN mUsbKeyboardLayoutBin = { + sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN), // Binary size + + // + // EFI_HII_PACKAGE_HEADER + // + { + sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN) - sizeof (UINT32), + EFI_HII_PACKAGE_KEYBOARD_LAYOUT + }, + 1, // LayoutCount + sizeof (USB_KEYBOARD_LAYOUT_PACK_BIN) - sizeof (UINT32) - sizeof (EFI_HII_PACKAGE_HEADER) - sizeof (UINT16), // LayoutLength + USB_KEYBOARD_LAYOUT_KEY_GUID, // KeyGuid + sizeof (UINT16) + sizeof (EFI_GUID) + sizeof (UINT32) + sizeof (UINT8) + (USB_KEYBOARD_KEY_COUNT * sizeof (EFI_KEY_DESCRIPTOR)), // LayoutDescriptorStringOffset + USB_KEYBOARD_KEY_COUNT, // DescriptorCount + { + // + // EFI_KEY_DESCRIPTOR (total number is USB_KEYBOARD_KEY_COUNT) + // + { EfiKeyC1, 'a', 'A', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB5, 'b', 'B', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB3, 'c', 'C', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC3, 'd', 'D', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD3, 'e', 'E', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC4, 'f', 'F', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC5, 'g', 'G', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC6, 'h', 'H', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD8, 'i', 'I', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC7, 'j', 'J', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC8, 'k', 'K', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC9, 'l', 'L', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB7, 'm', 'M', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB6, 'n', 'N', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD9, 'o', 'O', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD10, 'p', 'P', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD1, 'q', 'Q', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD4, 'r', 'R', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyC2, 's', 'S', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD5, 't', 'T', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD7, 'u', 'U', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB4, 'v', 'V', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD2, 'w', 'W', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB2, 'x', 'X', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyD6, 'y', 'Y', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyB1, 'z', 'Z', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_CAPS_LOCK }, + { EfiKeyE1, '1', '!', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE2, '2', '@', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE3, '3', '#', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE4, '4', '$', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE5, '5', '%', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE6, '6', '^', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE7, '7', '&', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE8, '8', '*', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE9, '9', '(', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE10, '0', ')', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyEnter, 0x0d, 0x0d, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyEsc, 0x1b, 0x1b, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyBackSpace, 0x08, 0x08, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyTab, 0x09, 0x09, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeySpaceBar, ' ', ' ', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyE11, '-', '_', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE12, '=', '+', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyD11, '[', '{', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyD12, ']', '}', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyD13, '\\', '|', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyC12, '\\', '|', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyC10, ';', ':', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyC11, '\'', '"', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyE0, '`', '~', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyB8, ',', '<', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyB9, '.', '>', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyB10, '/', '?', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyCapsLock, 0x00, 0x00, 0, 0, EFI_CAPS_LOCK_MODIFIER, 0 }, + { EfiKeyF1, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_ONE_MODIFIER, 0 }, + { EfiKeyF2, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TWO_MODIFIER, 0 }, + { EfiKeyF3, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_THREE_MODIFIER, 0 }, + { EfiKeyF4, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_FOUR_MODIFIER, 0 }, + { EfiKeyF5, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_FIVE_MODIFIER, 0 }, + { EfiKeyF6, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_SIX_MODIFIER, 0 }, + { EfiKeyF7, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_SEVEN_MODIFIER, 0 }, + { EfiKeyF8, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_EIGHT_MODIFIER, 0 }, + { EfiKeyF9, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_NINE_MODIFIER, 0 }, + { EfiKeyF10, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TEN_MODIFIER, 0 }, + { EfiKeyF11, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_ELEVEN_MODIFIER, 0 }, + { EfiKeyF12, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TWELVE_MODIFIER, 0 }, + { EfiKeyPrint, 0x00, 0x00, 0, 0, EFI_PRINT_MODIFIER, 0 }, + { EfiKeySLck, 0x00, 0x00, 0, 0, EFI_SCROLL_LOCK_MODIFIER, 0 }, + { EfiKeyPause, 0x00, 0x00, 0, 0, EFI_PAUSE_MODIFIER, 0 }, + { EfiKeyIns, 0x00, 0x00, 0, 0, EFI_INSERT_MODIFIER, 0 }, + { EfiKeyHome, 0x00, 0x00, 0, 0, EFI_HOME_MODIFIER, 0 }, + { EfiKeyPgUp, 0x00, 0x00, 0, 0, EFI_PAGE_UP_MODIFIER, 0 }, + { EfiKeyDel, 0x00, 0x00, 0, 0, EFI_DELETE_MODIFIER, 0 }, + { EfiKeyEnd, 0x00, 0x00, 0, 0, EFI_END_MODIFIER, 0 }, + { EfiKeyPgDn, 0x00, 0x00, 0, 0, EFI_PAGE_DOWN_MODIFIER, 0 }, + { EfiKeyRightArrow, 0x00, 0x00, 0, 0, EFI_RIGHT_ARROW_MODIFIER, 0 }, + { EfiKeyLeftArrow, 0x00, 0x00, 0, 0, EFI_LEFT_ARROW_MODIFIER, 0 }, + { EfiKeyDownArrow, 0x00, 0x00, 0, 0, EFI_DOWN_ARROW_MODIFIER, 0 }, + { EfiKeyUpArrow, 0x00, 0x00, 0, 0, EFI_UP_ARROW_MODIFIER, 0 }, + { EfiKeyNLck, 0x00, 0x00, 0, 0, EFI_NUM_LOCK_MODIFIER, 0 }, + { EfiKeySlash, '/', '/', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyAsterisk, '*', '*', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyMinus, '-', '-', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyPlus, '+', '+', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyEnter, 0x0d, 0x0d, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyOne, '1', '1', 0, 0, EFI_END_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyTwo, '2', '2', 0, 0, EFI_DOWN_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyThree, '3', '3', 0, 0, EFI_PAGE_DOWN_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyFour, '4', '4', 0, 0, EFI_LEFT_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyFive, '5', '5', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeySix, '6', '6', 0, 0, EFI_RIGHT_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeySeven, '7', '7', 0, 0, EFI_HOME_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyEight, '8', '8', 0, 0, EFI_UP_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyNine, '9', '9', 0, 0, EFI_PAGE_UP_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyZero, '0', '0', 0, 0, EFI_INSERT_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyPeriod, '.', '.', 0, 0, EFI_DELETE_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyA4, 0x00, 0x00, 0, 0, EFI_MENU_MODIFIER, 0 }, + { EfiKeyLCtrl, 0, 0, 0, 0, EFI_LEFT_CONTROL_MODIFIER, 0 }, + { EfiKeyLShift, 0, 0, 0, 0, EFI_LEFT_SHIFT_MODIFIER, 0 }, + { EfiKeyLAlt, 0, 0, 0, 0, EFI_LEFT_ALT_MODIFIER, 0 }, + { EfiKeyA0, 0, 0, 0, 0, EFI_LEFT_LOGO_MODIFIER, 0 }, + { EfiKeyRCtrl, 0, 0, 0, 0, EFI_RIGHT_CONTROL_MODIFIER, 0 }, + { EfiKeyRShift, 0, 0, 0, 0, EFI_RIGHT_SHIFT_MODIFIER, 0 }, + { EfiKeyA2, 0, 0, 0, 0, EFI_RIGHT_ALT_MODIFIER, 0 }, + { EfiKeyA3, 0, 0, 0, 0, EFI_RIGHT_LOGO_MODIFIER, 0 }, + }, + 1, // DescriptionCount + { 'e', 'n', '-', 'U', 'S' }, // RFC4646 language code + ' ', // Space + { 'E', 'n', 'g', 'l', 'i', 's', 'h', ' ', 'K', 'e', 'y', 'b', 'o', 'a', 'r', 'd', '\0' }, // DescriptionString[] +}; \ No newline at end of file diff --git a/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayoutNs.bin b/HidPkg/Crates/HiiKeyboardLayout/resources/test/KeyboardLayoutNs.bin new file mode 100644 index 0000000000000000000000000000000000000000..9caecfdcbb53b97d11cee635dcf13ea8ea7db276 GIT binary patch literal 1837 zcmY+FxmQy`7{%|10Rd4EabJR45M&Jr1VmZf5qI1GH9!CrpE1QCG;S1-tjcylRhOKvY&RMUNLFPtP}L>pE8B+s z1teF|RrzD#lzz!pjv!Tb6-Q7TO*uzU2~C_MNITVxn?-bfr~1%j+)Foxm&OO_Ch*F* zk8T`G#`XkSFnGx>g)E-dS{7)^LVoh?V^@nWA*<@c>+P?THBM+F|E) zdJAW$v{<`#OQxu8T(beVZs(kg@DBQ^2)vY=o8CxdQQUUeia#G=+g{o1IdOdW$&aLHC z=eKeMsq-7HpWr*cnF$ZlJDV)u>ZSktNqf-FAyp#g_n-rMs_1@V&>kdxDs}HMW((-# zkgCc0rW8j(F=h=&b4WFOeSm8hhg3lxb8YcgRlQ?= zkNk@~sMn%$1Z6l#pIs4vtDXgVPmrGd+QT5dSD6h`tyTso`dEY;=p7T7XB9@7oH+ld c@=V-3|8W+Wv^bxBIX-', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyB10, '/', '?', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT }, + { EfiKeyCapsLock, 0x00, 0x00, 0, 0, EFI_CAPS_LOCK_MODIFIER, 0 }, + { EfiKeyF1, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_ONE_MODIFIER, 0 }, + { EfiKeyF2, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TWO_MODIFIER, 0 }, + { EfiKeyF3, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_THREE_MODIFIER, 0 }, + { EfiKeyF4, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_FOUR_MODIFIER, 0 }, + { EfiKeyF5, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_FIVE_MODIFIER, 0 }, + { EfiKeyF6, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_SIX_MODIFIER, 0 }, + { EfiKeyF7, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_SEVEN_MODIFIER, 0 }, + { EfiKeyF8, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_EIGHT_MODIFIER, 0 }, + { EfiKeyF9, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_NINE_MODIFIER, 0 }, + { EfiKeyF10, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TEN_MODIFIER, 0 }, + { EfiKeyF11, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_ELEVEN_MODIFIER, 0 }, + { EfiKeyF12, 0x00, 0x00, 0, 0, EFI_FUNCTION_KEY_TWELVE_MODIFIER, 0 }, + { EfiKeyPrint, 0x00, 0x00, 0, 0, EFI_PRINT_MODIFIER, 0 }, + { EfiKeySLck, 0x00, 0x00, 0, 0, EFI_SCROLL_LOCK_MODIFIER, 0 }, + { EfiKeyPause, 0x00, 0x00, 0, 0, EFI_PAUSE_MODIFIER, 0 }, + { EfiKeyIns, 0x00, 0x00, 0, 0, EFI_INSERT_MODIFIER, 0 }, + { EfiKeyHome, 0x00, 0x00, 0, 0, EFI_HOME_MODIFIER, 0 }, + { EfiKeyPgUp, 0x00, 0x00, 0, 0, EFI_PAGE_UP_MODIFIER, 0 }, + { EfiKeyDel, 0x00, 0x00, 0, 0, EFI_DELETE_MODIFIER, 0 }, + { EfiKeyEnd, 0x00, 0x00, 0, 0, EFI_END_MODIFIER, 0 }, + { EfiKeyPgDn, 0x00, 0x00, 0, 0, EFI_PAGE_DOWN_MODIFIER, 0 }, + { EfiKeyRightArrow, 0x00, 0x00, 0, 0, EFI_RIGHT_ARROW_MODIFIER, 0 }, + { EfiKeyLeftArrow, 0x00, 0x00, 0, 0, EFI_LEFT_ARROW_MODIFIER, 0 }, + { EfiKeyDownArrow, 0x00, 0x00, 0, 0, EFI_DOWN_ARROW_MODIFIER, 0 }, + { EfiKeyUpArrow, 0x00, 0x00, 0, 0, EFI_UP_ARROW_MODIFIER, 0 }, + { EfiKeyNLck, 0x00, 0x00, 0, 0, EFI_NUM_LOCK_MODIFIER, 0 }, + { EfiKeySlash, '/', '/', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyAsterisk, '*', '*', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyMinus, '-', '-', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyPlus, '+', '+', 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyEnter, 0x0d, 0x0d, 0, 0, EFI_NULL_MODIFIER, 0 }, + { EfiKeyOne, '1', '1', 0, 0, EFI_END_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyTwo, '2', '2', 0, 0, EFI_DOWN_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyThree, '3', '3', 0, 0, EFI_PAGE_DOWN_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyFour, '4', '4', 0, 0, EFI_LEFT_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyFive, '5', '5', 0, 0, EFI_NULL_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeySix, '6', '6', 0, 0, EFI_RIGHT_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeySeven, '7', '7', 0, 0, EFI_HOME_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyEight, '8', '8', 0, 0, EFI_UP_ARROW_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyNine, '9', '9', 0, 0, EFI_PAGE_UP_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyZero, '0', '0', 0, 0, EFI_INSERT_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyPeriod, '.', '.', 0, 0, EFI_DELETE_MODIFIER, EFI_AFFECTED_BY_STANDARD_SHIFT | EFI_AFFECTED_BY_NUM_LOCK }, + { EfiKeyA4, 0x00, 0x00, 0, 0, EFI_MENU_MODIFIER, 0 }, + { EfiKeyLCtrl, 0, 0, 0, 0, EFI_LEFT_CONTROL_MODIFIER, 0 }, + { EfiKeyLShift, 0, 0, 0, 0, EFI_LEFT_SHIFT_MODIFIER, 0 }, + { EfiKeyLAlt, 0, 0, 0, 0, EFI_LEFT_ALT_MODIFIER, 0 }, + { EfiKeyA0, 0, 0, 0, 0, EFI_LEFT_LOGO_MODIFIER, 0 }, + { EfiKeyRCtrl, 0, 0, 0, 0, EFI_RIGHT_CONTROL_MODIFIER, 0 }, + { EfiKeyRShift, 0, 0, 0, 0, EFI_RIGHT_SHIFT_MODIFIER, 0 }, + { EfiKeyA2, 0, 0, 0, 0, EFI_RIGHT_ALT_MODIFIER, 0 }, + { EfiKeyA3, 0, 0, 0, 0, EFI_RIGHT_LOGO_MODIFIER, 0 }, + }, + 1, // DescriptionCount + { 'e', 'n', '-', 'U', 'S' }, // RFC4646 language code + ' ', // Space + { 'E', 'n', 'g', 'l', 'i', 's', 'h', ' ', 'K', 'e', 'y', 'b', 'o', 'a', 'r', 'd', '\0' }, // DescriptionString[] +}; \ No newline at end of file diff --git a/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs b/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs new file mode 100644 index 0000000000..a3a1669362 --- /dev/null +++ b/HidPkg/Crates/HiiKeyboardLayout/src/lib.rs @@ -0,0 +1,775 @@ +//! HiiKeyboardLayout - provides support for UEFI HII Keyboard Layout structures +//! +//! This crate provides a rust wrapper around UEFI HII Keyboard Layout structures. The relevant structures defined in +//! the UEFI spec are not well-suited for direct definition in rust; so this crate defines analogous rust structures and +//! provides serialization/deserialization support for converting the rust structures into byte buffers and vice versa. +//! +//! ## Examples and Usage +//! +//! Retrieving a default (en-US) layout, writing it to a buffer, and then reading the buffer back into a rust structure: +//!``` +//! use hii_keyboard_layout::{get_default_keyboard_pkg, HiiKeyboardPkg}; +//! use scroll::{Pread, Pwrite}; +//! let mut buffer = [0u8; 4096]; +//! +//! let package = get_default_keyboard_pkg(); +//! buffer.pwrite(&package, 0).unwrap(); +//! +//! let package2: HiiKeyboardPkg = buffer.pread(0).unwrap(); +//! assert_eq!(package, package2); +//!``` +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. All rights reserved. +//! SPDX-License-Identifier: BSD-2-Clause-Patent +//! + +#![no_std] +extern crate alloc; + +use alloc::{format, string::String, vec, vec::Vec}; +use core::mem; + +#[macro_use] +extern crate num_derive; + +use num_traits::FromPrimitive; +use r_efi::{ + efi, + hii::{self, PACKAGE_END}, + protocols::hii_database::*, +}; +use scroll::{ctx, Pread, Pwrite}; + +/// HII Keyboard Package List +/// Refer to UEFI spec version 2.10 section 33.3.1.2 which defines the generic header structure. This implementation +/// only supports HII Keyboard Packages; other HII package types (or mixes) are not supported. +#[derive(Debug, PartialEq, Eq)] +pub struct HiiKeyboardPkgList { + /// The GUID associated with this package list. + pub package_list_guid: efi::Guid, + /// The HiiKeyboardPkg contained in this package list. + pub package: HiiKeyboardPkg, +} + +/// HII Keyboard Package +/// Refer to UEFI spec version 2.10 section 33.3.9 which defines the keyboard package structure. +#[derive(Debug, PartialEq, Eq)] +pub struct HiiKeyboardPkg { + /// The list of keyboard layouts in this package. + pub layouts: Vec, +} + +/// HII Keyboard Layout +/// Refer to UEFI spec version 2.10 section 34.8.10 which defines the keyboard layout structure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HiiKeyboardLayout { + /// The unique ID associated with this keyboard layout. + pub guid: efi::Guid, + /// A list of key descriptors + pub keys: Vec, + /// A list of descriptions for this keyboard layout. + pub descriptions: Vec, +} + +/// HII Key descriptor +/// Refer to UEFI spec version 2.10 section 34.10.10 which defines the key descriptor structure. +#[derive(Debug, Pread, Pwrite, PartialEq, Eq, Clone, Copy)] +#[repr(C)] +pub struct HiiKeyDescriptor { + /// Describes the physical key on the keyboard. + pub key: EfiKey, + /// Unicode character for the key (note: UEFI only supports UCS-2 encoding). + pub unicode: u16, + /// Unicode character for the key with the shift key being held down. + pub shifted_unicode: u16, + /// Unicode character for the key with the Alt-GR being held down. + pub alt_gr_unicode: u16, + /// Unicode character for the key with the Alt-GR and shift keys being held down. + pub shifted_alt_gr_unicode: u16, + /// Modifier keys are defined to allow for special functionality that is not necessarily accomplished by a printable + /// character. Many of these modifier keys are flags to toggle certain state bits on and off inside of a keyboard + /// driver. See [`r_efi::protocols::hii_database`] for modifier definitions. + pub modifier: u16, + /// Indicates what modifiers affect this key. See [`r_efi::protocols::hii_database`] for "affected by" definitions. + pub affected_attribute: u16, +} + +/// Non-Spacing HII Key Descriptor variant. Used for "non-spacing" keys. +/// Refer to discussion in UEFI spec version 2.10 section 33.2.4.3 for information on "non-spacing" keys and how they +/// are used. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct HiiNsKeyDescriptor { + /// The descriptor for the "non-spacing key" itself. + pub descriptor: HiiKeyDescriptor, + /// The list of descriptors that are active if the "non-spacing" key has been pressed. + pub dependent_keys: Vec, +} + +/// HII Key descriptor enumeration. +/// HII spec allows for two types of key descriptors - normal and "non-spacing". +/// Refer to UEFI spec version 2.10 section 33.2.4.3 +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum HiiKey { + Key(HiiKeyDescriptor), + NsKey(HiiNsKeyDescriptor), +} + +/// Enumeration of physical keys. +/// Refer to UEFI spec version 2.10 section 34.8.10 and section 33.2.4.1. +#[rustfmt::skip] +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)] +//Note: UEFI specifies this as an C enum. That means the size is a bit ambiguous; but most compilers +//will make it 32-bit, so that's what this implementation assumes. +#[repr(u32)] +pub enum EfiKey { + LCtrl, A0, LAlt, SpaceBar, A2, A3, A4, RCtrl, LeftArrow, DownArrow, RightArrow, Zero, Period, Enter, LShift, B0, B1, + B2, B3, B4, B5, B6, B7, B8, B9, B10, RShift, UpArrow, One, Two, Three, CapsLock, C1, C2, C3, C4, C5, C6, C7, C8, C9, + C10, C11, C12, Four, Five, Six, Plus, Tab, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, Del, End, PgDn, + Seven, Eight, Nine, E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, BackSpace, Ins, Home, PgUp, NLck, Slash, + Asterisk, Minus, Esc, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Print, SLck, Pause, Intl0, Intl1, Intl2, + Intl3, Intl4, Intl5, Intl6, Intl7, Intl8, Intl9, +} + +impl TryFrom for EfiKey { + type Error = &'static str; + fn try_from(value: u32) -> Result { + ::from_u32(value).ok_or("Invalid EfiKey enum value") + } +} + +/// Description for a keyboard layout. +/// Refer to UEFI spec version 2.10 section 34.8.10 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HiiKeyboardDescription { + /// The language code for the description (e.g. "en-US") + pub language: String, + /// The description (e.g. "English Keyboard") + pub description: String, +} + +impl ctx::TryFromCtx<'_> for HiiKeyboardPkgList { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + //Note: This is not a general purpose HII package list reader: it only supports a package list with a single + //keyboard layout package in it. + let offset = &mut 0; + //EFI_HII_PACAKGE_LIST_HEADER::PackageListGuid + let guid = efi::Guid::from_fields( + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), + ); + //EFI_HII_PACKAGE_LIST_HEADER::PackageLength + let _package_length: u32 = src.gread(offset)?; + + //Read HiiKeyboard Pkg + let hii_keyboard_pkg: HiiKeyboardPkg = src.gread(offset)?; + + //Read EFI_HHI_PACAKGE_END package + let _pkg_end_length_type: u32 = src.gread(offset)?; + + Ok((HiiKeyboardPkgList { package_list_guid: guid, package: hii_keyboard_pkg }, *offset)) + } +} + +impl ctx::TryIntoCtx for &HiiKeyboardPkgList { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_PACKAGE_LIST_HEADER::PackageListGuid + dest.gwrite(&self.package_list_guid.as_bytes()[..], offset)?; + + //EFI_HII_PACKAGE_LIST_HEADER::PackageLength will be updated at the end. + let mut package_length_offset = *offset; + *offset += 4; + + //Write HiiKeyboardPkg + dest.gwrite(&self.package, offset)?; + + //EFI_HII_PACKAGE_END + let length_type: u32 = 4 | ((PACKAGE_END as u32) << 24); + dest.gwrite(length_type, offset)?; + + //go back and update EFI_HII_PACKAGE_LIST_HEADER::PackageLength + dest.gwrite(*offset as u32, &mut package_length_offset)?; + + Ok(*offset) + } +} + +impl ctx::TryFromCtx<'_> for HiiKeyboardPkg { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //EFI_HII_KEYBOARD_PACKAGE_HDR::Header (bitfield as single u32) + let length_type: u32 = src.gread(offset)?; + let pkg_type = (length_type >> 24) as u8; + if pkg_type != hii::PACKAGE_KEYBOARD_LAYOUT { + return Err(scroll::Error::BadInput { size: 0, msg: "Unsupported Pkg Type" }); + } + //EFI_HII_KEYBOARD_PACKAGE_HDR::LayoutCount + let layout_count: u16 = src.gread(offset)?; + + //EFI_HII_KEYBOARD_PACKAGE_HDR::Layout[] array into vector. + let mut layouts = vec![]; + for _ in 0..layout_count { + layouts.push(src.gread_with(offset, ctx)?); + } + + Ok((HiiKeyboardPkg { layouts }, *offset)) + } +} + +impl ctx::TryIntoCtx for &HiiKeyboardPkg { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_KEYBOARD_PKG_HDR::Header::Length will be updated at the end. + *offset += 4; + //EFI_HII_KEYBOARD_PKG_HDR::LayoutCount + dest.gwrite(self.layouts.len() as u16, offset)?; + //EFI_HII_KEYBOARD_PKG_HDR::Layout[] + for layout in &self.layouts { + dest.gwrite(layout, offset)?; + } + //update EFI_HII_KEYBOARD_PKG_HEADER at offset zero. + let length = *offset; + let length_type: u32 = (hii::PACKAGE_KEYBOARD_LAYOUT as u32) << 24; + let length_type = length_type | (length & 0xFFFFFF) as u32; + dest.gwrite(length_type, &mut 0)?; + + Ok(*offset) + } +} + +impl ctx::TryFromCtx<'_> for HiiKeyboardLayout { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //EFI_HII_KEYBOARD_LAYOUT::LayoutLength + let _layout_length: u16 = src.gread(offset)?; + //EFI_HII_KEYBOARD_LAYOUT::Guid + let guid = efi::Guid::from_fields( + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread(offset)?, + src.gread_with::<&[u8]>(offset, 6)?.try_into().unwrap(), + ); + //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset + let layout_descriptor_string_offset: u32 = src.gread(offset)?; + //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount + let _descriptor_count: u8 = src.gread(offset)?; + + //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] array into vector. Note: descriptor_count is not used since ns_keys + //may consume multiple descriptors which are included in the count, resulting in a vector of "real" descriptors that + //is smaller than the descriptor_count. + let mut descriptors = vec![]; + while *offset < layout_descriptor_string_offset as usize { + descriptors.push(src.gread(offset)?); + } + + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount + let description_count: u16 = src.gread(offset)?; + let mut descriptions = vec![]; + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] + for _ in 0..description_count { + descriptions.push(src.gread(offset)?); + } + + Ok((HiiKeyboardLayout { guid, keys: descriptors, descriptions }, *offset)) + } +} + +impl ctx::TryIntoCtx for &HiiKeyboardLayout { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //EFI_HII_KEYBOARD_LAYOUT::LayoutLength will be updated at the end. + *offset += 2; + //EFI_HII_KEYBOARD_LAYOUT::Guid + dest.gwrite(&self.guid.as_bytes()[..], offset)?; + //EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset will be updated after writing out the descriptors. + let mut descriptor_string_offset = *offset; + *offset += 4; + + //EFI_HII_KEYBOARD_LAYOUT::DescriptorCount will be updated after writing out the descriptors. + let mut descriptor_count_offset = *offset; + *offset += 1; + + let descriptor_start = *offset; + //EFI_HII_KEYBOARD_LAYOUT::Descriptors[] + for descriptor in &self.keys { + //Note: may expand to more than one descriptor due to non-spacing keys. + dest.gwrite(descriptor, offset)?; + } + + //Go back and update EFI_HII_KEYBOARD_LAYOUT::DescriptorCount + let descriptor_count = (*offset - descriptor_start) / mem::size_of::(); + dest.gwrite(descriptor_count as u8, &mut descriptor_count_offset)?; + + //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutDescriptorStringOffset. + dest.gwrite(*offset as u32, &mut descriptor_string_offset)?; + + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionCount + dest.gwrite(self.descriptions.len() as u16, offset)?; + + //EFI_DESCRIPTION_STRING_BUNDLE::DescriptionString[] + for description in &self.descriptions { + dest.gwrite(description, offset)?; + } + + //Go back and update EFI_HII_KEYBOARD_LAYOUT::LayoutLength + dest.gwrite(*offset as u16, &mut 0)?; + + Ok(*offset) + } +} + +impl ctx::TryFromCtx<'_> for HiiKey { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + let descriptor: HiiKeyDescriptor = src.gread(offset)?; + if descriptor.modifier == NS_KEY_MODIFIER { + //For Non-Spacing keys, consume descriptors until we find one without EFI_NS_KEY_DEPENDENCY_MODIFIER or run out. + //Refer to UEFI spec 2.10 section 33.2.4.3 for details. + let mut dependent_keys = vec![]; + while let Ok(dependent_key) = src.pread::(*offset) { + if dependent_key.modifier == NS_KEY_DEPENDENCY_MODIFIER { + //found a dependent descriptor. Re-read it with gread to update offset. + dependent_keys.push(src.gread(offset)?); + } else { + //found a descriptor without EFI_NS_KEY_DEPENDENCY_MODIFIER + break; + } + } + Ok((HiiKey::NsKey(HiiNsKeyDescriptor { descriptor, dependent_keys }), *offset)) + } else { + Ok((HiiKey::Key(descriptor), *offset)) + } + } +} + +impl ctx::TryIntoCtx for &HiiKey { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + match self { + HiiKey::Key(descriptor) => { + dest.gwrite(descriptor, offset)?; + } + HiiKey::NsKey(ns_descriptor) => { + dest.gwrite(&ns_descriptor.descriptor, offset)?; + for descriptor in &ns_descriptor.dependent_keys { + dest.gwrite(descriptor, offset)?; + } + } + } + Ok(*offset) + } +} + +impl ctx::TryFromCtx<'_> for HiiKeyboardDescription { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + //consume u16 characters until NULL. + let mut desc_chars = vec![]; + loop { + let desc_char: u16 = src.gread(offset)?; + if desc_char == 0 { + break; + } + desc_chars.push(desc_char); + } + //convert to string. Note: UEFI spec uses UCS-2 encoding, so all valid inputs should translate to UTF-16 without + //error. + let desc_string = String::from_utf16(&desc_chars) + .map_err(|_| scroll::Error::BadInput { size: 0, msg: "Invalid string in keyboard description." })?; + + //split the resulting string on the first space - this gives us language and description. + if let Some((lang, desc)) = desc_string.split_once(' ') { + Ok((HiiKeyboardDescription { language: String::from(lang), description: String::from(desc) }, *offset)) + } else { + Err(scroll::Error::BadInput { size: 0, msg: "No space in keyboard description." }) + } + } +} + +impl ctx::TryIntoCtx for &HiiKeyboardDescription { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: ()) -> Result { + let offset = &mut 0; + //Format as EFI_DESCRIPTION_STRING per UEFI spec 2.10 section 34.8.10. + let desc_string = format!("{} {}", self.language, self.description); + let mut characters: Vec = desc_string.encode_utf16().collect(); + characters.push(0); + for character in characters { + dest.gwrite(character, offset)?; + } + Ok(*offset) + } +} + +impl ctx::TryFromCtx<'_, scroll::Endian> for EfiKey { + type Error = scroll::Error; + fn try_from_ctx(src: &'_ [u8], _ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> { + let offset = &mut 0; + let efi_key = + EfiKey::try_from(src.gread::(offset)?).map_err(|err| scroll::Error::BadInput { size: 0, msg: err })?; + Ok((efi_key, *offset)) + } +} + +impl ctx::TryIntoCtx for &EfiKey { + type Error = scroll::Error; + fn try_into_ctx(self, dest: &mut [u8], _ctx: scroll::Endian) -> Result { + let offset = &mut 0; + dest.gwrite(*self as u32, offset)?; + Ok(*offset) + } +} + +// Convenience macro for defining HiiKey::Key structures. +macro_rules! key { + ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { + HiiKey::Key(key_descriptor!($key, $unicode, $shifted, $alt_gr, $shifted_alt_gr, $modifier, $affected)) + }; +} + +// convenience macro for defining HiiKeyDescriptor structures. +// note: for unicode characters, these are encoded as u16 for compliance with UEFI spec. UEFI only supports UCS-2 +// encoding - so unicode characters that require more than two bytes under UTF-16 are not supported (and will panic). +macro_rules! key_descriptor { + ($key:expr, $unicode:literal, $shifted:literal, $alt_gr:literal, $shifted_alt_gr:literal, $modifier:expr, $affected:expr ) => { + HiiKeyDescriptor { + key: $key, + unicode: $unicode.encode_utf16(&mut [0u16; 1])[0], + shifted_unicode: $shifted.encode_utf16(&mut [0u16; 1])[0], + alt_gr_unicode: $alt_gr.encode_utf16(&mut [0u16; 1])[0], + shifted_alt_gr_unicode: $shifted_alt_gr.encode_utf16(&mut [0u16; 1])[0], + modifier: $modifier, + affected_attribute: $affected, + } + }; +} + +/// Returns a default HiiKeyboardLayout (which is a standard US-104 layout) +#[rustfmt::skip] +pub fn get_default_keyboard_layout() -> HiiKeyboardLayout { + HiiKeyboardLayout { + guid: efi::Guid::from_fields(0x3a4d7a7c, 0x18a, 0x4b42, 0x81, 0xb3, &[0xdc, 0x10, 0xe3, 0xb5, 0x91, 0xbd]), + keys: vec![ + key!(EfiKey::C1, 'a', 'A', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B5, 'b', 'B', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B3, 'c', 'C', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C3, 'd', 'D', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D3, 'e', 'E', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C4, 'f', 'F', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C5, 'g', 'G', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C6, 'h', 'H', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D8, 'i', 'I', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C7, 'j', 'J', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C8, 'k', 'K', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C9, 'l', 'L', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B7, 'm', 'M', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B6, 'n', 'N', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D9, 'o', 'O', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D10, 'p', 'P', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D1, 'q', 'Q', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D4, 'r', 'R', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::C2, 's', 'S', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D5, 't', 'T', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D7, 'u', 'U', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B4, 'v', 'V', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D2, 'w', 'W', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B2, 'x', 'X', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::D6, 'y', 'Y', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::B1, 'z', 'Z', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key!(EfiKey::E1, '1', '!', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E2, '2', '@', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E3, '3', '#', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E4, '4', '$', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E5, '5', '%', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E6, '6', '^', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E7, '7', '&', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E8, '8', '*', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E9, '9', '(', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E10, '0', ')', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::Enter, '\x0d', '\x0d', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Esc, '\x1b', '\x1b', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::BackSpace, '\x08', '\x08', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Tab, '\x09', '\x09', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::SpaceBar, ' ', ' ', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::E11, '-', '_', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E12, '=', '+', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::D11, '[', '{', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::D12, ']', '}', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::D13, '\\', '|', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::C12, '\\', '|', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::C10, ';', ':', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::C11, '\'', '"', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::E0, '`', '~', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::B8, ',', '<', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::B9, '.', '>', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::B10, '/', '?', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT ), + key!(EfiKey::CapsLock, '\0', '\0', '\0', '\0', CAPS_LOCK_MODIFIER, 0 ), + key!(EfiKey::F1, '\0', '\0', '\0', '\0', FUNCTION_KEY_ONE_MODIFIER, 0 ), + key!(EfiKey::F2, '\0', '\0', '\0', '\0', FUNCTION_KEY_TWO_MODIFIER, 0 ), + key!(EfiKey::F3, '\0', '\0', '\0', '\0', FUNCTION_KEY_THREE_MODIFIER, 0 ), + key!(EfiKey::F4, '\0', '\0', '\0', '\0', FUNCTION_KEY_FOUR_MODIFIER, 0 ), + key!(EfiKey::F5, '\0', '\0', '\0', '\0', FUNCTION_KEY_FIVE_MODIFIER, 0 ), + key!(EfiKey::F6, '\0', '\0', '\0', '\0', FUNCTION_KEY_SIX_MODIFIER, 0 ), + key!(EfiKey::F7, '\0', '\0', '\0', '\0', FUNCTION_KEY_SEVEN_MODIFIER, 0 ), + key!(EfiKey::F8, '\0', '\0', '\0', '\0', FUNCTION_KEY_EIGHT_MODIFIER, 0 ), + key!(EfiKey::F9, '\0', '\0', '\0', '\0', FUNCTION_KEY_NINE_MODIFIER, 0 ), + key!(EfiKey::F10, '\0', '\0', '\0', '\0', FUNCTION_KEY_TEN_MODIFIER, 0 ), + key!(EfiKey::F11, '\0', '\0', '\0', '\0', FUNCTION_KEY_ELEVEN_MODIFIER, 0 ), + key!(EfiKey::F12, '\0', '\0', '\0', '\0', FUNCTION_KEY_TWELVE_MODIFIER, 0 ), + key!(EfiKey::Print, '\0', '\0', '\0', '\0', PRINT_MODIFIER, 0 ), + key!(EfiKey::SLck, '\0', '\0', '\0', '\0', SCROLL_LOCK_MODIFIER, 0 ), + key!(EfiKey::Pause, '\0', '\0', '\0', '\0', PAUSE_MODIFIER, 0 ), + key!(EfiKey::Ins, '\0', '\0', '\0', '\0', INSERT_MODIFIER, 0 ), + key!(EfiKey::Home, '\0', '\0', '\0', '\0', HOME_MODIFIER, 0 ), + key!(EfiKey::PgUp, '\0', '\0', '\0', '\0', PAGE_UP_MODIFIER, 0 ), + key!(EfiKey::Del, '\0', '\0', '\0', '\0', DELETE_MODIFIER, 0 ), + key!(EfiKey::End, '\0', '\0', '\0', '\0', END_MODIFIER, 0 ), + key!(EfiKey::PgDn, '\0', '\0', '\0', '\0', PAGE_DOWN_MODIFIER, 0 ), + key!(EfiKey::RightArrow, '\0', '\0', '\0', '\0', RIGHT_ARROW_MODIFIER, 0 ), + key!(EfiKey::LeftArrow, '\0', '\0', '\0', '\0', LEFT_ARROW_MODIFIER, 0 ), + key!(EfiKey::DownArrow, '\0', '\0', '\0', '\0', DOWN_ARROW_MODIFIER, 0 ), + key!(EfiKey::UpArrow, '\0', '\0', '\0', '\0', UP_ARROW_MODIFIER, 0 ), + key!(EfiKey::NLck, '\0', '\0', '\0', '\0', NUM_LOCK_MODIFIER, 0 ), + key!(EfiKey::Slash, '/', '/', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Asterisk, '*', '*', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Minus, '-', '-', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Plus, '+', '+', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::Enter, '\x0d', '\x0d', '\0', '\0', NULL_MODIFIER, 0 ), + key!(EfiKey::One, '1', '1', '\0', '\0', END_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Two, '2', '2', '\0', '\0', DOWN_ARROW_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Three, '3', '3', '\0', '\0', PAGE_DOWN_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Four, '4', '4', '\0', '\0', LEFT_ARROW_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Five, '5', '5', '\0', '\0', NULL_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Six, '6', '6', '\0', '\0', RIGHT_ARROW_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Seven, '7', '7', '\0', '\0', HOME_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Eight, '8', '8', '\0', '\0', UP_ARROW_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Nine, '9', '9', '\0', '\0', PAGE_UP_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Zero, '0', '0', '\0', '\0', INSERT_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::Period, '.', '.', '\0', '\0', DELETE_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_NUM_LOCK ), + key!(EfiKey::A4, '\0', '\0', '\0', '\0', MENU_MODIFIER, 0 ), + key!(EfiKey::LCtrl, '\0', '\0', '\0', '\0', LEFT_CONTROL_MODIFIER, 0 ), + key!(EfiKey::LShift, '\0', '\0', '\0', '\0', LEFT_SHIFT_MODIFIER, 0 ), + key!(EfiKey::LAlt, '\0', '\0', '\0', '\0', LEFT_ALT_MODIFIER, 0 ), + key!(EfiKey::A0, '\0', '\0', '\0', '\0', LEFT_LOGO_MODIFIER, 0 ), + key!(EfiKey::RCtrl, '\0', '\0', '\0', '\0', RIGHT_CONTROL_MODIFIER, 0 ), + key!(EfiKey::RShift, '\0', '\0', '\0', '\0', RIGHT_SHIFT_MODIFIER, 0 ), + key!(EfiKey::A2, '\0', '\0', '\0', '\0', RIGHT_ALT_MODIFIER, 0 ), + key!(EfiKey::A3, '\0', '\0', '\0', '\0', RIGHT_LOGO_MODIFIER, 0 ), + ], + descriptions: vec![ + HiiKeyboardDescription { + language: String::from("en-US"), + description: String::from("English Keyboard") + } + ] + } +} + +/// returns a default keyboard layout package +pub fn get_default_keyboard_pkg() -> HiiKeyboardPkg { + HiiKeyboardPkg { layouts: vec![get_default_keyboard_layout()] } +} + +/// Returns a default keyboard layout package list. +pub fn get_default_keyboard_pkg_list() -> HiiKeyboardPkgList { + HiiKeyboardPkgList { + package_list_guid: efi::Guid::from_fields( + 0xc0f3b43, + 0x44de, + 0x4907, + 0xb4, + 0x78, + &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], + ), + package: get_default_keyboard_pkg(), + } +} + +/// Returns a default keyboard layout package list as a byte vector. +/// +/// This is suitable for use with [`r_efi::protocols::hii_database::ProtocolNewPackageList`]. +/// +/// Example: +/// ```ignore +/// let mut hii_handle: hii::Handle = core::ptr::null_mut(); +/// let status = (hii_database_protocol.new_package_list)( +/// hii_database_protocol_ptr, +/// hii_keyboard_layout::get_default_keyboard_pkg_list_buffer().as_ptr() as *const hii::PackageListHeader, +/// core::ptr::null_mut(), +/// core::ptr::addr_of_mut!(hii_handle), +/// ); +/// ``` +pub fn get_default_keyboard_pkg_list_buffer() -> Vec { + let mut buffer = vec![0u8; 4096]; + + let result = buffer.pwrite(&get_default_keyboard_pkg_list(), 0); + if let Ok(buffer_size) = result { + buffer.resize(buffer_size, 0); + buffer + } else { + panic!("Unexpected error serializing HII Keyboard Package List: {:?}", result); + } +} + +/// Defines errors that can occur while parsing keyboard layout. +pub enum LayoutError { + /// Malformed key buffer + ParseError(scroll::Error), +} + +/// Returns a HiiKeyboardLayout structure parsed from the given buffer. +pub fn keyboard_layout_from_buffer(buffer: &[u8]) -> Result { + buffer.pread::(0).map_err(LayoutError::ParseError) +} + +#[cfg(test)] +mod tests { + extern crate std; + use core::mem; + use std::{fs::File, io::Read}; + + use alloc::{vec, vec::Vec}; + use r_efi::{ + efi, + protocols::hii_database::{ + AFFECTED_BY_CAPS_LOCK, AFFECTED_BY_STANDARD_SHIFT, NS_KEY_DEPENDENCY_MODIFIER, NS_KEY_MODIFIER, + }, + }; + use scroll::{Pread, Pwrite}; + + use crate::{ + get_default_keyboard_pkg, EfiKey, HiiKey, HiiKeyDescriptor, HiiKeyboardPkg, HiiKeyboardPkgList, HiiNsKeyDescriptor, + }; + + macro_rules! test_collateral { + ($fname:expr) => { + concat!(env!("CARGO_MANIFEST_DIR"), "/resources/test/", $fname) + }; + } + + #[test] + fn hii_keyboard_package_serialize_deserialize_should_produce_consistent_results() { + let mut buffer = [0u8; 4096]; + + let package = get_default_keyboard_pkg(); + buffer.pwrite(&package, 0).unwrap(); + + let package2: HiiKeyboardPkg = buffer.pread(0).unwrap(); + assert_eq!(package, package2); + } + + #[test] + fn default_layout_should_match_reference_binary() { + let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); + + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); + + let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); + let test_pkg = get_default_keyboard_pkg(); + assert_eq!(file_pkg, test_pkg); + + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); + + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + } + #[test] + fn ns_key_test_should_match_reference_ns_key_binary() { + let mut test_file = File::open(test_collateral!("KeyboardLayoutNs.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); + + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); + + let ns_file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); + + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&ns_file_pkg, 0).unwrap(); + + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + + //Start with the default key layout and reconstruct it to match the reference ns key layout. + let mut test_pkg = get_default_keyboard_pkg(); + + let keys = &mut test_pkg.layouts[0].keys; + + let (index, _) = keys + .iter() + .enumerate() + .find(|(_, element)| if let HiiKey::Key(key) = element { key.key == EfiKey::E0 } else { false }) + .unwrap(); + + #[rustfmt::skip] + let ns_key = HiiKey::NsKey(HiiNsKeyDescriptor { + descriptor: + key_descriptor!(EfiKey::E0, '\0', '\0', '\0', '\0', NS_KEY_MODIFIER, 0), + dependent_keys: vec![ + key_descriptor!(EfiKey::C1, '\u{00E2}', '\u{00C2}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key_descriptor!(EfiKey::D3, '\u{00EA}', '\u{00CA}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key_descriptor!(EfiKey::D8, '\u{00EC}', '\u{00CC}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key_descriptor!(EfiKey::D9, '\u{00F4}', '\u{00D4}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK), + key_descriptor!(EfiKey::D7, '\u{00FB}', '\u{00CB}', '\0', '\0', NS_KEY_DEPENDENCY_MODIFIER, AFFECTED_BY_STANDARD_SHIFT | AFFECTED_BY_CAPS_LOCK) + ]}); + + keys[index] = ns_key; + + assert_eq!(ns_file_pkg, test_pkg); + + let mut test_buffer = [0u8; 4096]; + let pkg_buffer_size = test_buffer.pwrite(&test_pkg, 0).unwrap(); + + assert_eq!(file_buffer, test_buffer[0..pkg_buffer_size]); + } + + #[test] + fn package_list_serialization_should_generate_package_list() { + let mut test_file = File::open(test_collateral!("KeyboardLayout.bin")).expect("failed to open test file."); + let mut file_buffer = Vec::new(); + + test_file.read_to_end(&mut file_buffer).expect("failed to read test file"); + + let file_pkg: HiiKeyboardPkg = file_buffer.pread(0).unwrap(); + + let pkg_list = HiiKeyboardPkgList { + package_list_guid: efi::Guid::from_fields( + 0xc0f3b43, + 0x44de, + 0x4907, + 0xb4, + 0x78, + &[0x22, 0x5f, 0x6f, 0x62, 0x89, 0xdc], + ), + package: file_pkg, + }; + + let mut test_buffer = [0u8; 4096]; + let pkg_list_buffer_size = test_buffer.pwrite(&pkg_list, 0).unwrap(); + let guid_size = mem::size_of::(); + + assert_eq!(&test_buffer[0..guid_size], pkg_list.package_list_guid.as_bytes()); + assert_eq!(&test_buffer[guid_size..guid_size + 4], (pkg_list_buffer_size as u32).to_le_bytes()); + assert_eq!(&test_buffer[guid_size + 4..guid_size + 4 + file_buffer.len()], file_buffer); + assert_eq!(&test_buffer[guid_size + 4 + file_buffer.len()..pkg_list_buffer_size], 0xDF000004u32.to_le_bytes()); + + let deserialized_pkg_list: HiiKeyboardPkgList = test_buffer.pread(0).unwrap(); + + assert_eq!(pkg_list, deserialized_pkg_list); + } +}