Compare commits

...

17 Commits

Author SHA1 Message Date
Simon Glass
2f597f1951 efi: mouse: Scale the pointer to the display
If a display is provided to the mouse uclass, use it to scale the coords
returned by the absolute-pointer protocol.

Series-to: concept
Series-cc: heinrich
Cover-letter:
expo: Complete mouse operation in the EFI app
This series includes various improvements which allow the mouse to be
used when running as an EFI app.

In particular:
- support for the absolute-pointer protocol, since this provides better
  integration when running under QEMU
- input tweaks to improve performance under QEMU

It also includes some x86-specific fixes for i8042 and MTRRs.

Finally, a new --bootcmd option is added to the build-qemu script to
allow passing a boot command to U-Boot.

This series is part F
END

Signed-off-by: Simon Glass <sjg@chromium.org>
Series-links: 1:48
2025-10-07 05:05:14 -06:00
Simon Glass
c4b73a8845 efi: mouse: Add support for an absolute pointer
The absolute-pointer protocol is useful particularly with QEMU since it
allows the position to be consistent between the host and the guest.

Add support for this new protocol and use it in preference to the simple
one, when both are available.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
af43cdae92 efi: Add definitions for the absolute-pointer protocol
Add these definitions to the API so that we can make use of this feature
in drivers, etc.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
b3025106c8 efi: mouse: Split out event handling further
Create a function which handles getting the pointer position using the
simple-pointer protocol. Call this from efi_mouse_get_event()

Return immediately if there is a button event. Otherwise return a motion
event if motion is detected.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
1d6f09612f efi: mouse: Move button handling into a function
Split out the code that handles button presses into its own function,
since efi_mouse_get_event() is already quite long.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
9b27554158 efi: mouse: Move simple-pointer code into a function
In preparation for supporting an absolute pointer, move the setup of
the simple pointer into its own function. Rename 'pointer' to simple
and last_state to simple_last. Take this opportunity to add comments
for struct efi_mouse_priv since it will be less obvious once the new
support is added.

Clean up some unnecessary cruft in the remove() path.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
207aec39f1 efi: video: Add some more debugging for the modes
This driver can operate in various modes, e.g. with VIDEO_COPY and with
different colour depths or in blit mode. Add some debugging to help
debug problems on particular boards. Fix an errant blank line while we
are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
ab57a5483f efi: serial: Speed up reading from input
The serial driver is currently quite slow since it sets an event and
waits for at least 1ms for a keypress, which may never come.

On real hardware, the keyboard does not work correctly wihout waiting
for events. Reduce the time to 10us which should be enough to provide an
opportunity for event processing.

QEMU does not actually have this problem, so detect that and skip the
processing altogether.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
2a75f77d66 efi: app: Detect running under QEMU
When the app is running under QEMU we may wish to do some things
differently. Add a flag for this and use the SMBIOS tables to detect it.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
c424223703 efi: app: Support reading SMBIOS3 tables
Check for both GUIDs when looking for the SMBIOS tables. This allows
both table versions to be detected when running from OVMF.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
56c582392d x86: efi: Disable i8042 in the x86 app
We should not try to directly access the keyboard when running as an EFI
app. Disable CONFIG_I8042_KEYB option to prevent that.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
e32fa61bf8 x86: efi: Avoid setting MTRRs in the app
The MTRRs have already been set by the previous phase so we should not
set them when the U-Boot app starts. Add a condition to prevent it.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
f0b69eca76 expo: Speed up polling the keyboard
The current algorithm spends 10ms or more looking for keyboard input.
Since we expect the frame rate to be 50 or more, it should be OK to only
check once.

Handle the timeout by recording the timestamp of the last key and timing
out the CLI entry after 10ms. This allows an 'Escape' key to work,
rather than it just waiting forever for a terminal sequence that starts
with escape.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
13e19af370 scripts: build-qemu: Add --bootcmd option to pass bootcmd via fw_cfg
Add a --bootcmd option to the build-qemu script that allows passing a
boot command to QEMU via fw_cfg. The bootcmd is written to the
"opt/u-boot/bootcmd" fw_cfg entry, which can be read by U-Boot's
EVT_BOOTCMD handler.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
4426d0e25e x86: qemu: Add EVT_BOOTCMD handler to get bootcmd from fw_cfg
Implement an EVT_BOOTCMD event handler that reads the bootcmd from
QEMU's fw_cfg interface using the "opt/u-boot/bootcmd" file. This
allows QEMU to provide a custom boot command to U-Boot at runtime.

Uses the newly exported qfw_locate_file() function to locate and
read the bootcmd file.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
4e8f519f66 event: Add EVT_BOOTCMD event for custom boot commands
Add a new event that is triggered in main_loop() before
autoboot_command(). This allows platform code to provide a custom
bootcmd string via the event system.

The event uses struct event_bootcmd which provides a buffer for the
bootcmd string and its size. Platform event handlers can write their
custom bootcmd to this buffer.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
Simon Glass
720884c68f qfw: Export qfw_locate_file() for external use
Export the qfw_locate_file() function to allow other modules to locate
files in the QEMU firmware config without loading them into memory.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-10-07 05:05:14 -06:00
20 changed files with 582 additions and 138 deletions

View File

@@ -4,6 +4,9 @@
*/
#include <cpu_func.h>
#include <env.h>
#include <errno.h>
#include <event.h>
#include <init.h>
#include <pci.h>
#include <qfw.h>
@@ -150,3 +153,31 @@ 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

View File

@@ -29,7 +29,8 @@ int init_cache_f_r(void)
* the MTRRs here
*/
do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) &&
!IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER);
!IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER) &&
!IS_ENABLED(CONFIG_EFI_APP);
if (do_mtrr) {
ret = mtrr_commit(false);

View File

@@ -16,6 +16,7 @@
#include <mapmem.h>
#include <menu.h>
#include <mouse.h>
#include <time.h>
#include <video.h>
#include <watchdog.h>
#include <linux/delay.h>
@@ -45,6 +46,7 @@ int expo_new(const char *name, void *priv, struct expo **expp)
INIT_LIST_HEAD(&exp->str_head);
exp->next_id = EXPOID_BASE_ID;
cli_ch_init(&exp->cch);
exp->last_key_ms = get_timer(0);
*expp = exp;
@@ -488,21 +490,19 @@ static int poll_keys(struct expo *exp)
ichar = cli_ch_process(&exp->cch, 0);
if (!ichar) {
int i;
for (i = 0; i < 10 && !ichar && !tstc(); i++) {
schedule();
mdelay(2);
ichar = cli_ch_process(&exp->cch, -ETIMEDOUT);
}
while (!ichar && tstc()) {
/* Check once for available input */
if (tstc()) {
ichar = getchar();
ichar = cli_ch_process(&exp->cch, ichar);
}
if (!ichar && get_timer(exp->last_key_ms) >= 10)
ichar = cli_ch_process(&exp->cch, -ETIMEDOUT);
}
key = 0;
if (ichar) {
exp->last_key_ms = get_timer(0);
key = bootmenu_conv_key(ichar);
if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA)
key = ichar;

View File

@@ -49,6 +49,7 @@ const char *const type_name[] = {
/* main loop events */
"main_loop",
"bootcmd",
/* booting */
"boot_os_addr",

View File

@@ -14,6 +14,7 @@
#include <command.h>
#include <console.h>
#include <env.h>
#include <event.h>
#include <fdtdec.h>
#include <init.h>
#include <net.h>
@@ -38,10 +39,35 @@ static void run_preboot_environment_command(void)
}
}
static const char *get_autoboot_cmd(char *buf, int size)
{
const char *s = NULL;
if (IS_ENABLED(CONFIG_EVENT)) {
struct event_bootcmd event_bootcmd;
int ret;
event_bootcmd.bootcmd = buf;
event_bootcmd.size = size;
buf[0] = '\0';
ret = event_notify(EVT_BOOTCMD, &event_bootcmd,
sizeof(event_bootcmd));
if (ret)
return NULL;
if (buf[0] != '\0')
s = buf;
}
return s;
}
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
char bootcmd_buf[CONFIG_SYS_CBSIZE];
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
@@ -64,7 +90,11 @@ void main_loop(void)
process_button_cmds();
s = bootdelay_process();
/* Allow platform code to provide bootcmd via event */
s = get_autoboot_cmd(bootcmd_buf, sizeof(bootcmd_buf));
if (!s)
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);

View File

@@ -64,6 +64,14 @@ Once configured, you can build and run QEMU for arm64 like this::
scripts/build-qemu -rsw
To pass a custom boot command to U-Boot via fw_cfg, use the `--bootcmd`
option::
scripts/build-qemu -rsw --bootcmd "echo Hello from QEMU; bootflow scan -lb"
This will cause U-Boot to execute the specified command instead of the default
autoboot behavior.
Options
~~~~~~~
@@ -107,3 +115,10 @@ Options are available to control the script:
-w
Use word version (32-bit). By default, 64-bit is used
--bootcmd BOOTCMD
U-Boot bootcmd to pass via fw_cfg. This allows passing a custom boot
command to U-Boot at runtime through QEMU's firmware configuration
interface. The bootcmd is written to the 'opt/u-boot/bootcmd' fw_cfg
entry and is read by U-Boot's EVT_BOOTCMD handler before the default
autoboot process runs.

View File

@@ -96,7 +96,7 @@ config TPL_CROS_EC_KEYB
config I8042_KEYB
bool "Enable Intel i8042 keyboard support"
depends on DM_KEYBOARD
default X86
default X86 && !EFI_APP
help
This adds a driver for the i8042 keyboard controller, allowing the
keyboard to be used on devices which support this controller. The

View File

@@ -13,25 +13,122 @@
#include <efi_api.h>
#include <log.h>
#include <mouse.h>
#include <video.h>
/* Maximum coordinate value for mouse position */
#define MOUSE_MAX_COORD 0xffff
/**
* struct efi_mouse_priv - Private data for EFI mouse driver
*
* @simple: Simple pointer protocol (relative movement)
* @abs: Absolute pointer protocol (absolute position)
* @simple_last: Last simple pointer state
* @abs_last: Last absolute pointer state
* @has_last_state: True if we have a previous state for delta calculation
* @use_absolute: True to use absolute pointer, false for simple/relative
* @x: Current X position
* @y: Current Y position
* @buttons: Current button state
* @old_buttons: Previous button state for detecting changes
* @timer_event: EFI timer event for periodic polling
*/
struct efi_mouse_priv {
struct efi_simple_pointer_protocol *pointer;
struct efi_simple_pointer_state last_state;
struct efi_simple_pointer_protocol *simple;
struct efi_absolute_pointer_protocol *abs;
struct efi_simple_pointer_state simple_last;
struct efi_absolute_pointer_state abs_last;
bool has_last_state;
bool use_absolute;
int x, y;
int buttons;
int old_buttons;
struct efi_event *timer_event;
};
static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event)
/**
* get_abs_pointer() - Handle absolute pointer input
*
* @priv: Private data
* @uc_priv: Uclass-private data
* @rel_x: Returns relative X movement
* @rel_y: Returns relative Y movement
* @new_buttons: Returns button state
* Return: 0 if OK, -EAGAIN if no event, -ve on error
*/
static int get_abs_pointer(struct efi_mouse_priv *priv,
struct mouse_uc_priv *uc_priv, int *rel_x,
int *rel_y, int *new_buttons)
{
struct efi_absolute_pointer_state state;
efi_status_t ret;
/* Debug: check structure size and alignment */
log_debug("State struct size: %zu, address: %p\n", sizeof(state),
&state);
ret = priv->abs->get_state(priv->abs, &state);
if (ret == EFI_NOT_READY)
return -EAGAIN;
if (ret) {
log_debug("abs: get_state failed (ret=0x%lx)\n", ret);
return -EIO;
}
/* Always log the state values to see what we're getting */
log_debug("abs: X=%llu Y=%llu Buttons=0x%x\n", state.current_x,
state.current_y, state.active_buttons);
/* Calculate relative movement */
if (priv->has_last_state) {
*rel_x = (int)(state.current_x - priv->abs_last.current_x);
*rel_y = (int)(state.current_y - priv->abs_last.current_y);
log_debug("abs: rel_x=%d, rel_y=%d\n", *rel_x, *rel_y);
}
priv->abs_last = state;
/* Update absolute position - scale to video display if available */
if (uc_priv->video_dev && priv->abs->mode) {
struct efi_absolute_pointer_mode *mode = priv->abs->mode;
u64 x_range = mode->abs_max_x - mode->abs_min_x;
u64 y_range = mode->abs_max_y - mode->abs_min_y;
if (x_range > 0 && y_range > 0) {
log_debug("abs: unscaled x=%llx y=%llx\n",
state.current_x, state.current_y);
priv->x = ((state.current_x - mode->abs_min_x) *
uc_priv->video_width) / x_range;
priv->y = ((state.current_y - mode->abs_min_y) *
uc_priv->video_height) / y_range;
} else {
priv->x = state.current_x;
priv->y = state.current_y;
}
} else {
priv->x = state.current_x;
priv->y = state.current_y;
}
/* Extract button state */
*new_buttons = state.active_buttons & 0x3; /* Left and right buttons */
return 0;
}
/**
* get_rel_pointer() - Handle relative pointer input
*
* @priv: Private data
* @rel_x: Returns relative X movement
* @rel_y: Returns relative Y movement
* @new_buttons: Returns button state
* Return: 0 if OK, -EAGAIN if no event, -ve on error
*/
static int get_rel_pointer(struct efi_mouse_priv *priv, int *rel_x,
int *rel_y, int *new_buttons)
{
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_simple_pointer_state state;
efi_status_t ret;
int new_buttons;
if (!priv->pointer)
return -ENODEV;
/* Use timer-based polling approach like EFI keyboard */
if (priv->timer_event) {
@@ -41,8 +138,8 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event)
efi_uintn_t num_events = 1;
events[0] = priv->timer_event;
if (priv->pointer->wait_for_input) {
events[1] = priv->pointer->wait_for_input;
if (priv->simple->wait_for_input) {
events[1] = priv->simple->wait_for_input;
num_events = 2;
}
@@ -51,70 +148,223 @@ static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event)
return -EAGAIN;
}
/* Get current pointer state */
ret = priv->pointer->get_state(priv->pointer, &state);
if (ret != EFI_SUCCESS) {
if (ret == EFI_NOT_READY)
return -EAGAIN;
printf("EFI mouse: get_state failed (ret=0x%lx)\n", ret);
log_debug("rel: calling get_state\n");
ret = priv->simple->get_state(priv->simple, &state);
log_debug("rel: get_state returned 0x%lx\n", ret);
if (ret == EFI_NOT_READY)
return -EAGAIN;
if (ret) {
log_debug("rel: get_state failed (ret=0x%lx)\n", ret);
return -EIO;
}
/* Check for button changes */
new_buttons = 0;
if (state.left_button)
new_buttons |= 1 << 0;
if (state.right_button)
new_buttons |= 1 << 1;
log_debug("rel: RelX=%d RelY=%d LeftBtn=%d RightBtn=%d\n",
state.relative_movement_x, state.relative_movement_y,
state.left_button, state.right_button);
if (new_buttons != priv->old_buttons) {
struct mouse_button *but = &event->button;
u8 diff = new_buttons ^ priv->old_buttons;
int i;
/*
* Scale down large movement values that seem to be incorrectly
* reported
*/
*rel_x = state.relative_movement_x;
*rel_y = state.relative_movement_y;
event->type = MOUSE_EV_BUTTON;
/* Find first changed button */
for (i = 0; i < 2; i++) {
u8 mask = 1 << i;
if (diff & mask) {
but->button = i;
but->press_state = (new_buttons & mask) ? 1 : 0;
but->clicks = 1;
but->x = priv->x;
but->y = priv->y;
priv->old_buttons ^= mask;
return 0;
}
}
/* If movement values are very large, scale them down */
if (abs(*rel_x) > 1000) {
*rel_x = *rel_x / 1000;
if (*rel_x == 0 && state.relative_movement_x != 0)
*rel_x = (state.relative_movement_x > 0) ? 1 : -1;
}
if (abs(*rel_y) > 1000) {
*rel_y = *rel_y / 1000;
if (*rel_y == 0 && state.relative_movement_y != 0)
*rel_y = (state.relative_movement_y > 0) ? 1 : -1;
}
/* Check for movement */
if (state.relative_movement_x || state.relative_movement_y) {
struct mouse_motion *motion = &event->motion;
log_debug("rel: scaled RelX=%d RelY=%d\n", *rel_x, *rel_y);
/* Update absolute position */
priv->x += state.relative_movement_x;
priv->x = max(priv->x, 0);
priv->x = min(priv->x, 0xffff);
/* Update absolute position */
priv->x += *rel_x;
priv->x = max(priv->x, 0);
priv->x = min(priv->x, MOUSE_MAX_COORD);
priv->y += state.relative_movement_y;
priv->y = max(priv->y, 0);
priv->y = min(priv->y, 0xffff);
priv->y += *rel_y;
priv->y = max(priv->y, 0);
priv->y = min(priv->y, MOUSE_MAX_COORD);
event->type = MOUSE_EV_MOTION;
motion->state = new_buttons;
motion->x = priv->x;
motion->y = priv->y;
motion->xrel = state.relative_movement_x;
motion->yrel = state.relative_movement_y;
/* Extract button state */
*new_buttons = 0;
if (state.left_button)
*new_buttons |= 1 << 0;
if (state.right_button)
*new_buttons |= 1 << 1;
priv->buttons = new_buttons;
return 0;
}
/**
* get_button_event() - Check for button-change events
*
* @priv: Private data
* @new_buttons: New button state
* @event: Event to populate if button changed
* Return: 0 if button event found, -EAGAIN if no button change
*/
static int get_button_event(struct efi_mouse_priv *priv, int new_buttons,
struct mouse_event *event)
{
struct mouse_button *but = &event->button;
int diff = new_buttons ^ priv->old_buttons;
int i;
if (new_buttons == priv->old_buttons)
return -EAGAIN;
event->type = MOUSE_EV_BUTTON;
/* Find first changed button */
for (i = 0; i < 2; i++) {
u8 mask = 1 << i;
if (!(diff & mask))
continue;
but->button = i;
but->press_state = (new_buttons & mask) ? 1 : 0;
but->clicks = 1;
but->x = priv->x;
but->y = priv->y;
priv->old_buttons ^= mask;
return 0;
}
return -EAGAIN;
}
static int efi_mouse_get_event(struct udevice *dev, struct mouse_event *event)
{
struct mouse_uc_priv *uc_priv = dev_get_uclass_priv(dev);
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct mouse_motion *motion;
int new_buttons;
int rel_x, rel_y;
int ret;
/*
* Get current pointer state. Under QEMU, EFI pointer-events are broken
* so we poll directly
*/
if (priv->use_absolute)
ret = get_abs_pointer(priv, uc_priv, &rel_x, &rel_y,
&new_buttons);
else
ret = get_rel_pointer(priv, &rel_x, &rel_y, &new_buttons);
if (ret)
return ret;
priv->has_last_state = true;
/* Check for button changes */
ret = get_button_event(priv, new_buttons, event);
if (!ret)
return 0;
/* If there's no movement, nothing to do */
if (!rel_x && !rel_y) {
priv->buttons = new_buttons;
return -EAGAIN;
}
motion = &event->motion;
event->type = MOUSE_EV_MOTION;
motion->state = new_buttons;
motion->x = priv->x;
motion->y = priv->y;
motion->xrel = rel_x;
motion->yrel = rel_y;
priv->buttons = new_buttons;
return -EAGAIN;
return 0;
}
/**
* setup_abs_pointer() - Set up absolute pointer protocol
*
* @priv: Private data
* Return: 0 if OK, -ve on error
*/
static int setup_abs_pointer(struct efi_mouse_priv *priv)
{
struct efi_boot_services *boot = efi_get_boot();
efi_handle_t *handles;
efi_uintn_t num_handles;
efi_status_t ret;
ret = boot->locate_handle_buffer(BY_PROTOCOL,
&efi_guid_absolute_pointer,
NULL, &num_handles, &handles);
if (ret)
return -ENODEV;
log_debug("Found %zu absolute pointer device(s) guid %pU\n",
num_handles, &efi_guid_absolute_pointer);
/* Use the first absolute pointer device */
ret = boot->open_protocol(handles[0], &efi_guid_absolute_pointer,
(void **)&priv->abs,
efi_get_parent_image(), NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret) {
log_debug("Cannot open absolute pointer protocol (ret=0x%lx)\n",
ret);
efi_free_pool(handles);
return -EIO;
}
priv->use_absolute = true;
log_debug("Using absolute pointer protocol\n");
efi_free_pool(handles);
return 0;
}
/**
* setup_simple_pointer() - Set up simple pointer protocol
*
* @priv: Private data
* Return: 0 if OK, -ve on error
*/
static int setup_simple_pointer(struct efi_mouse_priv *priv)
{
struct efi_boot_services *boot = efi_get_boot();
efi_handle_t *handles;
efi_uintn_t num_handles;
efi_status_t ret;
log_debug("EFI simple-pointer mouse probe\n");
ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_guid_simple_pointer,
NULL, &num_handles, &handles);
if (ret)
return -ENODEV;
log_debug("Found %zu simple pointer device(s)\n", num_handles);
/* Use the first simple pointer device */
ret = boot->open_protocol(handles[0], &efi_guid_simple_pointer,
(void **)&priv->simple,
efi_get_parent_image(), NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret) {
log_debug("Cannot open simple pointer protocol (ret=0x%lx)\n",
ret);
efi_free_pool(handles);
return -EIO;
}
priv->use_absolute = false;
log_debug("Using simple pointer protocol\n");
efi_free_pool(handles);
return 0;
}
static int efi_mouse_probe(struct udevice *dev)
@@ -122,64 +372,55 @@ static int efi_mouse_probe(struct udevice *dev)
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_boot_services *boot = efi_get_boot();
efi_status_t ret;
efi_handle_t *handles;
efi_uintn_t num_handles;
log_debug("EFI mouse probe\n");
/* Find Simple Pointer Protocol handles */
ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_guid_simple_pointer,
NULL, &num_handles, &handles);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: No EFI pointer devices found (ret=0x%lx)\n", ret);
/* Try absolute pointer first, then fall back to simple pointer */
if (setup_abs_pointer(priv) && setup_simple_pointer(priv))
return -ENODEV;
}
log_debug("Found %zu EFI pointer device(s)\n", num_handles);
/* Use the first pointer device */
ret = boot->open_protocol(handles[0], &efi_guid_simple_pointer,
(void **)&priv->pointer, efi_get_parent_image(),
NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to open EFI pointer protocol (ret=0x%lx)\n", ret);
efi_free_pool(handles);
return -ENODEV;
}
efi_free_pool(handles);
/* Reset the pointer device */
ret = priv->pointer->reset(priv->pointer, false);
if (ret != EFI_SUCCESS) {
log_warning("Failed to reset EFI pointer device\n");
if (priv->use_absolute)
ret = priv->abs->reset(priv->abs, true);
else
ret = priv->simple->reset(priv->simple, true);
if (ret) {
log_warning("Failed to reset device (err=0x%lx)\n", ret);
/* Continue anyway - some devices might not support reset */
}
priv->x = 0;
priv->y = 0;
priv->buttons = 0;
priv->old_buttons = 0;
priv->has_last_state = false;
/* Create a timer event for periodic checking */
ret = boot->create_event(EVT_TIMER, TPL_NOTIFY, NULL, NULL,
&priv->timer_event);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to create timer event (ret=0x%lx)\n", ret);
if (ret) {
log_debug("Failed to create timer event (ret=0x%lx)\n", ret);
/* Continue without timer - fallback to direct polling */
priv->timer_event = NULL;
} else {
/* Set timer to trigger every 10ms (100000 x 100ns = 10ms) */
ret = boot->set_timer(priv->timer_event, EFI_TIMER_PERIODIC, 10000);
if (ret != EFI_SUCCESS) {
printf("EFI mouse: Failed to set timer (ret=0x%lx)\n", ret);
ret = boot->set_timer(priv->timer_event, EFI_TIMER_PERIODIC,
10000);
if (ret) {
log_debug("Failed to set timer (ret=0x%lx)\n", ret);
boot->close_event(priv->timer_event);
priv->timer_event = NULL;
}
}
log_info("EFI mouse initialized\n");
/* Test protocol validity */
if (priv->use_absolute && priv->abs->mode) {
struct efi_absolute_pointer_mode *mode = priv->abs->mode;
log_debug("absolute mouse mode: x %llx-%llx y %llx-%llx\n",
mode->abs_min_x, mode->abs_max_x,
mode->abs_min_y, mode->abs_max_y);
log_debug("absolute mouse wait_for_input event: %p\n",
priv->abs->wait_for_input);
} else if (!priv->use_absolute && priv->simple) {
log_debug("simple mouse wait_for_input event: %p\n",
priv->simple->wait_for_input);
}
log_debug("initialized (%s protocol)\n",
priv->use_absolute ? "absolute" : "simple");
return 0;
}
@@ -188,15 +429,10 @@ static int efi_mouse_remove(struct udevice *dev)
struct efi_mouse_priv *priv = dev_get_priv(dev);
struct efi_boot_services *boot = efi_get_boot();
if (priv->timer_event) {
if (priv->timer_event)
boot->close_event(priv->timer_event);
priv->timer_event = NULL;
}
if (priv->pointer) {
/* Protocol will be automatically closed when the image is unloaded */
priv->pointer = NULL;
}
/* Protocol will be automatically closed when the image is unloaded */
return 0;
}

View File

@@ -212,8 +212,8 @@ int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr,
return 0;
}
static int qfw_locate_file(struct udevice *dev, const char *fname,
enum fw_cfg_selector *selectp, ulong *sizep)
int qfw_locate_file(struct udevice *dev, const char *fname,
enum fw_cfg_selector *selectp, ulong *sizep)
{
struct fw_file *file;
int ret;

View File

@@ -12,9 +12,12 @@
#include <fdtdec.h>
#include <log.h>
#include <linux/compiler.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <serial.h>
DECLARE_GLOBAL_DATA_PTR;
/* Information about the efi console */
struct serial_efi_priv {
struct efi_simple_text_input_protocol *con_in;
@@ -44,26 +47,38 @@ int serial_efi_setbrg(struct udevice *dev, int baudrate)
static int serial_efi_get_key(struct serial_efi_priv *priv)
{
struct efi_simple_text_input_protocol *cin = priv->con_in;
struct efi_event *events[2] = {cin->wait_for_key, priv->timer};
efi_uintn_t index;
efi_status_t ret;
if (priv->have_key)
return 0;
ret = priv->boot->wait_for_event(2, events, &index);
if (ret) {
log_err("wait_for_event() failed\n");
return -EAGAIN;
}
if (index)
return -EAGAIN;
/* For emulators like QEMU, skip the timer wait since USB event
* processing is not needed
*/
if (gd->flags & GD_FLG_EMUL) {
ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key);
if (ret == EFI_NOT_READY)
return -EAGAIN;
else if (ret != EFI_SUCCESS)
return -EIO;
} else {
struct efi_event *events[2] = {cin->wait_for_key, priv->timer};
efi_uintn_t index;
ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key);
if (ret == EFI_NOT_READY)
return -EAGAIN;
else if (ret != EFI_SUCCESS)
return -EIO;
ret = priv->boot->wait_for_event(2, events, &index);
if (ret) {
log_err("wait_for_event() failed\n");
return -EAGAIN;
}
if (index)
return -EAGAIN;
ret = priv->con_in->read_key_stroke(priv->con_in, &priv->key);
if (ret == EFI_NOT_READY)
return -EAGAIN;
else if (ret != EFI_SUCCESS)
return -EIO;
}
priv->have_key = true;
@@ -155,7 +170,7 @@ static int serial_efi_probe(struct udevice *dev)
if (ret)
return -ECOMM;
ret = boot->set_timer(priv->timer, EFI_TIMER_PERIODIC,
1000 * 1000 / 100 /* 1ms in 100ns units */);
100 /* 1s in 100ns units */);
if (ret)
return -ECOMM;

View File

@@ -11,6 +11,7 @@
#include <efi_api.h>
#include <efi_stub.h>
#include <log.h>
#include <mapmem.h>
#include <vesa.h>
#include <video.h>
@@ -60,6 +61,10 @@ static int efi_video_sync(struct udevice *dev, uint flags)
return -EIO;
}
}
/*
* When use_blit is false and CONFIG_VIDEO_COPY is enabled,
* video_manual_sync() handles copying to copy_fb
*/
return 0;
}
@@ -252,7 +257,9 @@ static int efi_video_probe(struct udevice *dev)
goto err;
printf("Video: %dx%dx%d @ %lx\n", uc_priv->xsize, uc_priv->ysize,
vesa->bits_per_pixel, (ulong)priv->fb);
vesa->bits_per_pixel, (ulong)uc_priv->fb);
log_debug("use_blit %d, copy_base %lx, copy_fb %p\n", priv->use_blit,
(ulong)plat->copy_base, uc_priv->copy_fb);
return 0;
@@ -267,7 +274,6 @@ static int efi_video_bind(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct efi_video_priv tmp_priv;
struct vesa_mode_info vesa;
int ret;
@@ -282,10 +288,13 @@ static int efi_video_bind(struct udevice *dev)
/* this is not reached if the EFI call failed */
plat->copy_size = vesa.bytes_per_scanline *
vesa.y_resolution;
log_debug("copy size %lx\n", plat->copy_size);
}
}
if (tmp_priv.use_blit)
if (tmp_priv.use_blit) {
plat->size = vesa.bytes_per_scanline * vesa.y_resolution;
log_debug("plat size %x\n", plat->size);
}
return 0;
}

View File

@@ -764,6 +764,12 @@ enum gd_flags {
* For now, this just avoids console output on startup
*/
GD_FLG_ULIB = 0x10000000,
/**
* @GD_FLG_EMUL: Running in an emulator (e.g. QEMU)
*
* Detected from SMBIOS or other platform information
*/
GD_FLG_EMUL = 0x20000000,
};
#if CONFIG_IS_ENABLED(ULIB)

View File

@@ -179,6 +179,7 @@ extern const efi_guid_t efi_guid_device_path;
extern const efi_guid_t efi_simple_file_system_protocol_guid;
extern const efi_guid_t efi_guid_simple_pointer;
extern const efi_guid_t efi_guid_absolute_pointer;
/* Generic EFI table header */
struct efi_table_hdr {

View File

@@ -1006,6 +1006,36 @@ struct efi_simple_pointer_protocol {
struct efi_simple_pointer_mode *mode;
};
#define EFI_ABSOLUTE_POINTER_PROTOCOL_GUID \
EFI_GUID(0x8d59d32b, 0xc655, 0x4ae9, \
0x9b, 0x15, 0xf2, 0x59, 0x04, 0x99, 0x2a, 0x43)
struct efi_absolute_pointer_state {
u64 current_x;
u64 current_y;
u64 current_z;
u32 active_buttons;
};
struct efi_absolute_pointer_mode {
u64 abs_min_x;
u64 abs_min_y;
u64 abs_min_z;
u64 abs_max_x;
u64 abs_max_y;
u64 abs_max_z;
u32 attributes;
};
struct efi_absolute_pointer_protocol {
efi_status_t (EFIAPI *reset)(struct efi_absolute_pointer_protocol *this,
bool extended_verification);
efi_status_t (EFIAPI *get_state)(struct efi_absolute_pointer_protocol *this,
struct efi_absolute_pointer_state *state);
struct efi_event *wait_for_input;
struct efi_absolute_pointer_mode *mode;
};
#define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \
EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)

View File

@@ -172,6 +172,16 @@ enum event_t {
*/
EVT_MAIN_LOOP,
/**
* @EVT_BOOTCMD:
* This event is triggered in main_loop() before autoboot_command().
* It allows platform code to provide a custom bootcmd string.
* Its parameter is of type struct event_bootcmd.
* The event handler can write the bootcmd to the provided buffer.
* A non-zero return value causes the boot to fail.
*/
EVT_BOOTCMD,
/**
* @EVT_BOOT_OS_ADDR:
* Triggered immediately before the OS is loaded into its final address
@@ -270,6 +280,18 @@ union event_data {
struct event_bootm_final {
enum bootm_final_t flags;
} bootm_final;
/**
* struct event_bootcmd - bootcmd override
*
* @bootcmd: Buffer for bootcmd string (provided by caller, must be an
* empty string on entry)
* @size: Size of bootcmd buffer
*/
struct event_bootcmd {
char *bootcmd;
int size;
} bootcmd;
};
/**

View File

@@ -146,6 +146,7 @@ struct expo_theme {
* @scene_head: List of scenes
* @str_head: list of strings
* @cch: Keyboard context for input
* @last_key_ms: timestamp of the last key received
*/
struct expo {
char *name;
@@ -173,6 +174,7 @@ struct expo {
struct list_head scene_head;
struct list_head str_head;
struct cli_ch_state cch;
ulong last_key_ms;
};
/**

View File

@@ -464,6 +464,18 @@ int qfw_load_file(struct udevice *dev, const char *fname, ulong addr);
*/
int qfw_get_file(struct udevice *dev, const char *fname, struct abuf *loader);
/**
* qfw_locate_file() - Locate a file in the QEMU firmware config
*
* @dev: UCLASS_QFW device
* @fname: Filename to locate
* @selectp: Returns the selector for the file
* @sizep: Returns the size of the file
* Return: 0 on success, -EINVAL if firmware list cannot be read or file not found
*/
int qfw_locate_file(struct udevice *dev, const char *fname,
enum fw_cfg_selector *selectp, ulong *sizep);
/**
* cmd_qfw_e820() - Execute the 'qfw e820' command for x86
*

View File

@@ -28,6 +28,7 @@ const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID;
const efi_guid_t efi_guid_simple_pointer = EFI_SIMPLE_POINTER_PROTOCOL_GUID;
const efi_guid_t efi_guid_absolute_pointer = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID;
const efi_guid_t efi_guid_loaded_image_device_path =
EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
const efi_guid_t efi_simple_file_system_protocol_guid =

View File

@@ -22,6 +22,7 @@
#include <image.h>
#include <init.h>
#include <malloc.h>
#include <smbios.h>
#include <sysreset.h>
#include <u-boot/uuid.h>
#include <asm/global_data.h>
@@ -164,7 +165,8 @@ static void free_memory(struct efi_priv *priv)
static void scan_tables(struct efi_system_table *sys_table)
{
efi_guid_t acpi = EFI_ACPI_TABLE_GUID;
efi_guid_t smbios = SMBIOS3_TABLE_GUID;
efi_guid_t smbios = SMBIOS_TABLE_GUID;
efi_guid_t smbios3 = SMBIOS3_TABLE_GUID;
uint i;
for (i = 0; i < sys_table->nr_tables; i++) {
@@ -172,11 +174,33 @@ static void scan_tables(struct efi_system_table *sys_table)
if (!memcmp(&tab->guid, &acpi, sizeof(efi_guid_t)))
gd_set_acpi_start(map_to_sysmem(tab->table));
else if (!memcmp(&tab->guid, &smbios, sizeof(efi_guid_t)))
gd->arch.smbios_start = map_to_sysmem(tab->table);
else if (!memcmp(&tab->guid, &smbios, sizeof(efi_guid_t)) ||
!memcmp(&tab->guid, &smbios3, sizeof(efi_guid_t)))
gd_set_smbios_start(map_to_sysmem(tab->table));
}
}
static bool detect_emulator(void)
{
struct smbios_info info;
struct smbios_type1 *t1;
const char *manufacturer;
/* Check if running in QEMU by looking at SMBIOS manufacturer */
if (!smbios_locate(gd_smbios_start(), &info)) {
t1 = (void *)smbios_get_header(&info,
SMBIOS_SYSTEM_INFORMATION);
if (t1) {
manufacturer = smbios_get_string(&t1->hdr,
t1->manufacturer);
if (manufacturer && !strcmp(manufacturer, "QEMU"))
return true;
}
}
return false;
}
static void find_protocols(struct efi_priv *priv)
{
efi_guid_t guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
@@ -413,7 +437,8 @@ efi_status_t EFIAPI efi_main(efi_handle_t image,
printf("starting\n");
board_init_f(GD_FLG_SKIP_RELOC);
board_init_f(GD_FLG_SKIP_RELOC |
(detect_emulator() ? GD_FLG_EMUL : 0));
gd = gd->new_gd;
board_init_r(NULL, 0);
free_memory(priv);

View File

@@ -41,6 +41,8 @@ def parse_args():
description='Build and/or run U-Boot with QEMU',
formatter_class=argparse.RawTextHelpFormatter)
build_helper.add_common_args(parser)
parser.add_argument('--bootcmd', type=str,
help='U-Boot bootcmd to pass via fw_cfg')
parser.add_argument('-e', '--sct-run', action='store_true',
help='Run UEFI Self-Certification Test (SCT)')
parser.add_argument('-E', '--use-tianocore', action='store_true',
@@ -290,6 +292,11 @@ class BuildQemu:
# Add other parameters gathered from options
qemu_cmd.extend(self.qemu_extra)
# Add bootcmd via fw_cfg if specified
if self.args.bootcmd:
qemu_cmd.extend(['-fw_cfg',
f'name=opt/u-boot/bootcmd,string={self.args.bootcmd}'])
self.helper.setup_share(qemu_cmd)
self.helper.run(qemu_cmd)