[PATCH 00/16] Continue TKey development
From: Simon Glass <simon.glass@canonical.com> This series adds the ability to provide a user-supplied secret to the TKey and use that to obtain a disk-encryption key. Expo is enhanced to support password entry and bootflows can now record whether the root disk is encrypted or not. Further work will enable the TKey in the UI and actually unlock an encrypted disk. Simon Glass (16): aes: Use const pointers for read-only parameters emulation: Support the bootcmd more generally input: Provide a way for tests to register a mouse click expo: Support hiding password entry expo: Support disk-passphrase entry in the menu expo: Allow entering text into textline in non-popup expos tkey: Allow modelling the tkey being disconnected tkey: Support loading an app with a user-supplied secret tkey: Provide some back-door functions for TKey tests tkey: Use SHA256 to obtain the disk-encryption key tkey: sandbox: Avoid returning -ENODEV from the driver tkey: Allow selecting the TKey device by name tkey: Provide a real tkey device with test.dts boot: Use constants for a few common strings boot: Show an indication for encrypted bootflows boot: Detect encrypted partitions with extlinux arch/sandbox/dts/test.dts | 5 + arch/x86/cpu/qemu/qemu.c | 28 ----- board/emulation/common/Makefile | 3 + board/emulation/common/bootcmd.c | 37 +++++++ boot/bootflow.c | 5 +- boot/bootflow_internal.h | 11 ++ boot/bootflow_menu.c | 31 ++++++ boot/bootmeth_extlinux.c | 54 ++++++++++ boot/scene.c | 28 ++++- cmd/bootflow.c | 15 ++- cmd/tkey.c | 36 ++++++- doc/board/emulation/common.rst | 28 +++++ doc/board/emulation/index.rst | 1 + doc/board/emulation/qemu-x86.rst | 18 +--- doc/usage/cmd/tkey.rst | 24 ++++- drivers/crypto/nuvoton/npcm_aes.c | 22 ++-- drivers/input/mouse-uclass.c | 13 +++ drivers/misc/tkey-uclass.c | 174 +++++++++++++++++------------- drivers/misc/tkey_emul.c | 82 +++++++++++++- drivers/misc/tkey_sandbox.c | 12 +-- include/bootflow.h | 4 + include/expo.h | 2 + include/mouse.h | 14 +++ include/tkey.h | 98 +++++++++++++++++ include/uboot_aes.h | 42 ++++---- lib/aes.c | 22 ++-- test/boot/bootflow.c | 111 +++++++++++++------ test/boot/cedit.c | 11 ++ test/cmd/tkey.c | 11 +- test/dm/tkey.c | 41 +++---- 30 files changed, 739 insertions(+), 244 deletions(-) create mode 100644 board/emulation/common/bootcmd.c create mode 100644 doc/board/emulation/common.rst -- 2.43.0 base-commit: 33f5f5cb3f175edb3b1ded10f091706aefda0d84 branch: secf
From: Simon Glass <simon.glass@canonical.com> Update AES function signatures to use const pointers for parameters that are not modified. This improves type safety and makes it easier to see which parameters are read-only. Fix the Nuvoton npcm_ae driver as well since it implements the API. Really that should be handled by a driver, but leave that for now. Functions updated: - aes_expand_key(): key parameter - aes_encrypt(): in and expkey parameters - aes_decrypt(): in and expkey parameters - aes_apply_cbc_chain_data(): cbc_chain_data and src parameters - aes_cbc_encrypt_blocks(): key_exp, iv, and src parameters - aes_cbc_decrypt_blocks(): key_exp, iv, and src parameters - add_round_key(): key parameter (internal) - debug_print_vector(): data parameter (internal) Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/crypto/nuvoton/npcm_aes.c | 22 ++++++++-------- include/uboot_aes.h | 42 +++++++++++++++---------------- lib/aes.c | 22 ++++++++-------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/crypto/nuvoton/npcm_aes.c b/drivers/crypto/nuvoton/npcm_aes.c index 8d3a30ea918..68b582b0d12 100644 --- a/drivers/crypto/nuvoton/npcm_aes.c +++ b/drivers/crypto/nuvoton/npcm_aes.c @@ -102,10 +102,10 @@ static int npcm_aes_init(u8 dec_enc) return 0; } -static inline void npcm_aes_load_iv(u8 *iv) +static inline void npcm_aes_load_iv(const u8 *iv) { struct npcm_aes_regs *regs = aes_priv->regs; - u32 *p = (u32 *)iv; + const u32 *p = (const u32 *)iv; u32 i; /* Initialization Vector is loaded in 32-bit chunks */ @@ -113,10 +113,10 @@ static inline void npcm_aes_load_iv(u8 *iv) writel(p[i], ®s->aes_iv_0 + i); } -static inline void npcm_aes_load_key(u8 *key) +static inline void npcm_aes_load_key(const u8 *key) { struct npcm_aes_regs *regs = aes_priv->regs; - u32 *p = (u32 *)key; + const u32 *p = (const u32 *)key; u32 i; /* The key can be loaded either via the configuration or by using sideband @@ -140,7 +140,7 @@ static inline void npcm_aes_load_key(u8 *key) } } -static inline void npcm_aes_write(u32 *in) +static inline void npcm_aes_write(const u32 *in) { struct npcm_aes_regs *regs = aes_priv->regs; u32 i; @@ -160,7 +160,7 @@ static inline void npcm_aes_read(u32 *out) out[i] = readl(®s->aes_fifo_data); } -static void npcm_aes_feed(u32 num_aes_blocks, u32 *datain, u32 *dataout) +static void npcm_aes_feed(u32 num_aes_blocks, const u32 *datain, u32 *dataout) { struct npcm_aes_regs *regs = aes_priv->regs; u32 aes_datablk; @@ -235,14 +235,14 @@ static void npcm_aes_feed(u32 num_aes_blocks, u32 *datain, u32 *dataout) } } -void aes_expand_key(u8 *key, u32 key_size, u8 *expkey) +void aes_expand_key(const u8 *key, u32 key_size, u8 *expkey) { /* npcm hw expands the key automatically, just copy it */ memcpy(expkey, key, SIZE_AES_BLOCK * 2); } -void aes_cbc_encrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks) +void aes_cbc_encrypt_blocks(u32 key_size, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks) { if (npcm_aes_init(AES_OP_ENCRYPT)) return; @@ -254,8 +254,8 @@ void aes_cbc_encrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, npcm_aes_feed(num_aes_blocks, (u32 *)src, (u32 *)dst); } -void aes_cbc_decrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks) +void aes_cbc_decrypt_blocks(u32 key_size, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks) { if (npcm_aes_init(AES_OP_DECRYPT)) return; diff --git a/include/uboot_aes.h b/include/uboot_aes.h index d2583bed992..440049e3069 100644 --- a/include/uboot_aes.h +++ b/include/uboot_aes.h @@ -44,67 +44,67 @@ enum { * Expand a key into a key schedule, which is then used for the other * operations. * - * @key Key + * @key Key (not modified) * @key_size Size of the key (in bits) * @expkey Buffer to place expanded key, AES_EXPAND_KEY_LENGTH */ -void aes_expand_key(u8 *key, u32 key_size, u8 *expkey); +void aes_expand_key(const u8 *key, u32 key_size, u8 *expkey); /** * aes_encrypt() - Encrypt single block of data with AES 128 * * @key_size Size of the aes key (in bits) - * @in Input data - * @expkey Expanded key to use for encryption (from aes_expand_key()) + * @in Input data (not modified) + * @expkey Expanded key to use for encryption (from aes_expand_key(), not modified) * @out Output data */ -void aes_encrypt(u32 key_size, u8 *in, u8 *expkey, u8 *out); +void aes_encrypt(u32 key_size, const u8 *in, const u8 *expkey, u8 *out); /** * aes_decrypt() - Decrypt single block of data with AES 128 * * @key_size Size of the aes key (in bits) - * @in Input data - * @expkey Expanded key to use for decryption (from aes_expand_key()) + * @in Input data (not modified) + * @expkey Expanded key to use for decryption (from aes_expand_key(), not modified) * @out Output data */ -void aes_decrypt(u32 key_size, u8 *in, u8 *expkey, u8 *out); +void aes_decrypt(u32 key_size, const u8 *in, const u8 *expkey, u8 *out); /** * Apply chain data to the destination using EOR * * Each array is of length AES_BLOCK_LENGTH. * - * @cbc_chain_data Chain data - * @src Source data + * @cbc_chain_data Chain data (not modified) + * @src Source data (not modified) * @dst Destination data, which is modified here */ -void aes_apply_cbc_chain_data(u8 *cbc_chain_data, u8 *src, u8 *dst); +void aes_apply_cbc_chain_data(const u8 *cbc_chain_data, const u8 *src, u8 *dst); /** * aes_cbc_encrypt_blocks() - Encrypt multiple blocks of data with AES CBC. * * @key_size Size of the aes key (in bits) - * @key_exp Expanded key to use - * @iv Initialization vector - * @src Source data to encrypt + * @key_exp Expanded key to use (not modified) + * @iv Initialization vector (not modified) + * @src Source data to encrypt (not modified) * @dst Destination buffer * @num_aes_blocks Number of AES blocks to encrypt */ -void aes_cbc_encrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks); +void aes_cbc_encrypt_blocks(u32 key_size, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks); /** * Decrypt multiple blocks of data with AES CBC. * * @key_size Size of the aes key (in bits) - * @key_exp Expanded key to use - * @iv Initialization vector - * @src Source data to decrypt + * @key_exp Expanded key to use (not modified) + * @iv Initialization vector (not modified) + * @src Source data to decrypt (not modified) * @dst Destination buffer * @num_aes_blocks Number of AES blocks to decrypt */ -void aes_cbc_decrypt_blocks(u32 key_size, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks); +void aes_cbc_decrypt_blocks(u32 key_size, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks); #endif /* _AES_REF_H_ */ diff --git a/lib/aes.c b/lib/aes.c index 3bcbeeab9af..6894dd89e50 100644 --- a/lib/aes.c +++ b/lib/aes.c @@ -498,7 +498,7 @@ static void inv_mix_sub_columns(u8 *state) * encrypt/decrypt columns of the key * n.b. you can replace this with byte-wise xor if you wish. */ -static void add_round_key(u32 *state, u32 *key) +static void add_round_key(u32 *state, const u32 *key) { int idx; @@ -537,7 +537,7 @@ static u32 aes_get_keycols(u32 key_len) } /* produce AES_STATECOLS bytes for each round */ -void aes_expand_key(u8 *key, u32 key_len, u8 *expkey) +void aes_expand_key(const u8 *key, u32 key_len, u8 *expkey) { u8 tmp0, tmp1, tmp2, tmp3, tmp4; uint idx, aes_rounds, aes_keycols; @@ -574,7 +574,7 @@ void aes_expand_key(u8 *key, u32 key_len, u8 *expkey) } /* encrypt one 128 bit block */ -void aes_encrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out) +void aes_encrypt(u32 key_len, const u8 *in, const u8 *expkey, u8 *out) { u8 state[AES_STATECOLS * 4]; u32 round, aes_rounds; @@ -597,7 +597,7 @@ void aes_encrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out) memcpy(out, state, sizeof(state)); } -void aes_decrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out) +void aes_decrypt(u32 key_len, const u8 *in, const u8 *expkey, u8 *out) { u8 state[AES_STATECOLS * 4]; int round, aes_rounds; @@ -620,7 +620,7 @@ void aes_decrypt(u32 key_len, u8 *in, u8 *expkey, u8 *out) memcpy(out, state, sizeof(state)); } -static void debug_print_vector(char *name, u32 num_bytes, u8 *data) +static void debug_print_vector(char *name, u32 num_bytes, const u8 *data) { #ifdef DEBUG printf("%s [%d] @0x%p", name, num_bytes, data); @@ -628,7 +628,7 @@ static void debug_print_vector(char *name, u32 num_bytes, u8 *data) #endif } -void aes_apply_cbc_chain_data(u8 *cbc_chain_data, u8 *src, u8 *dst) +void aes_apply_cbc_chain_data(const u8 *cbc_chain_data, const u8 *src, u8 *dst) { int i; @@ -636,11 +636,11 @@ void aes_apply_cbc_chain_data(u8 *cbc_chain_data, u8 *src, u8 *dst) *dst++ = *src++ ^ *cbc_chain_data++; } -void aes_cbc_encrypt_blocks(u32 key_len, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks) +void aes_cbc_encrypt_blocks(u32 key_len, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks) { u8 tmp_data[AES_BLOCK_LENGTH]; - u8 *cbc_chain_data = iv; + const u8 *cbc_chain_data = iv; u32 i; for (i = 0; i < num_aes_blocks; i++) { @@ -662,8 +662,8 @@ void aes_cbc_encrypt_blocks(u32 key_len, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, } } -void aes_cbc_decrypt_blocks(u32 key_len, u8 *key_exp, u8 *iv, u8 *src, u8 *dst, - u32 num_aes_blocks) +void aes_cbc_decrypt_blocks(u32 key_len, const u8 *key_exp, const u8 *iv, + const u8 *src, u8 *dst, u32 num_aes_blocks) { u8 tmp_data[AES_BLOCK_LENGTH], tmp_block[AES_BLOCK_LENGTH]; /* Convenient array of 0's for IV */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The code for obtaining a bootcmd from the host when running until QEMU is currently x86-specific. In fact it can be supported on other architecture. Move it into a common place and update the documentation. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/x86/cpu/qemu/qemu.c | 28 ------------------------ board/emulation/common/Makefile | 3 +++ board/emulation/common/bootcmd.c | 37 ++++++++++++++++++++++++++++++++ doc/board/emulation/common.rst | 28 ++++++++++++++++++++++++ doc/board/emulation/index.rst | 1 + doc/board/emulation/qemu-x86.rst | 18 ++-------------- 6 files changed, 71 insertions(+), 44 deletions(-) create mode 100644 board/emulation/common/bootcmd.c create mode 100644 doc/board/emulation/common.rst diff --git a/arch/x86/cpu/qemu/qemu.c b/arch/x86/cpu/qemu/qemu.c index 24916d867ee..b393205acb3 100644 --- a/arch/x86/cpu/qemu/qemu.c +++ b/arch/x86/cpu/qemu/qemu.c @@ -153,31 +153,3 @@ int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq) return irq; } #endif - -#if CONFIG_IS_ENABLED(EVENT) -static int qemu_get_bootcmd(void *ctx, struct event *event) -{ - struct event_bootcmd *bc = &event->data.bootcmd; - enum fw_cfg_selector select; - struct udevice *qfw_dev; - ulong size; - - if (qfw_get_dev(&qfw_dev)) - return 0; - - if (qfw_locate_file(qfw_dev, "opt/u-boot/bootcmd", &select, &size)) - return 0; - if (!size) - return 0; - - /* Check if the command fits in the provided buffer with terminator */ - if (size >= bc->size) - return -ENOSPC; - - qfw_read_entry(qfw_dev, select, size, bc->bootcmd); - bc->bootcmd[size] = '\0'; - - return 0; -} -EVENT_SPY_FULL(EVT_BOOTCMD, qemu_get_bootcmd); -#endif diff --git a/board/emulation/common/Makefile b/board/emulation/common/Makefile index c5b452e7e34..a91e4f16fef 100644 --- a/board/emulation/common/Makefile +++ b/board/emulation/common/Makefile @@ -2,3 +2,6 @@ obj-$(CONFIG_SYS_MTDPARTS_RUNTIME) += qemu_mtdparts.o obj-$(CONFIG_SET_DFU_ALT_INFO) += qemu_dfu.o +ifdef CONFIG_QFW +obj-$(CONFIG_$(PHASE_)EVENT) += bootcmd.o +endif diff --git a/board/emulation/common/bootcmd.c b/board/emulation/common/bootcmd.c new file mode 100644 index 00000000000..6fc7c618c8f --- /dev/null +++ b/board/emulation/common/bootcmd.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <errno.h> +#include <event.h> +#include <qfw.h> + +#if CONFIG_IS_ENABLED(EVENT) +static int qemu_get_bootcmd(void *ctx, struct event *event) +{ + struct event_bootcmd *bc = &event->data.bootcmd; + enum fw_cfg_selector select; + struct udevice *qfw_dev; + ulong size; + + if (qfw_get_dev(&qfw_dev)) + return 0; + + if (qfw_locate_file(qfw_dev, "opt/u-boot/bootcmd", &select, &size)) + return 0; + if (!size) + return 0; + + /* Check if the command fits in the provided buffer with terminator */ + if (size >= bc->size) + return -ENOSPC; + + qfw_read_entry(qfw_dev, select, size, bc->bootcmd); + bc->bootcmd[size] = '\0'; + + return 0; +} +EVENT_SPY_FULL(EVT_BOOTCMD, qemu_get_bootcmd); +#endif diff --git a/doc/board/emulation/common.rst b/doc/board/emulation/common.rst new file mode 100644 index 00000000000..90dde5f442a --- /dev/null +++ b/doc/board/emulation/common.rst @@ -0,0 +1,28 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Common features +=============== + +It is possible to specify the boot command directly using the fw_cfg interface. +This allows QEMU to control the boot command, which can be useful for automated +testing or scripting. To use this feature, create a file containing the boot +command and pass it to QEMU using the fw_cfg option. + +Here is an x86 example:: + + $ echo "qfw load; zboot 01000000 - 04000000 1b1ab50" > bootcmd.txt + $ qemu-system-x86_64 -nographic -bios path/to/u-boot.rom \ + -fw_cfg name=opt/u-boot/bootcmd,file=bootcmd.txt + +U-Boot will read the boot command from the firmware configuration and execute it +automatically during the boot process. This bypasses the normal distro boot +sequence. + +Note that the boot command is limited in length and should not exceed the boot +command buffer size. If the command is too long, U-Boot will fail to read it and +fall back to the default boot behavior. + +The :doc:`script` and build-efi scripts provide a `-c` option for this feature, +although it uses a string rather than a file. + +Note that ``CONFIG_QFW`` must be enabled for this feature to work. diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst index 5a2a00ae225..6eccf7bad8a 100644 --- a/doc/board/emulation/index.rst +++ b/doc/board/emulation/index.rst @@ -8,6 +8,7 @@ Emulation acpi blkdev + common script qemu-arm qemu-mips diff --git a/doc/board/emulation/qemu-x86.rst b/doc/board/emulation/qemu-x86.rst index c2862e631ee..27f0d273f38 100644 --- a/doc/board/emulation/qemu-x86.rst +++ b/doc/board/emulation/qemu-x86.rst @@ -116,22 +116,8 @@ supports 32-bit. Specifying a boot command -------------------------- -It is possible to specify the boot command directly using the fw_cfg interface. -This allows QEMU to control the boot command, which can be useful for automated -testing or scripting. To use this feature, create a file containing the boot -command and pass it to QEMU using the fw_cfg option:: - - $ echo "qfw load; zboot 01000000 - 04000000 1b1ab50" > bootcmd.txt - $ qemu-system-x86_64 -nographic -bios path/to/u-boot.rom \ - -fw_cfg name=opt/u-boot/bootcmd,file=bootcmd.txt - -U-Boot will read the boot command from the firmware configuration and execute it -automatically during the boot process. This bypasses the normal distro boot -sequence. - -Note that the boot command is limited in length and should not exceed the boot -command buffer size. If the command is too long, U-Boot will fail to read it and -fall back to the default boot behavior. +See :doc:`common` for details on how to provide a boot command to U-Boot on +startup. Booting distros --------------- -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> In tests it is useful to fake a mouse click to check that expo handles it correctly. Create a function for this. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/input/mouse-uclass.c | 13 +++++++++++++ include/mouse.h | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c index dea8babf5fe..99de94e68b0 100644 --- a/drivers/input/mouse-uclass.c +++ b/drivers/input/mouse-uclass.c @@ -107,6 +107,19 @@ int mouse_set_video(struct udevice *dev, struct udevice *video_dev) return 0; } +int mouse_queue_click_for_test(struct udevice *dev, int x, int y) +{ + struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->click_pending = true; + uc_priv->click_pos.x = x; + uc_priv->click_pos.y = y; + uc_priv->last_pos.x = x; + uc_priv->last_pos.y = y; + + return 0; +} + UCLASS_DRIVER(mouse) = { .id = UCLASS_MOUSE, .name = "mouse", diff --git a/include/mouse.h b/include/mouse.h index 92609cfd0e0..6180a2fc957 100644 --- a/include/mouse.h +++ b/include/mouse.h @@ -176,4 +176,18 @@ int mouse_set_ptr_visible(struct udevice *dev, bool visible); */ int mouse_set_video(struct udevice *dev, struct udevice *video_dev); +/** + * mouse_queue_click_for_test() - Queue a click event for testing + * + * This is a back-door function for tests to simulate a mouse click at a + * specific position. The click will be returned by the next call to + * mouse_get_click(). + * + * @dev: Mouse device + * @x: X coordinate of click + * @y: Y coordinate of click + * Returns: 0 if OK, -ve on error + */ +int mouse_queue_click_for_test(struct udevice *dev, int x, int y); + #endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Some fields may have sensitive information. Allow it to be obscured during entry, in case someone is watching the display nearby. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/scene.c | 19 +++++++++++++++++-- include/expo.h | 2 ++ test/boot/cedit.c | 11 +++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index c2d106738f5..77926bda5fa 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -573,6 +573,19 @@ static void scene_render_background(struct scene_obj *obj, bool box_only, } } +static void draw_string(struct udevice *cons, const char *str, int len, + bool password) +{ + if (password) { + int i; + + for (i = 0; i < len; i++) + vidconsole_put_char(cons, '*'); + } else { + vidconsole_put_stringn(cons, str, len); + } +} + static int scene_txt_render(struct expo *exp, struct udevice *dev, struct udevice *cons, struct scene_obj *obj, struct scene_txt_generic *gen, int x, int y, @@ -630,7 +643,8 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, if (!mline) { vidconsole_set_cursor_pos(cons, x, y); - vidconsole_put_string(cons, str); + draw_string(cons, str, strlen(str), + obj->flags & SCENEOF_PASSWORD); } alist_for_each(mline, &gen->lines) { @@ -648,7 +662,8 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, if (y > bbox.y1) break; /* clip this line and any following */ vidconsole_set_cursor_pos(cons, x, y); - vidconsole_put_stringn(cons, str + mline->start, mline->len); + draw_string(cons, str + mline->start, mline->len, + obj->flags & SCENEOF_PASSWORD); } if (obj->flags & SCENEOF_POINT) vidconsole_pop_colour(cons, &old); diff --git a/include/expo.h b/include/expo.h index 2f32ff0aa72..e6093769421 100644 --- a/include/expo.h +++ b/include/expo.h @@ -320,6 +320,7 @@ enum scene_obj_align { * @SCENEOF_SYNC_BBOX: object's bounding box has changed * @SCENEOF_MANUAL: manually arrange the items associated with this object * @SCENEOF_DIRTY: object has been modified and needs to be redrawn + * @SCENEOF_PASSWORD: textline input should show stars instead of characters * @SCENEOF_LAST: used just as a check for the size of the flags mask */ enum scene_obj_flags_t { @@ -333,6 +334,7 @@ enum scene_obj_flags_t { SCENEOF_SYNC_BBOX = BIT(7), SCENEOF_MANUAL = BIT(8), SCENEOF_DIRTY = BIT(9), + SCENEOF_PASSWORD = BIT(10), SCENEOF_LAST, /* check for size of flags below */ }; diff --git a/test/boot/cedit.c b/test/boot/cedit.c index bccc93f8926..041da445459 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -376,6 +376,7 @@ static int cedit_render_lineedit(struct unit_test_state *uts) extern struct expo *cur_exp; struct expo_action evt; struct expo_action act; + struct scene_obj *edit; struct udevice *dev, *con; struct stdio_dev *sdev; struct scene *scn; @@ -405,6 +406,16 @@ static int cedit_render_lineedit(struct unit_test_state *uts) ut_asserteq(5344, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); + edit = scene_obj_find(scn, ID_MACHINE_NAME_EDIT, SCENEOBJT_TEXT); + ut_assert(edit); + + /* try the password flag */ + edit->flags |= SCENEOF_PASSWORD; + ut_assertok(expo_render(exp)); + ut_asserteq(5135, video_compress_fb(uts, dev, false)); + ut_assertok(video_check_copy_fb(uts, dev)); + edit->flags &= ~SCENEOF_PASSWORD; + /* move to the line-edit field */ act.type = EXPOACT_POINT_OBJ; act.select.id = ID_MACHINE_NAME; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Provide fields to show that a disk is locked and allow the user to enter a passphrase to unlock it. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/bootflow_internal.h | 11 +++++++++++ boot/bootflow_menu.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index 519c3ee782a..b35700ba38f 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -26,6 +26,11 @@ * @ITEM_VERSION_NAME: Distro's name for the version, e.g. 'Noble Numbat' * @ITEM_BOX: Box around the item (normally hidden) * @ITEM_VERIFIED: Indicates that the item is verified by the vendor + * @ITEM_LOCKED: Indicates that the partition is encrypted (e.g., LUKS) + * @ITEM_PASS: Textline object for passphrase entry + * @ITEM_PASS_LABEL: Label text for the passphrase field (e.g., "Passphrase:") + * @ITEM_PASS_EDIT: Edit field for entering the passphrase + * @ITEM_PASS_MSG: Message text for displaying success/error feedback */ enum boomenu_id_t { START, @@ -37,6 +42,7 @@ enum boomenu_id_t { STR_AUTOBOOT, STR_MENU_TITLE, STR_POINTER, + STR_PASS_MSG, /* scene */ MAIN, @@ -70,6 +76,11 @@ enum boomenu_id_t { ITEM_VERSION_NAME = 1100, ITEM_BOX = 1200, ITEM_VERIFIED = 1300, + ITEM_LOCKED = 1400, + ITEM_PASS = 1500, + ITEM_PASS_LABEL = 1600, + ITEM_PASS_EDIT = 1700, + ITEM_PASS_MSG = 1800, /* left margin for the main menu */ MARGIN_LEFT = 100, diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index eda95428bf4..17fab284ef7 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -44,6 +44,9 @@ static int bootflow_menu_set_item_props(struct scene *scn, ret |= scene_obj_set_hide(scn, ITEM_VERSION_NAME + i, true); scene_obj_set_hide(scn, ITEM_VERIFIED + i, true); ret |= scene_obj_set_hide(scn, ITEM_KEY + i, false); + scene_obj_set_hide(scn, ITEM_LOCKED + i, true); + scene_obj_set_hide(scn, ITEM_PASS_LABEL + i, true); + scene_obj_set_hide(scn, ITEM_PASS_EDIT + i, true); if (ret) return log_msg_ret("msp", ret); @@ -229,6 +232,7 @@ int bootflow_menu_add(struct expo *exp, struct bootflow *bflow, int seq, struct scene **scnp) { struct menu_priv *priv = exp->priv; + struct scene_obj_textline *tline; char str[2], *key; struct scene *scn; uint preview_id; @@ -279,6 +283,33 @@ int bootflow_menu_add(struct expo *exp, struct bootflow *bflow, int seq, if (ret < 0) return log_msg_ret("itm", -EINVAL); + /* + * Create passphrase textline with label and edit field (12 chars). Show + * characters as asterisks + */ + ret = scene_textline(scn, "passphrase", ITEM_PASS + seq, 12, &tline); + if (ret < 0) + return log_msg_ret("itp", -EINVAL); + tline->obj.flags |= SCENEOF_PASSWORD; + ret = scene_txt_str(scn, "pass_label", ITEM_PASS_LABEL + seq, 0, + "Passphrase:", NULL); + if (ret < 0) + return log_msg_ret("itl", -EINVAL); + tline->label_id = ret; + + ret = scene_txt_str(scn, "pass_edit", ITEM_PASS_EDIT + seq, 0, + "", NULL); + if (ret < 0) + return log_msg_ret("ite", -EINVAL); + tline->edit_id = ret; + + /* Create message text (hidden by default) for success/error feedback */ + ret = scene_txt_str(scn, "pass_msg", ITEM_PASS_MSG + seq, + STR_PASS_MSG + seq, "", NULL); + if (ret < 0) + return log_msg_ret("ipm", -EINVAL); + scene_obj_set_hide(scn, ITEM_PASS_MSG + seq, true); + ret = bootflow_menu_set_item_props(scn, seq, bflow); if (ret) return log_msg_ret("itp", -EINVAL); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Currently textlines only support text entry when with popup expos. In some cases we want to have menu items to support this, e.g. to enter a passphrase to unlock an encrypted disk. Add the missing logic. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/scene.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/boot/scene.c b/boot/scene.c index 77926bda5fa..1392d063c49 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -1145,6 +1145,15 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) if (ret) return log_msg_ret("key", ret); break; + } else if (!(obj->flags & SCENEOF_OPEN) && + obj->type == SCENEOBJT_TEXTLINE) { + struct scene_obj_textline *tline; + + tline = (struct scene_obj_textline *)obj; + ret = scene_textline_send_key(scn, tline, key, event); + if (ret) + return log_msg_ret("key", ret); + break; } } -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Provide some plat data which tracks whether the emulated tkey is connected or not, to allow testing of re-inserting a tkey to reset the passphrase. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/misc/tkey_emul.c | 41 +++++++++++++++++++++++++++++++++++++++- include/tkey.h | 13 +++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tkey_emul.c b/drivers/misc/tkey_emul.c index d8d81280486..5f7a4aa2f09 100644 --- a/drivers/misc/tkey_emul.c +++ b/drivers/misc/tkey_emul.c @@ -45,6 +45,15 @@ #define STATUS_OK 0x00 #define STATUS_ERROR 0x01 +/* + * struct tkey_emul_plat - TKey emulator platform data (persists across remove) + * + * @disconnected: Whether device is disconnected (for testing removal) + */ +struct tkey_emul_plat { + bool disconnected; +}; + /* * struct tkey_emul_priv - TKey emulator state * @@ -200,10 +209,15 @@ static int handle_app_cmd(struct udevice *dev, u8 cmd) static int tkey_emul_write(struct udevice *dev, const void *buf, int len) { + struct tkey_emul_plat *plat = dev_get_plat(dev); const u8 *data = buf; u8 header, endpoint, cmd; int ret; + /* Simulate device disconnection */ + if (plat->disconnected) + return -EIO; + if (len < 2) return -EINVAL; @@ -229,8 +243,15 @@ static int tkey_emul_write(struct udevice *dev, const void *buf, int len) static int tkey_emul_read_all(struct udevice *dev, void *buf, int maxlen, int timeout_ms) { + struct tkey_emul_plat *plat = dev_get_plat(dev); struct tkey_emul_priv *priv = dev_get_priv(dev); - int len = min(priv->resp_len, maxlen); + int len; + + /* Simulate device disconnection */ + if (plat->disconnected) + return -EIO; + + len = min(priv->resp_len, maxlen); log_debug("read_all: %d bytes max, returning %d bytes\n", maxlen, len); @@ -241,11 +262,28 @@ static int tkey_emul_read_all(struct udevice *dev, void *buf, int maxlen, return len; } +int tkey_emul_set_connected_for_test(struct udevice *dev, bool connected) +{ + struct tkey_emul_plat *plat = dev_get_plat(dev); + + plat->disconnected = !connected; + log_debug("Set emulator %s\n", connected ? "connected" : "disconnected"); + + return 0; +} + static int tkey_emul_probe(struct udevice *dev) { + struct tkey_emul_plat *plat = dev_get_plat(dev); struct tkey_emul_priv *priv = dev_get_priv(dev); int i; + /* Fail probe if device is disconnected */ + if (plat->disconnected) { + log_debug("probe failed - device disconnected\n"); + return -ENODEV; + } + /* Generate a deterministic UDI based on device name */ for (i = 0; i < 8; i++) priv->udi[i] = 0xa0 + i; @@ -281,4 +319,5 @@ U_BOOT_DRIVER(tkey_emul) = { .probe = tkey_emul_probe, .ops = &tkey_emul_ops, .priv_auto = sizeof(struct tkey_emul_priv), + .plat_auto = sizeof(struct tkey_emul_plat), }; diff --git a/include/tkey.h b/include/tkey.h index a16447795b1..14ad3ebc9e9 100644 --- a/include/tkey.h +++ b/include/tkey.h @@ -229,4 +229,17 @@ int tkey_derive_disk_key(struct udevice *dev, const void *app_data, int tkey_derive_wrapping_key(struct udevice *dev, const char *password, void *wrapping_key); +/** + * tkey_emul_set_connected_for_test() - Simulate device connection state + * + * This is a back-door function for tests to simulate physical insertion or + * removal of the TKey device. When disconnected, all I/O operations and + * probe attempts will fail. + * + * @dev: TKey device (must be tkey-emul) + * @connected: true to simulate device present, false to simulate removal + * Return: 0 on success, -ve error on failure + */ +int tkey_emul_set_connected_for_test(struct udevice *dev, bool connected); + #endif /* _TKEY_UCLASS_H */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> One useful feature a the TKey is the ability to set up its app with an key modified by a user-supplied secret. Add support for this. Take this opportunity to make the API more expo-friendly by allowing loading of the TKey to take place iteratively. The TKey runs fairly slowly (about 60Kbaud) and loading an app takes 6 seconds or so. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/misc/tkey-uclass.c | 149 +++++++++++++++++++++---------------- include/tkey.h | 49 ++++++++++++ 2 files changed, 132 insertions(+), 66 deletions(-) diff --git a/drivers/misc/tkey-uclass.c b/drivers/misc/tkey-uclass.c index 4696f9c8d83..d2c4b8351fd 100644 --- a/drivers/misc/tkey-uclass.c +++ b/drivers/misc/tkey-uclass.c @@ -502,27 +502,80 @@ static int tkey_load_app_header(struct udevice *dev, int app_size, return 0; } -static int tkey_load_app_data(struct udevice *dev, const void *data, int size) +int tkey_load_app_with_uss(struct udevice *dev, const void *app_data, + int app_size, const void *uss, int uss_size) { - struct tkey_frame cmd_frame, rsp_frame; - int offset = 0; + struct tkey_load_ctx ctx; int ret; - log_debug("Loading app data, %u bytes\n", size); + /* Start loading */ + ret = tkey_load_start(&ctx, dev, app_data, app_size, uss, + uss_size); + if (ret) + return ret; + + /* Send all remaining blocks */ + do { + ret = tkey_load_next(&ctx, 0); + } while (ret == -EAGAIN); + + return ret; +} + +int tkey_load_app(struct udevice *dev, const void *app_data, int app_size) +{ + return tkey_load_app_with_uss(dev, app_data, app_size, NULL, 0); +} + +int tkey_load_start(struct tkey_load_ctx *ctx, struct udevice *dev, + const void *app_data, int app_size, + const void *uss, int uss_size) +{ + int ret; + + /* Initialize context */ + ctx->dev = dev; + ctx->app_data = app_data; + ctx->app_size = app_size; + ctx->offset = 0; + + /* Check if we're in firmware mode first */ + ret = tkey_in_app_mode(dev); + if (ret < 0) { + log_debug("Failed to check device mode (error %d)\n", ret); + return ret; + } + + if (ret) { + log_debug("Device must be in firmware mode to load app\n"); + return -ENOTSUPP; + } + + log_debug("Starting iterative app load (%u bytes)...\n", app_size); + + /* Send app header with size and USS (if provided) */ + ret = tkey_load_app_header(dev, app_size, uss, uss_size); + if (ret) { + log_debug("Failed to send app header (error %d)\n", ret); + return ret; + } + + return 0; +} + +int tkey_load_next(struct tkey_load_ctx *ctx, int max_blocks) +{ + struct tkey_frame cmd_frame, rsp_frame; + int blocks_sent = 0; + int ret; - while (offset < size) { - int todo = min(size - offset, TKEY_MAX_DATA_SIZE - 1); - u8 len_code; + /* If max_blocks is 0, send all remaining blocks */ + if (max_blocks == 0) + max_blocks = INT_MAX; - /* Determine length code for chunk */ - if (todo <= 1) - len_code = TKEY_LEN_1_BYTE; - else if (todo <= 4) - len_code = TKEY_LEN_4_BYTES; - else if (todo <= 32) - len_code = TKEY_LEN_32_BYTES; - else - len_code = TKEY_LEN_128_BYTES; + while (ctx->offset < ctx->app_size && blocks_sent < max_blocks) { + int todo = min(ctx->app_size - ctx->offset, + TKEY_MAX_DATA_SIZE - 1); /* * Build LOAD_APP_DATA command (always use 128-byte frames @@ -533,7 +586,7 @@ static int tkey_load_app_data(struct udevice *dev, const void *data, int size) TKEY_STATUS_OK, TKEY_LEN_128_BYTES); cmd_frame.data[0] = TKEY_FW_CMD_LOAD_APP_DATA; - memcpy(&cmd_frame.data[1], data + offset, todo); + memcpy(&cmd_frame.data[1], ctx->app_data + ctx->offset, todo); /* Pad remaining bytes with zeros */ if (todo + 1 < 128) @@ -541,73 +594,37 @@ static int tkey_load_app_data(struct udevice *dev, const void *data, int size) 128 - (todo + 1)); /* Send chunk (always 128 bytes like Go app) */ - ret = tkey_send_frame(dev, &cmd_frame, 128); + ret = tkey_send_frame(ctx->dev, &cmd_frame, 128); if (ret < 0) return ret; /* Receive response */ - ret = tkey_recv_frame(dev, &rsp_frame, TKEY_LOAD_TIMEOUT_MS); + ret = tkey_recv_frame(ctx->dev, &rsp_frame, + TKEY_LOAD_TIMEOUT_MS); if (ret < 0) return ret; /* Check response status */ if (rsp_frame.header & TKEY_STATUS_ERROR) { log_debug("Load app data failed at offset %u\n", - offset); + ctx->offset); return -EIO; } - offset += todo; - log_debug("Loaded chunk: %u/%u bytes\n", offset, size); + ctx->offset += todo; + blocks_sent++; + log_debug("Loaded chunk: %u/%u bytes (%d blocks sent)\n", + ctx->offset, ctx->app_size, blocks_sent); schedule(); } - log_debug("App data loaded successfully\n"); - - return 0; -} - -int tkey_load_app_with_uss(struct udevice *dev, const void *app_data, - int app_size, const void *uss, int uss_size) -{ - int ret; - - /* Check if we're in firmware mode first */ - ret = tkey_in_app_mode(dev); - if (ret < 0) { - log_debug("Failed to check device mode (error %d)\n", ret); - return ret; + /* Check if we're done */ + if (ctx->offset >= ctx->app_size) { + log_debug("App data loaded successfully\n"); + return 0; /* Done */ } - if (ret) { - log_debug("Device must be in firmware mode to load app\n"); - return -ENOTSUPP; - } - - log_debug("Loading app (%u bytes)...\n", app_size); - - /* Send app header with size and USS (if provided) */ - ret = tkey_load_app_header(dev, app_size, uss, uss_size); - if (ret) { - log_debug("Failed to send app header (error %d)\n", ret); - return ret; - } - - /* Send app data */ - ret = tkey_load_app_data(dev, app_data, app_size); - if (ret) { - log_debug("Failed to send app data (error %d)\n", ret); - return ret; - } - - log_debug("App loaded successfully\n"); - - return 0; -} - -int tkey_load_app(struct udevice *dev, const void *app_data, int app_size) -{ - return tkey_load_app_with_uss(dev, app_data, app_size, NULL, 0); + return -EAGAIN; /* More blocks remain */ } int tkey_get_pubkey(struct udevice *dev, void *pubkey) diff --git a/include/tkey.h b/include/tkey.h index 14ad3ebc9e9..1c66dab8484 100644 --- a/include/tkey.h +++ b/include/tkey.h @@ -13,6 +13,21 @@ struct tkey_frame; struct udevice; +/** + * struct tkey_load_ctx - Context for iterative app loading + * + * @dev: TKey device + * @app_data: Complete app binary data + * @app_size: Size of app data + * @offset: Current offset in app data + */ +struct tkey_load_ctx { + struct udevice *dev; + const void *app_data; + int app_size; + int offset; +}; + /* TKey constants */ #define TKEY_NAME_SIZE 5 #define TKEY_CDI_SIZE 32 @@ -182,6 +197,40 @@ int tkey_load_app(struct udevice *dev, const void *app_data, int app_size); int tkey_load_app_with_uss(struct udevice *dev, const void *app_data, int app_size, const void *uss, int uss_size); +/** + * tkey_load_start() - Start iterative app loading + * + * @ctx: Context structure to initialize + * @dev: TKey device + * @app_data: Complete app binary data + * @app_size: Size of app data + * @uss: User-Supplied Secret (password/passphrase) - can be NULL + * @uss_size: Size of USS data (max 32 bytes) + * + * This function initializes the context and sends the app header. + * Call tkey_load_next() repeatedly to send the data blocks. + * + * Return: 0 on success, -ve error on failure (-ENOTSUPP if not in + * firmware mode) + */ +int tkey_load_start(struct tkey_load_ctx *ctx, struct udevice *dev, + const void *app_data, int app_size, + const void *uss, int uss_size); + +/** + * tkey_load_next() - Send next block(s) of app data + * + * @ctx: Context structure from tkey_load_start() + * @max_blocks: Maximum number of blocks to send (0 = send all remaining) + * + * This function sends the next n blocks of app data. Call repeatedly + * until it returns 0 (done) instead of -EAGAIN (more blocks remain). + * + * Return: 0 if loading is complete, -EAGAIN if more blocks remain, + * -ve error on failure + */ +int tkey_load_next(struct tkey_load_ctx *ctx, int max_blocks); + /** * tkey_get_pubkey() - Get public key from signer app * -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The tests need to reset the emulated TKey and put it into a known state. Add a few more functions to help with this. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/misc/tkey_emul.c | 41 ++++++++++++++++++++++++++++++++++++++++ include/tkey.h | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/drivers/misc/tkey_emul.c b/drivers/misc/tkey_emul.c index 5f7a4aa2f09..403e6e819b9 100644 --- a/drivers/misc/tkey_emul.c +++ b/drivers/misc/tkey_emul.c @@ -262,6 +262,47 @@ static int tkey_emul_read_all(struct udevice *dev, void *buf, int maxlen, return len; } +int tkey_emul_reset_for_test(struct udevice *dev) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + + /* Reset to firmware mode */ + priv->app_loaded = false; + priv->total_loaded = 0; + priv->resp_len = 0; + log_debug("Reset emulator to firmware mode\n"); + + return 0; +} + +int tkey_emul_set_pubkey_for_test(struct udevice *dev, const void *pubkey) +{ + struct tkey_emul_priv *priv = dev_get_priv(dev); + + memcpy(priv->pubkey, pubkey, 32); + log_debug("Set test pubkey\n"); + + return 0; +} + +int tkey_emul_set_app_mode_for_test(struct udevice *dev, bool app_mode) +{ + /* + * Only set app_loaded if device is active (has priv data). + * After device_remove(), priv is freed, so we can't access it. + * When device is re-probed, it will start in firmware mode by default. + */ + if (device_active(dev)) { + struct tkey_emul_priv *priv = dev_get_priv(dev); + + priv->app_loaded = app_mode; + } + + log_debug("Set emulator to %s mode\n", app_mode ? "app" : "firmware"); + + return 0; +} + int tkey_emul_set_connected_for_test(struct udevice *dev, bool connected) { struct tkey_emul_plat *plat = dev_get_plat(dev); diff --git a/include/tkey.h b/include/tkey.h index 1c66dab8484..e610d91140c 100644 --- a/include/tkey.h +++ b/include/tkey.h @@ -278,6 +278,42 @@ int tkey_derive_disk_key(struct udevice *dev, const void *app_data, int tkey_derive_wrapping_key(struct udevice *dev, const char *password, void *wrapping_key); +/** + * tkey_emul_reset_for_test() - Reset emulator to firmware mode for testing + * + * This is a back-door function for tests to simulate TKey replug by resetting + * the emulator to firmware mode. Only works with tkey-emul driver. + * + * @dev: TKey device (must be tkey-emul) + * Return: 0 on success, -ve error on failure + */ +int tkey_emul_reset_for_test(struct udevice *dev); + +/** + * tkey_emul_set_pubkey_for_test() - Set public key returned by emulator + * + * This is a back-door function for tests to configure what public key the + * emulator returns from APP_CMD_GET_PUBKEY. The disk key will be derived + * from this pubkey using SHA256(hex(pubkey)). + * + * @dev: TKey device (must be tkey-emul) + * @pubkey: Public key to return (32 bytes) + * Return: 0 on success, -ve error on failure + */ +int tkey_emul_set_pubkey_for_test(struct udevice *dev, const void *pubkey); + +/** + * tkey_emul_set_app_mode_for_test() - Set emulator mode for testing + * + * This is a back-door function for tests to force the emulator into app mode + * or firmware mode, allowing tests to verify replug behavior. + * + * @dev: TKey device (must be tkey-emul) + * @app_mode: true for app mode, false for firmware mode + * Return: 0 on success, -ve error on failure + */ +int tkey_emul_set_app_mode_for_test(struct udevice *dev, bool app_mode); + /** * tkey_emul_set_connected_for_test() - Simulate device connection state * -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Rather than Blake2b, use SHA256 to obtain the disk-encryption key based on the key material provided by the TKey. This matches the upcoming disk-encryption test. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/misc/tkey-uclass.c | 25 ++++++++++++++++++------- test/cmd/tkey.c | 4 ++-- test/dm/tkey.c | 18 +++++++++--------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/drivers/misc/tkey-uclass.c b/drivers/misc/tkey-uclass.c index d2c4b8351fd..fad5ffd6534 100644 --- a/drivers/misc/tkey-uclass.c +++ b/drivers/misc/tkey-uclass.c @@ -18,6 +18,8 @@ #include <linux/kernel.h> #include <u-boot/blake2.h> #include <u-boot/schedule.h> +#include <u-boot/sha256.h> +#include <hexdump.h> /* TKey Protocol Constants */ #define TKEY_FRAME_HEADER_SIZE 1 @@ -670,6 +672,7 @@ int tkey_derive_disk_key(struct udevice *dev, const void *app_data, int app_size, const void *uss, int uss_size, void *disk_key, void *pubkey, void *key_hash) { + char pubkey_hex[TKEY_PUBKEY_SIZE * 2 + 1]; int ret; /* Load the signer app with USS */ @@ -693,14 +696,22 @@ int tkey_derive_disk_key(struct udevice *dev, const void *app_data, log_debug("Public key retrieved\n"); - /* Derive disk encryption key from public key using BLAKE2b */ - ret = blake2b(disk_key, 32, pubkey, 32, NULL, 0); - if (ret) { - log_debug("Failed to derive disk key (error %d)\n", ret); - return ret; - } + /* + * Derive disk encryption key from public key using SHA256 + * Must match Python tkey-fde-key.py implementation which does: + * hashlib.sha256(pubkey.encode()).digest() + * + * This converts the binary public key to hex string, + * then hashes the string bytes. + */ + bin2hex(pubkey_hex, pubkey, TKEY_PUBKEY_SIZE); + + sha256_context ctx; + sha256_starts(&ctx); + sha256_update(&ctx, (const u8 *)pubkey_hex, TKEY_PUBKEY_SIZE * 2); + sha256_finish(&ctx, disk_key); - log_debug("Disk encryption key derived\n"); + log_debug("Disk encryption key derived using SHA256\n"); /* Generate verification hash if requested */ if (key_hash) { diff --git a/test/cmd/tkey.c b/test/cmd/tkey.c index 605ce070f0e..f2d52850d4e 100644 --- a/test/cmd/tkey.c +++ b/test/cmd/tkey.c @@ -53,8 +53,8 @@ static int cmd_test_tkey_sandbox(struct unit_test_state *uts) /* Test getkey command */ ut_assertok(run_command("tkey getkey testuss", 0)); ut_assert_nextline("Public Key: 505152535455565758595a5b5c5d5e5f505152535455565758595a5b5c5d5e5f"); - ut_assert_nextline("Disk Key: 228b2f6abf8be05649b2417586150bbf3e1b3f669afa1c6151ddc72957933c21"); - ut_assert_nextline("Verification Hash: a72a46b8f8c7ff0824416ada886f62b6c2808896d71201a32814ab432c7a81cf"); + ut_assert_nextline("Disk Key: e9b0599268ff8b083ef80dbd04be207ce9a19a60a888ccb3fe93710a0a70a34e"); + ut_assert_nextline("Verification Hash: 8583a08d6c534e84ae81a8518071c16a8030893df05fecb84e514438591ba5ed"); /* After getkey, device should be in app mode */ ut_assertok(run_command("tkey fwmode", 0)); diff --git a/test/dm/tkey.c b/test/dm/tkey.c index 9dffae66a5e..4ff5ecad7ea 100644 --- a/test/dm/tkey.c +++ b/test/dm/tkey.c @@ -175,19 +175,19 @@ static int dm_test_tkey_derive_disk_key(struct unit_test_state *uts) 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, }; - /* Expected disk key: BLAKE2b(pubkey) */ + /* Expected disk key: SHA256(pubkey) - from emulator */ const u8 expected_disk_key[TKEY_DISK_KEY_SIZE] = { - 0x22, 0x8b, 0x2f, 0x6a, 0xbf, 0x8b, 0xe0, 0x56, - 0x49, 0xb2, 0x41, 0x75, 0x86, 0x15, 0x0b, 0xbf, - 0x3e, 0x1b, 0x3f, 0x66, 0x9a, 0xfa, 0x1c, 0x61, - 0x51, 0xdd, 0xc7, 0x29, 0x57, 0x93, 0x3c, 0x21, + 0xe9, 0xb0, 0x59, 0x92, 0x68, 0xff, 0x8b, 0x08, + 0x3e, 0xf8, 0x0d, 0xbd, 0x04, 0xbe, 0x20, 0x7c, + 0xe9, 0xa1, 0x9a, 0x60, 0xa8, 0x88, 0xcc, 0xb3, + 0xfe, 0x93, 0x71, 0x0a, 0x0a, 0x70, 0xa3, 0x4e, }; /* Expected key hash: BLAKE2b(disk_key) */ const u8 expected_key_hash[TKEY_HASH_SIZE] = { - 0xa7, 0x2a, 0x46, 0xb8, 0xf8, 0xc7, 0xff, 0x08, - 0x24, 0x41, 0x6a, 0xda, 0x88, 0x6f, 0x62, 0xb6, - 0xc2, 0x80, 0x88, 0x96, 0xd7, 0x12, 0x01, 0xa3, - 0x28, 0x14, 0xab, 0x43, 0x2c, 0x7a, 0x81, 0xcf, + 0x85, 0x83, 0xa0, 0x8d, 0x6c, 0x53, 0x4e, 0x84, + 0xae, 0x81, 0xa8, 0x51, 0x80, 0x71, 0xc1, 0x6a, + 0x80, 0x30, 0x89, 0x3d, 0xf0, 0x5f, 0xec, 0xb8, + 0x4e, 0x51, 0x44, 0x38, 0x59, 0x1b, 0xa5, 0xed, }; struct udevice *dev; u8 dummy_app[128]; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> With driver model, -ENODEV has a specific meaning, i.e. there is no device. Return -EIO instead, since the device actually does exist in driver model, even if it is not currently connected. Remove a few error messages which we are here. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/misc/tkey_sandbox.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tkey_sandbox.c b/drivers/misc/tkey_sandbox.c index 9b26bd36f1b..27d3ffaf123 100644 --- a/drivers/misc/tkey_sandbox.c +++ b/drivers/misc/tkey_sandbox.c @@ -37,7 +37,7 @@ static int tkey_sandbox_read(struct udevice *dev, void *buffer, int len, int total, ret; if (priv->fd < 0) - return -ENODEV; + return -EIO; log_debug("Reading %d bytes...\n", len); @@ -78,7 +78,7 @@ static int tkey_sandbox_write(struct udevice *dev, const void *buffer, int len) int ret; if (priv->fd < 0) - return -ENODEV; + return -EIO; log_debug("Writing %d bytes:", len); for (int i = 0; i < len; i++) @@ -112,17 +112,17 @@ static int tkey_sandbox_probe(struct udevice *dev) /* Open the serial device */ priv->fd = os_open(priv->path, OS_O_RDWR); if (priv->fd < 0) { - log_err("Failed to open %s (error %d)\n", priv->path, priv->fd); + log_debug("Failed to open %s (error %d)\n", priv->path, priv->fd); free(priv->path); - return -ENODEV; + return -ENOENT; } /* Configure serial port for raw mode */ if (os_tty_set_params(priv->fd) < 0) { - log_err("Failed to configure serial port %s\n", priv->path); + log_debug("Failed to configure serial port %s\n", priv->path); os_close(priv->fd); free(priv->path); - return -ENODEV; + return -EIO; } log_debug("Connected to %s with serial parameters configured\n", priv->path); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Enhance the 'tkey connect' command to allow the device name to be specified. This will be useful in tests. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- cmd/tkey.c | 36 +++++++++++++++++++++++++++++++----- doc/usage/cmd/tkey.rst | 24 ++++++++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/cmd/tkey.c b/cmd/tkey.c index a269ca86085..3a31712990a 100644 --- a/cmd/tkey.c +++ b/cmd/tkey.c @@ -19,11 +19,18 @@ #include <linux/string.h> #include <linux/errno.h> +/* Static device pointer set by tkey connect command */ +static struct udevice *tkey_dev; + static struct udevice *tkey_get_device(void) { struct udevice *dev; int ret; + /* If a device was set by tkey connect, return it */ + if (tkey_dev) + return tkey_dev; + ret = uclass_first_device_err(UCLASS_TKEY, &dev); if (ret) { printf("No device found (err %dE)\n", ret); @@ -47,10 +54,28 @@ static int do_tkey_connect(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct udevice *dev; + int ret; - dev = tkey_get_device(); - if (!dev) - return CMD_RET_FAILURE; + /* Check if device name is provided as optional first argument */ + if (argc > 1) { + const char *dev_name = argv[1]; + + ret = uclass_get_device_by_name(UCLASS_TKEY, dev_name, &dev); + if (ret) { + printf("Failed to find TKey device '%s' (err %dE)\n", + dev_name, ret); + return CMD_RET_FAILURE; + } + } else { + ret = uclass_first_device_err(UCLASS_TKEY, &dev); + if (ret) { + printf("No device found (err %dE)\n", ret); + return CMD_RET_FAILURE; + } + } + + /* Set the static device pointer for subsequent commands */ + tkey_dev = dev; printf("Connected to TKey device\n"); @@ -276,7 +301,8 @@ static int do_tkey_loadapp(struct cmd_tbl *cmdtp, int flag, int argc, } U_BOOT_LONGHELP(tkey, - "connect - Connect to TKey device\n" + "connect [device-name] - Connect to TKey device\n" + " Optional device-name to connect to specific TKey device\n" "tkey fwmode - Check if device is in firmware or app mode\n" "tkey getkey <uss> [verify-hash] - Get disk encryption key\n" " Loads app with USS, derives key. Same USS always produces same key.\n" @@ -289,7 +315,7 @@ U_BOOT_LONGHELP(tkey, U_BOOT_CMD_WITH_SUBCMDS(tkey, "Tillitis TKey security token operations", tkey_help_text, - U_BOOT_SUBCMD_MKENT(connect, 1, 1, do_tkey_connect), + U_BOOT_SUBCMD_MKENT(connect, 2, 1, do_tkey_connect), U_BOOT_SUBCMD_MKENT(fwmode, 1, 1, do_tkey_fwmode), U_BOOT_SUBCMD_MKENT(getkey, 3, 1, do_tkey_getkey), U_BOOT_SUBCMD_MKENT(info, 1, 1, do_tkey_info), diff --git a/doc/usage/cmd/tkey.rst b/doc/usage/cmd/tkey.rst index 6a9f37354eb..b7d138f7307 100644 --- a/doc/usage/cmd/tkey.rst +++ b/doc/usage/cmd/tkey.rst @@ -11,7 +11,7 @@ Synopsis :: - tkey connect + tkey connect [device-name] tkey fwmode tkey getkey <uss> [verify-hash] tkey info @@ -46,8 +46,19 @@ internal UDI to generate deterministic encryption keys. tkey connect ~~~~~~~~~~~~ -Test connectivity to a TKey device. This command attempts to find and connect -to the first available TKey device in the system. +Test connectivity to a TKey device and optionally select a specific device for +subsequent commands. + +When called without arguments, this command connects to the first available TKey +device in the system. When a device name is provided, it connects to that +specific device. + +Once connected, the selected device is remembered and will be used by all +subsequent tkey commands (info, getkey, loadapp, etc.) until a different device +is selected with another connect command. + +device-name + Optional name of a specific TKey device to connect to tkey fwmode @@ -160,11 +171,16 @@ password Example ------- -Connect to device:: +Connect to the first available device:: => tkey connect Connected to TKey device +Connect to a specific device by name:: + + => tkey connect tkey@0 + Connected to TKey device + Check device mode:: => tkey fwmode -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> It is sometimes useful to use a real TKey even when running with the test devicetree. Put it first, so it becomes the default. Update tests to select the emulator explicitly. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/sandbox/dts/test.dts | 5 +++++ test/cmd/tkey.c | 7 +++---- test/dm/tkey.c | 23 ++++++++++++----------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 1d0ebc33387..7fef23a9285 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1124,6 +1124,11 @@ }; }; + tkey { + compatible = "sandbox,tkey"; + sandbox,device-path = "/dev/ttyACM0"; + }; + tkey-emul { compatible = "tkey,emul"; }; diff --git a/test/cmd/tkey.c b/test/cmd/tkey.c index f2d52850d4e..15ff5283b62 100644 --- a/test/cmd/tkey.c +++ b/test/cmd/tkey.c @@ -28,10 +28,9 @@ CMD_TEST(cmd_test_tkey_help, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); /* Test 'tkey' subcommands with emulator */ static int cmd_test_tkey_sandbox(struct unit_test_state *uts) { - struct udevice *dev; - - /* TKey device should be available in sandbox */ - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + /* Use tkey-emul for predictable test results */ + ut_assertok(run_command("tkey connect tkey-emul", 0)); + ut_assert_nextline("Connected to TKey device"); /* Test info command */ ut_assertok(run_command("tkey info", 0)); diff --git a/test/dm/tkey.c b/test/dm/tkey.c index 4ff5ecad7ea..5835e214f50 100644 --- a/test/dm/tkey.c +++ b/test/dm/tkey.c @@ -16,8 +16,9 @@ static int dm_test_tkey_find(struct unit_test_state *uts) { struct udevice *dev; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); ut_assertnonnull(dev); + ut_asserteq_str("tkey-emul", dev->name); return 0; } @@ -29,7 +30,7 @@ static int dm_test_tkey_get_udi(struct unit_test_state *uts) u8 udi[TKEY_UDI_SIZE]; struct udevice *dev; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); ut_assertok(tkey_get_udi(dev, udi)); @@ -54,7 +55,7 @@ static int dm_test_tkey_get_name_version(struct unit_test_state *uts) struct udevice *dev; u32 version; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Get name and version */ ut_assertok(tkey_get_name_version(dev, name0, name1, &version)); @@ -74,7 +75,7 @@ static int dm_test_tkey_in_app_mode(struct unit_test_state *uts) struct udevice *dev; int ret; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Check mode - should be in firmware mode initially */ ret = tkey_in_app_mode(dev); @@ -92,7 +93,7 @@ static int dm_test_tkey_load_app(struct unit_test_state *uts) u8 dummy_app[128]; int ret; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Create a dummy app */ memset(dummy_app, 0x42, sizeof(dummy_app)); @@ -118,7 +119,7 @@ static int dm_test_tkey_get_pubkey(struct unit_test_state *uts) u8 dummy_app[128]; int i; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Load a dummy app first */ memset(dummy_app, 0x42, sizeof(dummy_app)); @@ -149,7 +150,7 @@ static int dm_test_tkey_derive_wrapping_key(struct unit_test_state *uts) }; struct udevice *dev; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Derive wrapping key from password */ ut_assertok(tkey_derive_wrapping_key(dev, password, wrapping_key)); @@ -192,7 +193,7 @@ static int dm_test_tkey_derive_disk_key(struct unit_test_state *uts) struct udevice *dev; u8 dummy_app[128]; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Create a dummy signer app */ memset(dummy_app, 0x42, sizeof(dummy_app)); @@ -217,7 +218,7 @@ static int dm_test_tkey_udi_app_mode(struct unit_test_state *uts) struct udevice *dev; u8 dummy_app[128]; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Load an app to enter app mode */ memset(dummy_app, 0x42, sizeof(dummy_app)); @@ -240,7 +241,7 @@ static int dm_test_tkey_load_app_with_uss(struct unit_test_state *uts) u8 dummy_app[128]; const char *uss = "my_secret"; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Create a dummy app */ memset(dummy_app, 0x55, sizeof(dummy_app)); @@ -270,7 +271,7 @@ static int dm_test_tkey_read_write(struct unit_test_state *uts) u8 write_buf[129]; /* Header + command */ u8 read_buf[256]; - ut_assertok(uclass_first_device_err(UCLASS_TKEY, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TKEY, "tkey-emul", &dev)); /* Prepare a GET_NAME_VERSION command */ write_buf[0] = 0x10; /* Header: CMD, FIRMWARE endpoint */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The header and the extlinux image on mmc1 appear in several tests. Add a shared constant for this. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/boot/bootflow.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index e2de3af4f29..7baff4b0b0d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -39,6 +39,10 @@ extern U_BOOT_DRIVER(bootmeth_2script); /* Use this as the vendor for EFI to tell the app to exit boot services */ static u16 __efi_runtime_data test_vendor[] = u"U-Boot testing"; +/* comment test strings */ +#define HEADER "Seq Method State Uclass Part Name Filename" +#define EXT0 " 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf" + static int inject_response(struct unit_test_state *uts) { /* @@ -62,11 +66,11 @@ static int bootflow_cmd(struct unit_test_state *uts) ut_assert_console_end(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':"); - ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(EXT0); ut_assert_nextline("No more bootdevs"); ut_assert_nextlinen("---"); ut_assert_nextline("(1 bootflow, 1 valid)"); @@ -74,9 +78,9 @@ static int bootflow_cmd(struct unit_test_state *uts) ut_assertok(run_command("bootflow list", 0)); ut_assert_nextline("Showing bootflows for bootdev 'mmc1.bootdev'"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); - ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(EXT0); ut_assert_nextlinen("---"); ut_assert_nextline("(1 bootflow, 1 valid)"); ut_assert_console_end(); @@ -155,11 +159,11 @@ static int bootflow_cmd_glob(struct unit_test_state *uts) ut_assertok(run_command("bootflow scan -lGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':"); - ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(EXT0); ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':"); ut_assert_nextline("No more bootdevs"); ut_assert_nextlinen("---"); @@ -168,9 +172,9 @@ static int bootflow_cmd_glob(struct unit_test_state *uts) ut_assertok(run_command("bootflow list", 0)); ut_assert_nextline("Showing all bootflows"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); - ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(EXT0); ut_assert_nextlinen("---"); ut_assert_nextline("(1 bootflow, 1 valid)"); ut_assert_console_end(); @@ -187,7 +191,7 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assertok(run_command("bootmeth order ", 0)); ut_assertok(run_command("bootflow scan -aleGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); @@ -220,7 +224,7 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assertok(run_command("bootflow list", 0)); ut_assert_nextline("Showing all bootflows"); - ut_assert_nextline("Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); @@ -573,8 +577,7 @@ static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts) ut_assertok(bootmeth_set_order("efi firmware0")); ut_assertok(run_command("bootflow scan -lGH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); - ut_assert_nextline( - "Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextlinen("---"); ut_assert_nextline("(0 bootflows, 0 valid)"); @@ -582,8 +585,7 @@ static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts) ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_nextline("Scanning for bootflows in all bootdevs"); - ut_assert_nextline( - "Seq Method State Uclass Part Name Filename"); + ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning global bootmeth 'firmware0':"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); @@ -941,8 +943,7 @@ static int bootflow_cmd_hunt_label(struct unit_test_state *uts) ut_assert_nextline("Hunting with: mmc"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':"); - ut_assert_nextline( - " 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(EXT0); ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':"); ut_assert_skip_to_line("(1 bootflow, 1 valid)"); ut_assert_console_end(); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> We don't support storing the OS on an encrypted partition, but in some cases the root partition may be encrypted. Add an indication of this when listing the bootflows. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/bootflow.c | 5 +++-- cmd/bootflow.c | 15 ++++++++++++--- include/bootflow.h | 4 ++++ test/boot/bootflow.c | 44 +++++++++++++++++++++++--------------------- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index 30e0644a09e..0c389f78a28 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -108,11 +108,12 @@ static void report_bootflow_err(struct bootflow *bflow, int err) void bootflow_show(int index, struct bootflow *bflow, bool errors) { const char *name = bootflow_guess_label(bflow); + char enc_mark = (bflow->flags & BOOTFLOWF_ENCRYPTED) ? 'E' : ' '; - printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, + printf("%3x %-11s %-6s %-9.9s %4x %c %-25.25s %s\n", index, bflow->method ? bflow->method->name : "(none)", bootflow_state_get_name(bflow->state), name, bflow->part, - bflow->name, bflow->fname ?: ""); + enc_mark, bflow->name, bflow->fname ?: ""); if (errors) report_bootflow_err(bflow, bflow->err); } diff --git a/cmd/bootflow.c b/cmd/bootflow.c index caff52fcc7c..93c1acb4f51 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -20,13 +20,13 @@ static void show_header(void) { - printf("Seq Method State Uclass Part Name Filename\n"); - printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n"); + printf("Seq Method State Uclass Part E Name Filename\n"); + printf("--- ----------- ------ -------- ---- - ------------------------ ----------------\n"); } static void show_footer(int count, int num_valid) { - printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n"); + printf("--- ----------- ------ -------- ---- - ------------------------ ----------------\n"); printf("(%d bootflow%s, %d valid)\n", count, count != 1 ? "s" : "", num_valid); } @@ -370,6 +370,15 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, printf("Method: %s\n", bflow->method ? bflow->method->name : "(none)"); printf("State: %s\n", bootflow_state_get_name(bflow->state)); printf("Partition: %d\n", bflow->part); + + /* Show encryption status with LUKS version if applicable */ + if (IS_ENABLED(CONFIG_BLK_LUKS)) { + if (bflow->flags & BOOTFLOWF_ENCRYPTED) + printf("Encrypted: LUKSv%d\n", bflow->luks_version); + else + printf("Encrypted: no\n"); + } + printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)"); printf("Filename: %s\n", bflow->fname); printf("Buffer: "); diff --git a/include/bootflow.h b/include/bootflow.h index 3a5c7bce847..82ebef2c5c8 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -55,12 +55,14 @@ enum bootflow_state_t { * @BOOTFLOWF_USE_BUILTIN_FDT: Indicates that current bootflow uses built-in FDT * @BOOTFLOWF_FAKE_GO: Do a 'fake' boot, up to the last possible point, then * return + * @BOOTFLOWF_ENCRYPTED: Indicates that the partition is encrypted (e.g., LUKS) */ enum bootflow_flags_t { BOOTFLOWF_USE_PRIOR_FDT = BIT(0), BOOTFLOWF_STATIC_BUF = BIT(1), BOOTFLOWF_USE_BUILTIN_FDT = BIT(2), BOOTFLOWF_FAKE_GO = BIT(3), + BOOTFLOWF_ENCRYPTED = BIT(4), }; /** @@ -93,6 +95,7 @@ enum bootflow_flags_t { * @fdt_size: Size of FDT file * @fdt_addr: Address of loaded fdt * @flags: Flags for the bootflow (see enum bootflow_flags_t) + * @luks_version: LUKS version (1 or 2) if BOOTFLOWF_ENCRYPTED is set, else 0 * @cmdline: OS command line, or NULL if not known (allocated) * @x86_setup: Pointer to x86 setup block inside @buf, NULL if not present * @bootmeth_priv: Private data for the bootmeth @@ -118,6 +121,7 @@ struct bootflow { int fdt_size; ulong fdt_addr; int flags; + int luks_version; char *cmdline; void *x86_setup; void *bootmeth_priv; diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 7baff4b0b0d..a1390ad9a6d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -40,8 +40,8 @@ extern U_BOOT_DRIVER(bootmeth_2script); static u16 __efi_runtime_data test_vendor[] = u"U-Boot testing"; /* comment test strings */ -#define HEADER "Seq Method State Uclass Part Name Filename" -#define EXT0 " 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf" +#define HEADER "Seq Method State Uclass Part E Name Filename" +#define EXT0 " 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf" static int inject_response(struct unit_test_state *uts) { @@ -194,28 +194,28 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); - ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); - ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); - ut_assert_nextline(" 2 vbe media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 2 vbe media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':"); - ut_assert_nextline(" 3 extlinux media mmc 0 mmc1.bootdev.whole "); + ut_assert_nextline(" 3 extlinux media mmc 0 mmc1.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-2: No such file or directory"); - ut_assert_nextline(" 4 efi media mmc 0 mmc1.bootdev.whole "); + ut_assert_nextline(" 4 efi media mmc 0 mmc1.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-2: No such file or directory"); - ut_assert_nextline(" 5 vbe media mmc 0 mmc1.bootdev.whole "); + ut_assert_nextline(" 5 vbe media mmc 0 mmc1.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-2: No such file or directory"); - ut_assert_nextline(" 6 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_nextline(" 6 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); ut_assert_nextline( - " 7 efi fs mmc 1 mmc1.bootdev.part_1 /EFI/BOOT/%s", + " 7 efi fs mmc 1 mmc1.bootdev.part_1 /EFI/BOOT/%s", efi_get_basename()); ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':"); ut_assert_skip_to_line( - " 5f vbe media mmc 0 mmc0.bootdev.whole "); + " 5f vbe media mmc 0 mmc0.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); ut_assert_nextline("No more bootdevs"); ut_assert_nextlinen("---"); @@ -226,9 +226,9 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assert_nextline("Showing all bootflows"); ut_assert_nextline(HEADER); ut_assert_nextlinen("---"); - ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); - ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); - ut_assert_skip_to_line(" 5f vbe media mmc 0 mmc0.bootdev.whole "); + ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); + ut_assert_skip_to_line(" 5f vbe media mmc 0 mmc0.bootdev.whole "); ut_assert_nextlinen("---"); ut_assert_nextline("(96 bootflows, 1 valid)"); ut_assert_console_end(); @@ -253,6 +253,8 @@ static int bootflow_cmd_info(struct unit_test_state *uts) ut_assert_nextline("Method: extlinux"); ut_assert_nextline("State: ready"); ut_assert_nextline("Partition: 1"); + if (IS_ENABLED(CONFIG_BLK_LUKS)) + ut_assert_nextline("Encrypted: no"); ut_assert_nextline("Subdir: (none)"); ut_assert_nextline("Filename: /extlinux/extlinux.conf"); ut_assert_nextlinen("Buffer: "); @@ -503,7 +505,7 @@ static int bootflow_system(struct unit_test_state *uts) bootstd_clear_glob(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( - " 1 efi_mgr ready (none) 0 <NULL> "); + " 1 efi_mgr ready (none) 0 <NULL> "); ut_assert_skip_to_line("No more bootdevs"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); ut_assert_console_end(); @@ -1309,8 +1311,8 @@ static int bootflow_cros(struct unit_test_state *uts) ut_assert_nextlinen("Seq"); ut_assert_nextlinen("---"); ut_assert_nextlinen(" 0 extlinux"); - ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.part_2 "); - ut_assert_nextlinen(" 2 cros ready mmc 4 mmc5.bootdev.part_4 "); + ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.part_2 "); + ut_assert_nextlinen(" 2 cros ready mmc 4 mmc5.bootdev.part_4 "); ut_assert_nextlinen("---"); ut_assert_skip_to_line("(3 bootflows, 3 valid)"); @@ -1346,7 +1348,7 @@ static int bootflow_android_image_v4(struct unit_test_state *uts) ut_assert_nextlinen("Seq"); ut_assert_nextlinen("---"); ut_assert_nextlinen(" 0 extlinux"); - ut_assert_nextlinen(" 1 android ready mmc 0 mmc7.bootdev.whole "); + ut_assert_nextlinen(" 1 android ready mmc 0 mmc7.bootdev.whole "); ut_assert_nextlinen("---"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); @@ -1369,7 +1371,7 @@ static int bootflow_android_image_v2(struct unit_test_state *uts) ut_assert_nextlinen("Seq"); ut_assert_nextlinen("---"); ut_assert_nextlinen(" 0 extlinux"); - ut_assert_nextlinen(" 1 android ready mmc 0 mmc8.bootdev.whole "); + ut_assert_nextlinen(" 1 android ready mmc 0 mmc8.bootdev.whole "); ut_assert_nextlinen("---"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); @@ -1415,9 +1417,9 @@ static int bootflow_efi(struct unit_test_state *uts) ut_assert_nextlinen("---"); ut_assert_nextlinen(" 0 extlinux"); ut_assert_nextlinen( - " 1 efi ready usb 1 hub1.p2.usb_mass_storage. /EFI/BOOT/BOOTSBOX.EFI"); + " 1 efi ready usb 1 hub1.p2.usb_mass_storage. /EFI/BOOT/BOOTSBOX.EFI"); ut_assert_nextlinen( - " 2 extlinux ready usb 1 hub1.p4.usb_mass_storage. /extlinux/extlinux.conf"); + " 2 extlinux ready usb 1 hub1.p4.usb_mass_storage. /extlinux/extlinux.conf"); ut_assert_nextlinen("---"); ut_assert_skip_to_line("(3 bootflows, 3 valid)"); ut_assert_console_end(); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> We don't have an explicit indication of whether the root disk is encrypted or not. For now, try to detect it and set the flag if found. Signed-off-by: Simon Glass <simon.glass@canonical.com> --- boot/bootmeth_extlinux.c | 54 ++++++++++++++++++++++++++++++++++++++++ test/boot/bootflow.c | 38 ++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index 5a4fefbd868..0cc8c2bf9a5 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -17,9 +17,11 @@ #include <dm.h> #include <extlinux.h> #include <fs_legacy.h> +#include <luks.h> #include <malloc.h> #include <mapmem.h> #include <mmc.h> +#include <part.h> #include <pxe_utils.h> static int extlinux_get_state_desc(struct udevice *dev, char *buf, int maxsize) @@ -64,6 +66,54 @@ static int extlinux_check(struct udevice *dev, struct bootflow_iter *iter) return 0; } +/** + * extlinux_check_luks() - Check for LUKS encryption on other partitions + * + * This scans all partitions on the same device to check for LUKS encryption. + * If found, it marks this bootflow as encrypted since it likely boots from + * an encrypted root partition. + * + * @bflow: Bootflow to potentially mark as encrypted + * Return: 0 on success, -ve on error + */ +static int extlinux_check_luks(struct bootflow *bflow) +{ + struct blk_desc *desc; + struct disk_partition info; + int ret, part; + + if (!IS_ENABLED(CONFIG_BLK_LUKS) || !bflow->blk) + return 0; + + desc = dev_get_uclass_plat(bflow->blk); + if (!desc || !desc->bdev) + return 0; + + /* + * Check all partitions on this device for LUKS encryption. + * Typically partition 1 has the bootloader files and partition 2 + * has the encrypted root filesystem. Check up to 10 partitions. + */ + for (part = 1; part <= 10; part++) { + ret = part_get_info(desc, part, &info); + if (ret) + continue; /* Partition doesn't exist */ + + ret = luks_detect(desc->bdev, &info); + if (!ret) { + int luks_ver = luks_get_version(desc->bdev, &info); + + log_debug("LUKS partition %d detected (v%d), marking bootflow as encrypted\n", + part, luks_ver); + bflow->flags |= BOOTFLOWF_ENCRYPTED; + bflow->luks_version = luks_ver; + return 0; + } + } + + return 0; +} + /** * extlinux_fill_info() - Decode the extlinux file to find out its info * @@ -158,6 +208,10 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) if (ret) return log_msg_ret("inf", ret); + ret = extlinux_check_luks(bflow); + if (ret) + return log_msg_ret("luks", ret); + return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index a1390ad9a6d..55f73cda3ef 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -1770,3 +1770,41 @@ static int bootflow_extlinux_localboot(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_extlinux_localboot, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); + +/* Check 'bootflow info' with encrypted partition on mmc12 */ +static int bootflow_cmd_info_encrypted(struct unit_test_state *uts) +{ + /* Enable mmc12 which has LUKS encrypted partition and scan it */ + ut_assertok(scan_mmc_bootdev(uts, "mmc12", false)); + + /* Check for bootflows - should find one on mmc12 */ + ut_assertok(run_command("bootflow list", 0)); + ut_assert_nextline("Showing all bootflows"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextlinen(" 0 extlinux"); + ut_assert_nextline( + " 1 extlinux ready mmc 1 %c mmc12.bootdev.part_1 /extlinux/extlinux.conf", + IS_ENABLED(CONFIG_BLK_LUKS) ? 'E' : ' '); + ut_assert_nextline("--- ----------- ------ -------- ---- - ------------------------ ----------------"); + ut_assert_nextline("(2 bootflows, 2 valid)"); + ut_assert_console_end(); + + /* Select the mmc12 bootflow and check info shows encryption */ + ut_assertok(run_command("bootflow select 1", 0)); + ut_assert_console_end(); + ut_assertok(run_command("bootflow info", 0)); + ut_assert_nextline("Name: mmc12.bootdev.part_1"); + ut_assert_nextline("Device: mmc12.bootdev"); + ut_assert_nextline("Block dev: mmc12.blk"); + ut_assert_nextline("Method: extlinux"); + ut_assert_nextline("State: ready"); + ut_assert_nextline("Partition: 1"); + if (IS_ENABLED(CONFIG_BLK_LUKS)) + ut_assert_nextline("Encrypted: LUKSv2"); + ut_assert_skip_to_line("Error: 0"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootflow_cmd_info_encrypted, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); -- 2.43.0
participants (1)
-
Simon Glass