Files
u-boot/boot/bootctl/logic.c
Simon Glass a23e4407d5 luks: Correct condition for calling handle_encrypted()
Use CONFIG_BLK_LUKS instead of CONFIG_LUKS, which does not exist. This
allows the test to pass.

Signed-off-by: Simon Glass <simon.glass@canonical.com>
2025-12-08 05:22:18 -07:00

999 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Implementation of the logic to perform a boot
*
* Copyright 2025 Canonical Ltd
* Written by Simon Glass <simon.glass@canonical.com>
*/
#define LOG_CATEGORY UCLASS_BOOTCTL
#include <bootctl.h>
#include <dm.h>
#include <hexdump.h>
#include <log.h>
#include <luks.h>
#include <part.h>
#include <time.h>
#include <tkey.h>
#include <version.h>
#include <bootctl/logic.h>
#include <bootctl/measure.h>
#include <bootctl/oslist.h>
#include <bootctl/state.h>
#include <bootctl/ui.h>
#include <bootctl/util.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <u-boot/sha256.h>
enum {
COUNTDOWN_INTERVAL_MS = 1000, /* interval between autoboot updates */
ERROR_TIMEOUT_MS = 5000, /* timeout for error message display */
};
static int logic_prepare(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
/* figure out the UI to use */
ret = bootctl_get_dev(UCLASS_BOOTCTL_UI, &priv->ui);
if (ret) {
log_err("UI required but failed (err=%dE)\n", ret);
return log_msg_ret("bgd", ret);
}
/* figure out the measurement to use */
if (priv->opt_measure) {
ret = bootctl_get_dev(UCLASS_BOOTCTL_MEASURE,
&priv->meas);
if (ret) {
log_err("Measurement required but failed (err=%dE)\n",
ret);
return log_msg_ret("bgs", ret);
}
}
/* figure out at least one oslist driver to use */
ret = uclass_first_device_err(UCLASS_BOOTCTL_OSLIST, &priv->oslist);
if (ret)
return log_msg_ret("bgo", ret);
/* figure out the state to use */
ret = bootctl_get_dev(UCLASS_BOOTCTL_STATE, &priv->state);
if (ret)
return log_msg_ret("bgs", ret);
if (priv->opt_labels) {
ret = bootdev_set_order(priv->opt_labels);
if (ret)
return log_msg_ret("blo", ret);
}
/* Find TKey device if enabled (test can override this) */
if (priv->opt_tkey) {
ret = uclass_find_first_device(UCLASS_TKEY, &priv->tkey);
if (ret || !priv->tkey) {
log_debug("TKey not found at startup\n");
} else {
log_debug("TKey '%s'\n", priv->tkey->name);
/* Device found but not probed yet - not present */
priv->tkey_present = false;
}
}
return 0;
}
static int logic_start(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
if (priv->opt_persist_state) {
/* read in our state */
ret = bc_state_load(priv->state);
if (ret)
log_warning("Cannot load state, starting fresh (err=%dE)\n", ret);
else
priv->state_loaded = true;
}
ret = bc_ui_show(priv->ui);
if (ret) {
log_err("Cannot show display (err=%dE)\n", ret);
return log_msg_ret("bds", ret);
}
priv->start_time = get_timer(0);
if (priv->opt_autoboot) {
priv->next_countdown = COUNTDOWN_INTERVAL_MS;
priv->autoboot_remain_s = priv->opt_timeout;
priv->autoboot_active = true;
}
if (priv->opt_default_os)
bc_state_read_str(priv->state, "default", &priv->default_os);
if (priv->opt_measure) {
ret = bc_measure_start(priv->meas);
if (ret)
return log_msg_ret("pme", ret);
}
/* start scanning for OSes */
bc_oslist_setup_iter(&priv->iter);
priv->scanning = true;
return 0;
}
/**
* prepare_for_boot() - Get ready to boot an OS
*
* Intended to include at least:
* - A/B/recovery logic
* - persist the state
* - devicetree fix-up
* - measure images
*
* @dev: Bootctrl logic device
* @osinfo: OS to boot
* Return: 0 if OK, -ve on error
*/
static int prepare_for_boot(struct udevice *dev, struct osinfo *osinfo)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
if (priv->opt_track_success) {
ret = bc_state_write_bool(priv->state, "recordfail", true);
if (ret)
log_warning("Cannot set up recordfail (err=%dE)\n",
ret);
}
if (priv->opt_persist_state) {
ret = bc_state_save(priv->state);
if (ret)
log_warning("Cannot save state (err=%dE)\n", ret);
else
priv->state_saved = true;
}
/* devicetree fix-ups go here */
/* measure loaded images */
if (priv->opt_measure) {
struct alist result;
ret = bc_measure_process(priv->meas, osinfo, &result);
if (ret)
return log_msg_ret("pbp", ret);
show_measures(&result);
/* TODO: pass these measurements on to OS */
}
return 0;
}
/**
* read_images() - Read all the images needed to boot an OS
*
* @dev: Bootctrl logic device
* @osinfo: OS we intend to boot
* Return: 0 if OK, -ve on error
*/
static int read_images(struct udevice *dev, struct osinfo *osinfo)
{
struct bootflow *bflow = &osinfo->bflow;
int ret;
ret = bootflow_read_all(bflow);
if (ret)
return log_msg_ret("rea", ret);
log_debug("Images read: %d\n", bflow->images.count);
return 0;
}
/**
* show_unlock_error() - Display error message and update UI state
*
* Helper function to show an error message, display error state,
* and hide the pass prompt.
*
* @priv: Logic private data
* @seq: Sequence number of the selected OS
* @msg: Error message to display
* Return: 0 if OK, -ve on error
*/
static int show_unlock_error(struct logic_priv *priv, int seq, const char *msg)
{
int ret;
ret = bc_ui_set_pass_msg(priv->ui, seq, msg);
if (ret && ret != -ENOSYS)
return log_msg_ret("sem", ret);
ret = bc_ui_show_pass_msg(priv->ui, seq, true);
if (ret && ret != -ENOSYS)
return log_msg_ret("see", ret);
ret = bc_ui_show_pass(priv->ui, seq, false);
if (ret)
return log_msg_ret("hsp", ret);
return 0;
}
/**
* start_tkey_load() - Start TKey app loading
*
* Starts the app loading process. Expects TKey device to be already probed
* and present.
*
* @dev: Logic device
* @pass: User passphrase to use as USS
* Return: 0 on success or if needs replug (state set appropriately),
* -ve on error
*/
static int start_tkey_load(struct udevice *dev, const char *pass)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
ret = tkey_load_start(&priv->tkey_load_ctx, priv->tkey,
(const u8 *)__signer_1_0_0_begin,
TKEY_SIGNER_SIZE, (const u8 *)pass, strlen(pass));
if (ret)
return ret;
log_debug("Started TKey app loading (%zx bytes)\n", TKEY_SIGNER_SIZE);
return 0;
}
/**
* derive_tkey_disk_key() - Derive disk encryption key from TKey public key
*
* Gets the public key from the TKey and derives the disk encryption key
* using SHA256. Must match the Python tkey-fde-key.py implementation.
*
* @dev: Logic device
* Return: 0 on success, -ve on error
*/
static int derive_tkey_disk_key(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
u8 pubkey[TKEY_PUBKEY_SIZE];
char pubkey_hex[TKEY_PUBKEY_SIZE * 2 + 1];
sha256_context ctx;
int ret;
/* Get public key from the loaded app */
ret = tkey_get_pubkey(priv->tkey, pubkey);
if (ret) {
log_warning("Failed to get TKey public key (err=%dE)\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_starts(&ctx);
sha256_update(&ctx, (const u8 *)pubkey_hex, TKEY_PUBKEY_SIZE * 2);
sha256_finish(&ctx, priv->tkey_disk_key);
log_info("TKey disk key derived successfully\n");
return 0;
}
/**
* perform_tkey_unlock() - Perform TKey-based LUKS unlock
*
* This function performs LUKS unlock using the TKey-derived key as binary
* passphrase material. Expects TKey to be already loaded and key derived.
*
* @dev: Logic device
* @os: OS information containing the encrypted bootflow
* @seq: Sequence number of the selected OS
* @master_key: Buffer to store the unlocked master key
* @key_size: Pointer to key size (input: buffer size, output: actual size)
* Return: 0 if unlock succeeded, -ENOENT if there is no encrypted partition,
* other -ve on other error
*/
static int perform_tkey_unlock(struct udevice *dev, struct osinfo *os, int seq,
u8 *master_key, u32 *key_sizep)
{
struct logic_priv *priv = dev_get_priv(dev);
struct disk_partition pinfo;
int ret;
/* TKey key should already be derived at this point */
assert(priv->ustate == UNS_TKEY_UNLOCK);
/* Get partition info for the encrypted partition (next after boot) */
ret = part_get_info(dev_get_uclass_plat(os->bflow.blk),
os->bflow.part + 1, &pinfo);
if (ret) {
log_warning("Failed to get partition info (err=%dE)\n", ret);
return -ENOENT;
}
/*
* Use TKey-derived key as binary passphrase input to LUKS KDF
* The key is treated as binary passphrase material that will be
* processed by PBKDF2/Argon2 just like a text passphrase would be.
* This matches how cryptsetup --key-file works.
*/
log_info("Using LUKS unlock with binary passphrase\n");
ret = luks_unlock(os->bflow.blk, &pinfo, priv->tkey_disk_key,
TKEY_DISK_KEY_SIZE, false, master_key, key_sizep);
if (ret)
return log_msg_ret("htu", ret);
return 0;
}
/**
* handle_idle() - Set up the passphrase prompt UI
*
* This handles the UNS_IDLE state, showing the passphrase prompt and
* transitioning to UNS_WAITING_PASS.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on success, -ve on error
*/
static int handle_idle(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
/* show passphrase prompt and hide any error */
ret = bc_ui_show_pass_msg(priv->ui, seq, false);
if (ret && ret != -ENOSYS)
return log_msg_ret("hse", ret);
ret = bc_ui_show_pass(priv->ui, seq, true);
if (ret)
return log_msg_ret("lsp", ret);
priv->ustate = UNS_WAITING_PASS;
priv->selected_seq = seq;
priv->refresh = true;
return 0;
}
/**
* handle_waiting_pass() - Handle waiting for passphrase entry
*
* This handles the UNS_WAITING_PASS state, getting the passphrase and
* showing the "Unlocking..." message, then transitioning to either
* UNS_UNLOCK_NORMAL or UNS_TKEY_START based on configuration.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on success, -ve on error
*/
static int handle_waiting_pass(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
const char *pass;
int ret;
/* Get pass and show "Unlocking..." message */
ret = bc_ui_get_pass(priv->ui, seq, &pass);
if (ret) {
log_warning("Failed to get pass (err=%dE)\n", ret);
priv->ustate = UNS_IDLE;
return -EAGAIN; /* Return to menu */
}
/* Show "Unlocking..." message */
ret = bc_ui_set_pass_msg(priv->ui, seq, "Unlocking...");
if (ret && ret != -ENOSYS)
return log_msg_ret("spu", ret);
ret = bc_ui_show_pass_msg(priv->ui, seq, true);
if (ret && ret != -ENOSYS)
return log_msg_ret("ssu", ret);
ret = bc_ui_show_pass(priv->ui, seq, false);
if (ret)
return log_msg_ret("hsp", ret);
/* Select unlock path based on TKey option */
if (priv->opt_tkey)
priv->ustate = UNS_TKEY_START;
else
priv->ustate = UNS_UNLOCK_NORMAL;
priv->refresh = true;
return 0;
}
/**
* handle_unlock_normal() - Perform normal LUKS unlock with direct passphrase
*
* This handles the UNS_UNLOCK_NORMAL state, performing LUKS unlock
* with direct passphrase.
*
* @dev: Logic device
* @os: OS information
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_unlock_normal(struct udevice *dev, struct osinfo *os, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
struct disk_partition pinfo;
u8 master_key[128];
u32 key_size = sizeof(master_key);
const char *pass;
int ret;
/* Get pass for direct LUKS unlock */
ret = bc_ui_get_pass(priv->ui, seq, &pass);
if (ret) {
log_warning("Failed to get pass (err=%dE)\n", ret);
priv->ustate = UNS_IDLE;
return 0;
}
/* Get partition info for the encrypted partition (next after boot) */
ret = part_get_info(dev_get_uclass_plat(os->bflow.blk),
os->bflow.part + 1, &pinfo);
if (ret) {
log_warning("Failed to get partition info (err=%dE)\n", ret);
priv->ustate = UNS_IDLE;
return 0;
}
/* Try to unlock with the pass */
ret = luks_unlock(os->bflow.blk, &pinfo, (const u8 *)pass, strlen(pass),
false, master_key, &key_size);
/* Store result and transition to result handling state */
priv->unlock_result = ret;
priv->ustate = UNS_UNLOCK_RESULT;
priv->refresh = true;
return 0;
}
/**
* handle_tkey_wait_remove() - Handle TKey removal wait state
*
* This handles the UNS_TKEY_WAIT_REMOVE state, waiting for TKey to be
* physically removed and then transitioning to UNS_TKEY_WAIT_INSERT.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_wait_remove(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
/* Check if TKey is still present by checking app mode */
ret = tkey_in_app_mode(priv->tkey);
if (ret < 0) {
/* TKey removed (error accessing device) */
log_debug("TKey removed, cleaning up device\n");
device_remove(priv->tkey, DM_REMOVE_NORMAL);
priv->tkey_present = false;
log_debug("TKey removed, ready for next attempt\n");
/* Show replug message */
ret = show_unlock_error(priv, seq, "Please insert TKey");
if (ret)
return log_msg_ret("rpe", ret);
priv->refresh = true;
priv->ustate = UNS_TKEY_WAIT_INSERT;
return 0;
}
return 0;
}
/**
* handle_tkey_start() - Handle TKey unlock start
*
* Checks if TKey device is available and transitions to wait for insert state.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_start(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
/* Check if TKey device is available */
if (!priv->tkey) {
log_err("TKey device not found\n");
show_unlock_error(priv, seq, "TKey not found");
return -ENODEV;
}
priv->ustate = UNS_TKEY_WAIT_INSERT;
return 0;
}
/**
* handle_tkey_wait_insert() - Handle TKey insertion wait state
*
* This handles the UNS_TKEY_WAIT_INSERT state, probing for TKey device
* and transitioning to UNS_TKEY_INSERTED when found.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: -EAGAIN to continue waiting or transition to next state
*/
static int handle_tkey_wait_insert(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
log_debug("Probing TKey device\n");
ret = device_probe(priv->tkey);
if (ret) {
/* Probe failed - device not yet inserted */
log_debug("TKey not inserted yet, waiting\n");
priv->ustate = UNS_TKEY_WAIT_INSERT;
return 0;
}
/* Probe succeeded - device is present */
log_debug("TKey probed successfully\n");
priv->tkey_present = true;
priv->ustate = UNS_TKEY_INSERTED;
return 0;
}
/**
* handle_tkey_inserted() - Handle TKey inserted state
*
* This handles the UNS_TKEY_INSERTED state, starting the TKey app loading
* process with the user's passphrase.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_inserted(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
const char *pass;
int ret;
/* Get passphrase for TKey derivation */
ret = bc_ui_get_pass(priv->ui, seq, &pass);
if (ret) {
log_warning("Failed to get pass (err=%dE)\n", ret);
priv->ustate = UNS_IDLE;
return 0; /* Return to menu */
}
/* Start loading TKey app with USS */
ret = start_tkey_load(dev, pass);
if (ret == -ENOTSUPP) {
/* TKey in app mode, needs to be replugged */
log_debug("TKey not in firmware mode, needs replug\n");
priv->ustate = UNS_TKEY_WAIT_REMOVE;
/* Show replug message */
ret = show_unlock_error(priv, seq, "Please remove TKey");
if (ret)
return log_msg_ret("rpe", ret);
priv->refresh = true;
return 0;
} else if (ret) {
log_warning("Failed to start TKey app load (err=%dE)\n", ret);
return ret;
}
priv->ustate = UNS_TKEY_LOADING;
return 0;
}
/**
* handle_tkey_loading() - Handle TKey app loading state
*
* This handles the UNS_TKEY_LOADING state, sending blocks of the TKey app
* and transitioning to UNS_TKEY_READY when complete.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_loading(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
/* Send 1 block per poll to keep the UI responsive */
ret = tkey_load_next(&priv->tkey_load_ctx, 1);
if (ret == -EAGAIN) {
char msg[64];
int percent;
/* More blocks to send */
log_debug("TKey loading: %u/%u bytes\n",
priv->tkey_load_ctx.offset,
priv->tkey_load_ctx.app_size);
/* Show loading progress - round up so 100% will show */
percent = ((priv->tkey_load_ctx.offset + 1) * 100 +
priv->tkey_load_ctx.app_size - 1) /
priv->tkey_load_ctx.app_size;
if (percent > 100)
percent = 100;
snprintf(msg, sizeof(msg), "Preparing TKey... %d%%", percent);
bc_ui_set_pass_msg(priv->ui, seq, msg);
priv->refresh = true;
return 0;
}
if (ret) {
log_warning("Failed to load TKey app (err=%dE)\n", ret);
priv->ustate = UNS_TKEY_START;
return ret;
}
/* Loading complete, now derive disk key */
log_info("TKey app loaded successfully, deriving disk key\n");
priv->ustate = UNS_TKEY_READY;
return 0;
}
/**
* handle_tkey_ready() - Handle TKey ready state
*
* This handles the UNS_TKEY_READY state, deriving the disk encryption key
* from the TKey public key and transitioning to IN_PROGRESS state.
*
* @dev: Logic device
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_ready(struct udevice *dev, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
int ret;
/* Derive disk encryption key from TKey public key */
ret = derive_tkey_disk_key(dev);
if (ret)
return ret;
bc_ui_set_pass_msg(priv->ui, seq, "Unlocking...");
priv->refresh = true;
/* Key derived, start unlock */
priv->ustate = UNS_TKEY_UNLOCK;
return 0;
}
/**
* handle_tkey_unlock() - Handle TKey-based LUKS unlock state
*
* This handles the UNS_TKEY_UNLOCK state, performing LUKS unlock with
* TKey-derived key and transitioning to UNS_UNLOCK_RESULT.
*
* @dev: Logic device
* @os: OS information
* @seq: Sequence number of the selected OS
* Return: 0 on normal operation, -ve on error
*/
static int handle_tkey_unlock(struct udevice *dev, struct osinfo *os, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
u8 master_key[128];
u32 key_size = sizeof(master_key);
int ret;
ret = perform_tkey_unlock(dev, os, seq, master_key, &key_size);
/* Store result and transition to result handling state */
priv->unlock_result = ret;
priv->ustate = UNS_UNLOCK_RESULT;
priv->refresh = true;
return 0;
}
/**
* handle_unlock_result() - Handle the result of unlock operation
*
* Processes unlock result, showing either error or success message.
* On error, shows "Incorrect passphrase" and transitions to UNS_BAD_PASS.
* On success, shows "Unlock successful" and transitions to UNS_OK.
*
* @priv: Logic private data
* @seq: Sequence number of the selected OS
* @unlock_ret: Return value from unlock operation
* Return: -EAGAIN always (to wait for next poll)
*/
static int handle_unlock_result(struct logic_priv *priv, int seq,
int unlock_ret)
{
int ret;
if (unlock_ret) {
log_warning("Failed to unlock LUKS partition (err=%dE)\n",
unlock_ret);
/* Set and show error message, hide pass prompt */
ret = show_unlock_error(priv, seq, "Incorrect passphrase");
if (ret)
return log_msg_ret("ipe", ret);
/* Display error for 5 seconds before allowing retry */
priv->time_error = get_timer(0);
priv->ustate = UNS_BAD_PASS;
priv->refresh = true;
return 0;
}
log_info("LUKS partition unlocked successfully\n");
/* Set and show success message briefly, hide pass prompt */
ret = show_unlock_error(priv, seq, "Unlock successful");
if (ret)
return log_msg_ret("suc", ret);
/* Show success message for one poll cycle, then boot */
priv->ustate = UNS_OK;
priv->refresh = true;
/* TODO: Create blkmap device for decrypted access */
return 0;
}
static int handle_encrypted_tkey(struct udevice *dev, struct osinfo *os,
int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
if (!IS_ENABLED(CONFIG_TKEY))
return -ENOSYS;
switch (priv->ustate) {
case UNS_TKEY_WAIT_REMOVE:
return handle_tkey_wait_remove(dev, seq);
case UNS_TKEY_START:
return handle_tkey_start(dev, seq);
case UNS_TKEY_WAIT_INSERT:
return handle_tkey_wait_insert(dev, seq);
case UNS_TKEY_INSERTED:
return handle_tkey_inserted(dev, seq);
case UNS_TKEY_LOADING:
return handle_tkey_loading(dev, seq);
case UNS_TKEY_READY:
return handle_tkey_ready(dev, seq);
case UNS_TKEY_UNLOCK:
return handle_tkey_unlock(dev, os, seq);
default:
return -EINVAL;
}
}
static int handle_encrypted(struct udevice *dev, struct osinfo *os, int seq)
{
struct logic_priv *priv = dev_get_priv(dev);
switch (priv->ustate) {
case UNS_IDLE:
return handle_idle(dev, seq);
case UNS_WAITING_PASS:
return handle_waiting_pass(dev, seq);
case UNS_UNLOCK_NORMAL:
return handle_unlock_normal(dev, os, seq);
case UNS_BAD_PASS:
case UNS_OK:
/* These states shouldn't reach here in normal flow */
return -EINVAL;
case UNS_UNLOCK_RESULT:
return handle_unlock_result(priv, seq, priv->unlock_result);
default:
return handle_encrypted_tkey(dev, os, seq);
}
}
static int logic_poll(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
struct osinfo info;
bool selected;
int ret, seq;
/* scan for the next OS, if any */
if (priv->scanning) {
ret = bc_oslist_next(priv->oslist, &priv->iter, &info);
if (!ret) {
ret = bc_ui_add(priv->ui, &info);
if (ret)
return log_msg_ret("bda", ret);
priv->refresh = true;
} else {
/* No more OSes from this driver, try the next */
ret = uclass_next_device_err(&priv->oslist);
if (ret)
priv->scanning = false;
else
memset(&priv->iter, '\0',
sizeof(struct oslist_iter));
}
}
/* if unlock succeeded - show a message and boot on the next poll */
if (priv->ustate == UNS_OK) {
/* Success message is set, prepare to boot after rendering */
priv->ustate = UNS_IDLE;
priv->ready_to_boot = true;
priv->refresh = true;
}
/* Check if error message display timeout has expired */
if (priv->ustate == UNS_BAD_PASS && priv->time_error &&
get_timer(priv->time_error) > ERROR_TIMEOUT_MS) {
/* Hide error message and allow retry */
ret = bc_ui_show_pass_msg(priv->ui, priv->selected_seq, false);
if (ret && ret != -ENOSYS)
return log_msg_ret("hse", ret);
priv->time_error = 0;
priv->ustate = UNS_IDLE;
priv->refresh = true;
}
if (priv->autoboot_active &&
get_timer(priv->start_time) > priv->next_countdown) {
ulong secs = get_timer(priv->start_time) / 1000;
priv->autoboot_remain_s = secs >= priv->opt_timeout ? 0 :
max(priv->opt_timeout - secs, 0ul);
priv->next_countdown += COUNTDOWN_INTERVAL_MS;
priv->refresh = true;
}
if (!priv->opt_slow_refresh || priv->refresh) {
ret = bc_ui_render(priv->ui);
if (ret)
return log_msg_ret("bdr", ret);
priv->refresh = false;
}
ret = bc_ui_poll(priv->ui, &seq, &selected);
if (ret < 0)
return log_msg_ret("bdo", ret);
else if (ret)
priv->refresh = true;
/* Ignore menu selection while displaying error message */
if (selected && priv->ustate == UNS_BAD_PASS)
selected = false;
if (!selected && priv->autoboot_active && !priv->autoboot_remain_s &&
seq >= 0) {
log_info("Selecting %d due to timeout\n", seq);
selected = true;
}
/* If ready to unlock, trigger selection to continue unlock process */
if (!selected && priv->ustate != UNS_WAITING_PASS &&
priv->ustate != UNS_IDLE && priv->ustate != UNS_BAD_PASS) {
seq = priv->selected_seq;
selected = true;
log_debug("Continuing unlock for seq %d\n", seq);
}
if (selected) {
struct osinfo *os;
os = alist_getw(&priv->osinfo, seq, struct osinfo);
if (!os)
return log_msg_ret("gos", -ENOENT);
/* If encrypted, handle pass entry and unlock */
if (IS_ENABLED(CONFIG_BLK_LUKS) &&
(os->bflow.flags & BOOTFLOWF_ENCRYPTED)) {
ret = handle_encrypted(dev, os, seq);
if (ret)
return ret;
}
priv->ready_to_boot = false;
priv->selected_seq = seq;
}
if (priv->ready_to_boot) {
struct osinfo *os;
seq = priv->selected_seq;
os = alist_getw(&priv->osinfo, seq, struct osinfo);
if (!os)
return log_msg_ret("gbo", -ENOENT);
log_info("Selected %d: %s\n", seq, os->bflow.os_name);
priv->ready_to_boot = false;
/*
* try to read the images first; some methods don't support
* this
*/
ret = read_images(dev, os);
if (ret && ret != -ENOSYS)
return log_msg_ret("lri", ret);
ret = prepare_for_boot(dev, os);
if (ret)
return log_msg_ret("lpb", ret);
/* boot OS */
ret = bootflow_boot(&os->bflow);
if (ret)
log_warning("Boot failed (err=%dE)\n", ret);
return -ESHUTDOWN;
}
return 0;
}
static int logic_of_to_plat(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
ofnode node = ofnode_find_subnode(dev_ofnode(dev), "options");
priv->opt_persist_state = ofnode_read_bool(node, "persist-state");
priv->opt_default_os = ofnode_read_bool(node, "default-os");
ofnode_read_u32(node, "timeout", &priv->opt_timeout);
priv->opt_skip_timeout = ofnode_read_bool(node,
"skip-timeout-on-success");
priv->opt_track_success = ofnode_read_bool(node, "track-success");
priv->opt_labels = ofnode_read_string(node, "labels");
priv->opt_autoboot = ofnode_read_bool(node, "autoboot");
priv->opt_measure = ofnode_read_bool(node, "measure");
priv->opt_slow_refresh = ofnode_read_bool(node, "slow-refresh");
priv->opt_tkey = ofnode_read_bool(node, "tkey");
return 0;
}
static int logic_probe(struct udevice *dev)
{
struct logic_priv *priv = dev_get_priv(dev);
alist_init_struct(&priv->osinfo, struct osinfo);
return 0;
}
static struct bc_logic_ops ops = {
.prepare = logic_prepare,
.start = logic_start,
.poll = logic_poll,
};
static const struct udevice_id logic_ids[] = {
{ .compatible = "bootctl,ubuntu-desktop" },
{ .compatible = "bootctl,logic" },
{ }
};
U_BOOT_DRIVER(bc_logic) = {
.name = "bc_logic",
.id = UCLASS_BOOTCTL,
.of_match = logic_ids,
.ops = &ops,
.of_to_plat = logic_of_to_plat,
.probe = logic_probe,
.priv_auto = sizeof(struct logic_priv),
};