Compare commits
17 Commits
cherry-70c
...
prof
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f597f1951 | ||
|
|
c4b73a8845 | ||
|
|
af43cdae92 | ||
|
|
b3025106c8 | ||
|
|
1d6f09612f | ||
|
|
9b27554158 | ||
|
|
207aec39f1 | ||
|
|
ab57a5483f | ||
|
|
2a75f77d66 | ||
|
|
c424223703 | ||
|
|
56c582392d | ||
|
|
e32fa61bf8 | ||
|
|
f0b69eca76 | ||
|
|
13e19af370 | ||
|
|
4426d0e25e | ||
|
|
4e8f519f66 | ||
|
|
720884c68f |
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
16
boot/expo.c
16
boot/expo.c
@@ -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;
|
||||
|
||||
@@ -49,6 +49,7 @@ const char *const type_name[] = {
|
||||
|
||||
/* main loop events */
|
||||
"main_loop",
|
||||
"bootcmd",
|
||||
|
||||
/* booting */
|
||||
"boot_os_addr",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 /* 10μs in 100ns units */);
|
||||
if (ret)
|
||||
return -ECOMM;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user