Compare commits

...

22 Commits

Author SHA1 Message Date
Simon Glass
eb3d5e4063 WIP: Disable EFI boot manager for demo
Disable this so that we can use bootstd features.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-22 10:26:02 -06:00
Simon Glass
3f1920b3cc WIP: video: Use 800x600 for bochs
Use a smaller display for the video.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-22 10:08:00 -06:00
Simon Glass
f737237d29 Merge branch 'fast' into 'master'
emulation: Improve support for booting from QFW

Closes #7

See merge request u-boot/u-boot!103
2025-06-12 20:47:50 +00:00
Simon Glass
3cac4dd7a4 emulation: Allow booting without CMDLINE
Use programmatic boot so that it is possible to boot without
CONFIG_CMDLINE enabled.

Series-to: u-boot
Cover-letter:
emulation: Improve support for booting from QFW
U-Boot supports booting Linux from QFW which means that the kernel and
any initrd are provided on the QEMU command line instead of being found
in boot media.

This series improves this support in several ways:
- Enhances bootstd to implement 'bootflow read', thus allowing the
  files to be loaded and inspected (with potential cmdline changes)
  before booting
- Updates bootstd to use programmatic boot, so that it works even when
  CONFIG_CMDLINE is disabled
- Expands build-qemu script to allow providing cmdline and root disk

It also includes a rough script to time U-Boot when running QEMU with
kvm, making use of the qemu-boot-time repo:

   https://github.com/stefano-garzarella/qemu-boot-time.git
END

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
28548b43b6 emulation: Support zboot in the qfw bootmeth
The zboot option is used on x86, so add support for it. Use the
programmatic boot API rather than building a command to run, so that it
can work without CONFIG_CMDLINE

For now the other boot options still use the cmdline.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
90aa800f03 emulation: Support read_all() in qfw bootmeth
It is useful to be able to read the images into memory without actually
booting the kernel. This allows the cmdline to be changed using
'bootflow cmd', for example. Implement this for the QFW bootmeth.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
2d5444b467 emulation: Place the setup area as QEMU requests
QEMU provides a SETUP_ADDR value which should be honoured when placing
the setup block, since that block can include pointers into other parts
of itself.

Instead of putting the setup block immediately before the kernel, put it
where QEMU requests.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
550cf4ca0c emulation: Move qfw reading into a new function
Add a new qemu_fwcfg_read_files() function which reads files into the
provided location. Use this from qemu_fwcfg_setup_kernel()

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
10d50ff11a emulation: Refactor how the kernel is set up
The qemu_fwcfg_setup_kernel() function checks whether a kernel exists
and also reads it. With bootstd we want to do those two steps
separately.

Add a new qemu_fwcfg_read_info() function which gets all the values and
returns then, including the full cmdline. Use that from the outer
function.

The new function also reads the setup address, although this it not used
by the code at present.

Use an abuf for the cmdline since we don't want the read_info function
to alter unallocated memory.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
a79751c65d emulation: Move endianness conversion into qfw_read_size()
Instead of worrying about endianness in each expression, move the
conversion into the function.

Use ulong for values, since this is the normal type for addresses in
U-Boot

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
97dd8f5b13 emulation: Use a function to get a selector size
Move the selector reading into a separate function so we can simplify
qemu_fwcfg_setup_kernel().

Read all sizes at the start and add an enum for the selector.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
4a504a9c59 emulation: Rename data_addr in qemu_fwcfg_setup_kernel()
This is a pointer, not an address, so rename the variable. Also drop the
unnecessary assignment.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
2e1d6e0db8 boot: Add a way to get writable image information
The bootflow_img_find() function does not permit updating the image.
Some bootmeths may want to do this, e.g. to change the address. Add a
function which returns a writeable pointer.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
8fd296aae9 abuf: Add a way to set up an abuf with a given address
In some cases it is convenient to provide an address when initing an
abuf, instead of a pointer. Add a function for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
a6fca9a51b x86: Move ramdisk higher
With kernel size going above 15M we need enough space between where the
kernel loaded and the ramdisk, otherwise the kernel corrupts its ramdisk
while relocating itself. Move the ramdisk up to 128M

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
78dead849f x86: Emit a post code before booting
It is possible to benchmark U-Boot using QEMU as described here:

   https://github.com/stefano-garzarella/qemu-boot-time

On x86 devices, that script can read post codes from a perf trace. Add
a code which is emitted just before handing off to the Linux kernel.
Existing post codes can be used to determine when U-Boot starts.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
f82cc0ee80 contrib: Add a script to time booting with QEMU
It is possible to use QEMU and kvm to measure the time taken by U-Boot
to hand off to Linux. Add a script to support this and print the
resulting time.

This requires a fair bit of setup; see the script for details.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
8001779b9b scripts: build-qemu: Support running qboot
Qboot is a good demonstration of the fastest possible boot on x86. Add
a way to run Qboot instead of U-Boot, for easy comparison.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
2d461a7186 scripts: build-qemu: Add a way to set the linux root
Add -t and -U flags to set the root for Linux, to a device or a UUID,
respectively.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
b6f9ded4bc scripts: build-qemu: Add a way to enable linux console
Add a -C flag to enable the serial console when booting Linux. This is
only supported on x86, but a future -c flag might perhaps support other
archs.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
8f28573146 scripts: build-qemu: Add a way to pass kernel and ramdisk
QEMU provides a special way to pass a kernel and a devicetree via the
QFW interface. Add -K and -I options to provide these.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
Simon Glass
b3743e7120 scripts: build-qemu: Fix message when creating config file
The message is missing an f-string specifier. Add it.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-12 19:43:54 +00:00
18 changed files with 466 additions and 63 deletions

View File

@@ -31,6 +31,7 @@
#define POST_DRAM 0x30
#define POST_LAPIC 0x31
#define POST_OS_RESUME 0x40
#define POST_BOOT 0x42
#define POST_RAM_FAILURE 0xea
#define POST_BIST_FAILURE 0xeb

View File

@@ -24,6 +24,7 @@
#include <asm/bootparam.h>
#include <asm/cpu.h>
#include <asm/byteorder.h>
#include <asm/post.h>
#include <asm/zimage.h>
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/timestamp.h>
@@ -201,6 +202,8 @@ int boot_linux_kernel(ulong setup_base, ulong entry, bool image_64bit)
if (IS_ENABLED(CONFIG_EFI_APP))
return efi_boot(setup_base, entry, image_64bit);
post_code(POST_BOOT);
if (image_64bit) {
if (!cpu_has_64bit()) {
puts("Cannot boot 64-bit kernel on 32-bit machine\n");

View File

@@ -998,10 +998,10 @@ struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname,
return ptr;
}
const struct bootflow_img *bootflow_img_find(const struct bootflow *bflow,
enum bootflow_img_t type)
struct bootflow_img *bootflow_img_findw(const struct bootflow *bflow,
enum bootflow_img_t type)
{
const struct bootflow_img *img;
struct bootflow_img *img;
alist_for_each(img, &bflow->images) {
if (img->type == type)

View File

@@ -8,11 +8,14 @@
#define LOG_CATEGORY UCLASS_BOOTSTD
#include <abuf.h>
#include <command.h>
#include <bootdev.h>
#include <bootflow.h>
#include <bootm.h>
#include <bootmeth.h>
#include <env.h>
#include <mapmem.h>
#include <qfw.h>
#include <dm.h>
@@ -31,26 +34,86 @@ static int qfw_check(struct udevice *dev, struct bootflow_iter *iter)
static int qfw_read_bootflow(struct udevice *dev, struct bootflow *bflow)
{
struct udevice *qfw_dev = dev_get_parent(bflow->dev);
ulong load, initrd;
ulong setup, kern, ramdisk, setup_addr;
size_t cmdline_size;
struct abuf cmdline;
int ret;
load = env_get_hex("kernel_addr_r", 0);
initrd = env_get_hex("ramdisk_addr_r", 0);
log_debug("setup kernel %s %lx %lx\n", qfw_dev->name, load, initrd);
/* Get the size of each region */
ret = qemu_fwcfg_read_info(qfw_dev, &setup, &kern, &ramdisk, &cmdline,
&setup_addr);
if (ret)
return log_msg_ret("qri", ret);
bflow->cmdline = abuf_uninit_move(&cmdline, &cmdline_size);
bflow->name = strdup("qfw");
if (!bflow->name)
return log_msg_ret("name", -ENOMEM);
ret = qemu_fwcfg_setup_kernel(qfw_dev, load, initrd);
log_debug("setup kernel result %d\n", ret);
if (ret)
return log_msg_ret("cmd", -EIO);
/*
* create images for each; only cmdline has the actual data; the others
* only have a size for now, since the data has yet not been read
*/
if (!bootflow_img_add(bflow, "setup",
(enum bootflow_img_t)IH_TYPE_X86_SETUP,
setup_addr, setup))
return log_msg_ret("cri", -ENOMEM);
if (!bootflow_img_add(bflow, "kernel",
(enum bootflow_img_t)IH_TYPE_KERNEL, 0, kern))
return log_msg_ret("qrk", -ENOMEM);
if (ramdisk && !bootflow_img_add(bflow, "ramdisk",
(enum bootflow_img_t)IH_TYPE_RAMDISK,
0, ramdisk))
return log_msg_ret("qrr", -ENOMEM);
if (!bootflow_img_add(bflow, "cmdline", BFI_CMDLINE,
map_to_sysmem(bflow->cmdline), cmdline_size))
return log_msg_ret("qrc", -ENOMEM);
bflow->state = BOOTFLOWST_READY;
return 0;
}
static int qfw_read_files(struct udevice *dev, struct bootflow *bflow,
bool re_read, const struct bootflow_img **simgp,
const struct bootflow_img **kimgp,
const struct bootflow_img **rimgp)
{
struct udevice *qfw_dev = dev_get_parent(bflow->dev);
struct bootflow_img *kimg, *rimg;
struct abuf setup, kern, ramdisk;
const struct bootflow_img *simg;
simg = bootflow_img_find(bflow, (enum bootflow_img_t)IH_TYPE_X86_SETUP);
kimg = bootflow_img_findw(bflow, (enum bootflow_img_t)IH_TYPE_KERNEL);
rimg = bootflow_img_findw(bflow, (enum bootflow_img_t)IH_TYPE_RAMDISK);
if (!kimg)
return log_msg_ret("qfs", -EINVAL);
/* read files only if not already read */
if (re_read || !kimg->addr) {
abuf_init_const_addr(&setup, simg ? simg->addr : 0,
simg ? simg->size : 0);
abuf_init_const_addr(&kern, env_get_hex("kernel_addr_r", 0),
kimg->size);
abuf_init_const_addr(&ramdisk, env_get_hex("ramdisk_addr_r", 0),
rimg ? rimg->size : 0);
qemu_fwcfg_read_files(qfw_dev, &setup, &kern, &ramdisk);
kimg->addr = abuf_addr(&kern);
if (rimg)
rimg->addr = abuf_addr(&ramdisk);
}
if (simgp)
*simgp = simg;
if (kimgp)
*kimgp = kimg;
if (rimgp)
*rimgp = rimg;
return 0;
}
static int qfw_read_file(struct udevice *dev, struct bootflow *bflow,
const char *file_path, ulong addr,
enum bootflow_img_t type, ulong *sizep)
@@ -58,15 +121,55 @@ static int qfw_read_file(struct udevice *dev, struct bootflow *bflow,
return -ENOSYS;
}
static int qfw_boot(struct udevice *dev, struct bootflow *bflow)
#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
static int qfw_read_all(struct udevice *dev, struct bootflow *bflow)
{
struct bootflow_img *kimg;
int ret;
ret = run_command("booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdtcontroladdr}",
0);
if (ret) {
ret = run_command("bootz ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} "
"${fdtcontroladdr}", 0);
kimg = bootflow_img_findw(bflow, (enum bootflow_img_t)IH_TYPE_KERNEL);
if (!kimg)
return log_msg_ret("qra", -ENOENT);
ret = qfw_read_files(dev, bflow, true, NULL, NULL, NULL);
if (ret)
return log_msg_ret("qrA", ret);
return 0;
}
#endif
static int qfw_boot(struct udevice *dev, struct bootflow *bflow)
{
const struct bootflow_img *simg, *kimg, *rimg;
char conf_fdt[20], conf_ramdisk[40], addr_img_str[20];
struct bootm_info bmi;
int ret;
/* read the files if not already done */
ret = qfw_read_files(dev, bflow, false, &simg, &kimg, &rimg);
if (!kimg)
return log_msg_ret("qkf", -EINVAL);
ret = booti_run(&bmi);
bootm_init(&bmi);
snprintf(conf_fdt, sizeof(conf_fdt), "%lx",
(ulong)map_to_sysmem(gd->fdt_blob));
snprintf(addr_img_str, sizeof(addr_img_str), "%lx", kimg->addr);
bmi.addr_img = addr_img_str;
snprintf(conf_ramdisk, sizeof(conf_ramdisk), "%lx:%lx", rimg->addr,
rimg->size);
bmi.conf_ramdisk = conf_ramdisk;
ret = -ENOENT;
if (IS_ENABLED(CONFIG_CMD_BOOTI))
ret = booti_run(&bmi);
if (ret && IS_ENABLED(CONFIG_CMD_BOOTZ))
ret = bootz_run(&bmi);
if (ret && IS_ENABLED(CONFIG_ZBOOT) && simg) {
ret = zboot_run_args(kimg->addr, kimg->size,
rimg->addr, rimg->size, simg->addr,
*bflow->cmdline ? bflow->cmdline : NULL);
}
return ret ? -EIO : 0;
@@ -85,6 +188,9 @@ static struct bootmeth_ops qfw_bootmeth_ops = {
.check = qfw_check,
.read_bootflow = qfw_read_bootflow,
.read_file = qfw_read_file,
#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
.read_all = qfw_read_all,
#endif
.boot = qfw_boot,
};

View File

@@ -4,6 +4,7 @@
* (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
*/
#include <abuf.h>
#include <dm.h>
#include <env.h>
#include <mapmem.h>
@@ -105,62 +106,106 @@ bool qfw_file_iter_end(struct fw_cfg_file_iter *iter)
return iter->entry == iter->end;
}
/**
* qfw_read_size() - Read the size of an entry
*
* @sel: Selector value, e.g. FW_CFG_SETUP_SIZE, FW_CFG_CMDLINE_SIZE
* Return: Size of the entry
*/
static ulong qfw_read_size(struct udevice *qfw_dev, enum fw_cfg_selector sel)
{
u32 size = 0;
qfw_read_entry(qfw_dev, sel, 4, &size);
return le32_to_cpu(size);
}
int qemu_fwcfg_read_info(struct udevice *qfw_dev, ulong *setupp, ulong *kernp,
ulong *initrdp, struct abuf *cmdline,
ulong *setup_addrp)
{
uint cmdline_size;
*setupp = qfw_read_size(qfw_dev, FW_CFG_SETUP_SIZE);
*kernp = qfw_read_size(qfw_dev, FW_CFG_KERNEL_SIZE);
*initrdp = qfw_read_size(qfw_dev, FW_CFG_INITRD_SIZE);
cmdline_size = qfw_read_size(qfw_dev, FW_CFG_CMDLINE_SIZE);
if (!*kernp)
return -ENOENT;
*setup_addrp = qfw_read_size(qfw_dev, FW_CFG_SETUP_ADDR);
if (!abuf_init_size(cmdline, cmdline_size))
return log_msg_ret("qri", -ENOMEM);
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, cmdline_size,
cmdline->data);
return 0;
}
void qemu_fwcfg_read_files(struct udevice *qfw_dev, const struct abuf *setup,
const struct abuf *kern, const struct abuf *initrd)
{
if (setup->size) {
qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, setup->size,
setup->data);
}
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, kern->size, kern->data);
if (initrd->size) {
qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, initrd->size,
initrd->data);
}
}
int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr,
ulong initrd_addr)
{
char *data_addr;
u32 setup_size, kernel_size, cmdline_size, initrd_size;
ulong setup_size, kernel_size, initrd_size, setup_addr;
struct abuf cmdline, setup, kern, initrd;
int ret;
qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size);
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size);
if (!kernel_size) {
ret = qemu_fwcfg_read_info(qfw_dev, &setup_size, &initrd_size,
&kernel_size, &cmdline, &setup_addr);
if (ret) {
printf("fatal: no kernel available\n");
return -ENOENT;
return log_msg_ret("qsk", ret);
}
data_addr = map_sysmem(load_addr, 0);
if (setup_size) {
qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA,
le32_to_cpu(setup_size), data_addr);
data_addr += le32_to_cpu(setup_size);
}
/*
* always put the setup area where QEMU wants it, since it includes
* absolute pointers to itself
*/
abuf_init_const_addr(&setup, setup_addr, 0);
abuf_init_const_addr(&kern, load_addr, 0);
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA,
le32_to_cpu(kernel_size), data_addr);
data_addr += le32_to_cpu(kernel_size);
env_set_hex("filesize", le32_to_cpu(kernel_size));
abuf_init_const_addr(&initrd, initrd_addr, 0);
qemu_fwcfg_read_files(qfw_dev, &setup, &kern, &initrd);
data_addr = map_sysmem(initrd_addr, 0);
qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size);
if (!initrd_size) {
env_set_hex("filesize", kern.size);
if (!initrd_size)
printf("warning: no initrd available\n");
} else {
qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA,
le32_to_cpu(initrd_size), data_addr);
data_addr += le32_to_cpu(initrd_size);
env_set_hex("filesize", le32_to_cpu(initrd_size));
}
else
env_set_hex("filesize", initrd_size);
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
if (cmdline_size) {
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA,
le32_to_cpu(cmdline_size), data_addr);
if (cmdline.data) {
/*
* if kernel cmdline only contains '\0', (e.g. no -append
* when invoking qemu), do not update bootargs
*/
if (*data_addr) {
if (env_set("bootargs", data_addr) < 0)
if (*(char *)cmdline.data) {
if (env_set("bootargs", cmdline.data) < 0)
printf("warning: unable to change bootargs\n");
}
}
abuf_uninit(&cmdline);
printf("loading kernel to address %lx size %x", load_addr,
le32_to_cpu(kernel_size));
printf("loading kernel to address %lx size %zx", abuf_addr(&kern),
kern.size);
if (initrd_size)
printf(" initrd %lx size %x\n", initrd_addr,
le32_to_cpu(initrd_size));
printf(" initrd %lx size %lx\n", abuf_addr(&initrd),
initrd_size);
else
printf("\n");

View File

@@ -94,3 +94,6 @@ CONFIG_GENERATE_ACPI_TABLE=y
CONFIG_CMD_DHRYSTONE=y
# CONFIG_GZIP is not set
CONFIG_UNIT_TEST=y
CONFIG_VIDEO_BOCHS_SIZE_X=800
CONFIG_VIDEO_BOCHS_SIZE_Y=600
# CONFIG_BOOTMETH_EFI_BOOTMGR is not set

View File

@@ -8,3 +8,11 @@ to the U-Boot project but are not maintained by the core developers.
Note to contributors: Each item must have at least an entry in this file. Be
sure to add the appropriate GPL-2.0-or-later SPDX tag to files.
QEMU boot-timer script
----------------------
Runs qboot (or builds and starts U-Boot) and measures how long it takes to
start Lunux. Must be run in the U-Boot source directory.
File: `qemu-boot-timer.sh <../../../../contrib/qemu-boot-timer.sh>`_

93
contrib/qemu-boot-timer.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-or-later
# .. code-block:: bash
# qemu-boot-timer.sh
#
# Runs qboot (or builds and starts U-Boot) and measures how long it takes to
# start Linux. Must be run in the U-Boot source directory.
#
# See:
# https://stefano-garzarella.github.io/posts/2019-08-24-qemu-linux-boot-time/
# https://github.com/stefano-garzarella/qemu-boot-time.git
#
# You will need to first install tracing tools, e.g.:
#
# sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r`
#
# and also change files and directories below
#
# Example usage to test qboot:
# $ ./contrib/qemu-boot-timer.sh
# qemu-system-x86_64: terminating on signal 15 from pid 3548708 (bash)
# 1) pid 3548829
# qemu_init_end: 48.352996
# fw_start: 48.480586 (+0.12759)
# linux_start_boot: 66.16271 (+17.682124)
# Exit point 0: 66.37544 (+0.21273)
#
# That shows a time of 17.7 milliseconds for qboot itself
# Enable this to boot qboot instead of U-Boot
#QBOOT=-QB
# Linux version to boot (read from /boot)
version=6.8.0-60-generic
# UUID of root disk within your disk image, which must be called root.img
uuid=bcfdda4a-8249-4f40-9f0f-7c1a76b6cbe8
# Set up directories to use
DIR=/scratch/sglass/qemu-boot-time/
PERF_DATA=perf.data
export PERF_EXEC_PATH=/scratch/sglass/linux/tools/perf
# No settings below here:
# Turn on tracing in Linux
sudo bash <<EOF
echo 1 > /sys/kernel/debug/tracing/events/kvm/enable
echo -1 > /proc/sys/kernel/perf_event_paranoid
mount -o remount,mode=755 /sys/kernel/debug
mount -o remount,mode=755 /sys/kernel/debug/tracing
EOF
# Start recording
sleep 1
cd $DIR
sudo perf record -a -e kvm:kvm_entry -e kvm:kvm_pio -e sched:sched_process_exec \
-o $PERF_DATA 2> /tmp/trace.log &
PERF_PID=$!
# Run QEWU with qboot or U-Boot (builds U-Boot automatically)
sleep 1
cd ~/u
./scripts/build-qemu -a x86 ${QBOOT} -rs -v -K /boot/vmlinuz-${version} \
-k -d root.img -I /boot/initrd.img-${version} -C -U ${uuid} -s \
2>&1 >/tmp/qemu.log &
while [[ -z "$QEMU_PID" ]]; do
QEMU_PID=$(pgrep qemu-system-x86)
done
# Wait for it to boot
sleep 5
# Kill QEMU and the perf process
kill $QEMU_PID
sleep 1
kill $PERF_PID
wait $PERF_PID
# Adjust permissions on perf data
cd $DIR
sudo chmod a+r $PERF_DATA
# Generate the Python script to permit access to the trace
perf script -g python 2>/dev/null
# Run our script which looks for timing points and prints the results
perf script -s ~/perf-script.py -s ${DIR}/perf-script/qemu-perf-script.py \
-i $PERF_DATA

View File

@@ -12,9 +12,13 @@ provided by the QEMU `-kernel` argument, the initial ramdisk provided by
`-initrd` and the boot arguments (command line) provided by `-append` into
memory ready for booting.
When the bootflow is booted, the bootmeth tries the `booti` command first, then
falls back to the `bootz` command. U-Boot's 'control' devicetree is passed
through to the kernel.
When the bootflow is booted, the bootmeth tries the `booti` option first, then
falls back to the `bootz` option, then `zboot`. U-Boot's 'control' devicetree
is passed through to the kernel on non-x86 devices. This works without needing
`CONFIG_CMDLINE` enabled.
The `bootflow read` command is supported, so it is possible to read the files
and then check the kernel command-line before using `bootflow boot` to boot.
The compatible string "u-boot,qfw-bootmeth" is used for the driver. It is
present if `CONFIG_QFW` is enabled.

View File

@@ -202,6 +202,18 @@ void abuf_init_set(struct abuf *abuf, void *data, size_t size);
*/
void abuf_init_const(struct abuf *abuf, const void *data, size_t size);
/**
* abuf_init_const_addr() - Set up a new buffer at a given address
*
* This is similar to abuf_init_const() except that it takes an address instead
* of a pointer. Note that the abuf is unallocated.
*
* @abuf: abuf to set up
* @addr: Address to use
* @size: Size of buffer
*/
void abuf_init_const_addr(struct abuf *abuf, ulong addr, size_t size);
/**
* abuf_init_size() - Set up an allocated abuf
*

View File

@@ -648,6 +648,16 @@ struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname,
enum bootflow_img_t type, ulong addr,
ulong size);
/**
* bootflow_img_findw() - Find the first image of a given type
*
* @bflow: Bootflow to search
* @type: Image type to search for
* Return: Non-const pointer to image, or NULL if not found
*/
struct bootflow_img *bootflow_img_findw(const struct bootflow *bflow,
enum bootflow_img_t type);
/**
* bootflow_img_find() - Find the first image of a given type
*
@@ -655,8 +665,12 @@ struct bootflow_img *bootflow_img_add(struct bootflow *bflow, const char *fname,
* @type: Image type to search for
* Return: Pointer to image, or NULL if not found
*/
const struct bootflow_img *bootflow_img_find(const struct bootflow *bflow,
enum bootflow_img_t type);
static inline const struct bootflow_img *
bootflow_img_find(const struct bootflow *bflow,
enum bootflow_img_t type)
{
return bootflow_img_findw(bflow, type);
}
/**
* bootflow_get_seq() - Get the sequence number of a bootflow

2
include/env/x86.env vendored
View File

@@ -11,7 +11,7 @@ netdev=eth0
consoledev=ttyS0
scriptaddr=0x7000000
kernel_addr_r=0x1000000
ramdisk_addr_r=0x4000000
ramdisk_addr_r=0x8000000
ramdiskfile=initramfs.gz
/* common console settings */

View File

@@ -8,12 +8,14 @@
#include <linux/list.h>
struct abuf;
/*
* List of firmware configuration item selectors. The official source of truth
* for these is the QEMU source itself; see
* https://github.com/qemu/qemu/blob/master/hw/nvram/fw_cfg.c
*/
enum {
enum fw_cfg_selector {
FW_CFG_SIGNATURE = 0x00,
FW_CFG_ID = 0x01,
FW_CFG_UUID = 0x02,
@@ -316,12 +318,47 @@ bool qfw_file_iter_end(struct fw_cfg_file_iter *iter);
*/
int qemu_cpu_fixup(void);
/**
* qemu_fwcfg_read_info() - See if QEMU has provided kernel, etc.
*
* Read info about the kernel and cmdline
*
* @qfw_dev: UCLASS_QFW device
* @setupp: Returns the size of the setup area on succes
* @kernp: Returns kernel size on success
* @initrd: Returns initrd size on success
* @cmdline: Set to the cmdline in the image (allocated by this function, must
* be freed by the caller)
* @setup_addrp: Address for the setup block, as requested by QEMU
*
* Return 0 on success, -ENOENT if there is no kernel provided, -ENOMEM if there
* was no memory for the cmdline
*/
int qemu_fwcfg_read_info(struct udevice *qfw_dev, ulong *setupp, ulong *kernp,
ulong *initrdp, struct abuf *cmdline,
ulong *setup_addrp);
/**
* qemu_fwcfg_read_files() - Read files from a qfw
*
* Reads kernel and optional setup/initrd images. For each image, the abuf
* controls how many bytes are read and the address into which they are read
*
* @qfw_dev: UCLASS_QFW device
* @setup: Buffer into which to read setup (skipped if setup->size is 0)
* @kern: Buffer into which to read kernel
* @initrd: Buffer into which to read initrd (skipped if initrd->size is 0)
*/
void qemu_fwcfg_read_files(struct udevice *qfw_dev, const struct abuf *setup,
const struct abuf *kern, const struct abuf *initrd);
/*
* qemu_fwcfg_setup_kernel() - Prepare the kernel for zboot
*
* Loads kernel data to 'load_addr', initrd to 'initrd_addr' and kernel command
* line using qemu fw_cfg interface
*
* @qfw_dev: UCLASS_QFW device
* @load_addr: Load address for kernel
* @initrd_addr: Load address for ramdisk
* @return 0 if OK, -ENOENT if no kernel

View File

@@ -183,6 +183,13 @@ void abuf_init_const(struct abuf *abuf, const void *data, size_t size)
abuf_init_set(abuf, (void *)data, size);
}
#ifndef USE_HOSTCC
void abuf_init_const_addr(struct abuf *abuf, ulong addr, size_t size)
{
return abuf_init_const(abuf, map_sysmem(addr, size), size);
}
#endif
void abuf_init_move(struct abuf *abuf, void *data, size_t size)
{
abuf_init_set(abuf, data, size);

View File

@@ -44,6 +44,8 @@ def parse_args():
help='Select architecture (arm, x86) Default: arm')
parser.add_argument('-B', '--no-build', action='store_true',
help="Don't build; assume a build exists")
parser.add_argument('-C', '--enable-console', action='store_true',
help="Enable linux console (x86 only)")
parser.add_argument('-d', '--disk',
help='Root disk image file to use with QEMU')
parser.add_argument('-D', '--share-dir', metavar='DIR',
@@ -52,11 +54,17 @@ def parse_args():
help='Run UEFI Self-Certification Test (SCT)')
parser.add_argument('-E', '--use-tianocore', action='store_true',
help='Run Tianocore (OVMF) instead of U-Boot')
parser.add_argument('-I', '--initrd',
help='Initial ramdisk to run using -initrd')
parser.add_argument(
'-k', '--kvm', action='store_true',
help='Use KVM (Kernel-based Virtual Machine) for acceleration')
parser.add_argument('-K', '--kernel',
help='Kernel to run using -kernel')
parser.add_argument('-o', '--os', metavar='NAME', choices=['ubuntu'],
help='Run a specified Operating System')
parser.add_argument('-Q', '--use-qboot', action='store_true',
help='Run qboot instead of U-Boot')
parser.add_argument('-r', '--run', action='store_true',
help='Run QEMU with the built/specified image')
parser.add_argument('-x', '--xpl', action='store_true',
@@ -70,6 +78,12 @@ def parse_args():
parser.add_argument(
'-S', '--sct-seq',
help='SCT sequence-file to be written into the SCT image if -e')
parser.add_argument(
'-t', '--root',
help='Pass the given root device to linux via root=xxx')
parser.add_argument(
'-U', '--uuid',
help='Pass the given root device to linux via root=/dev/disk/by-uuid/')
parser.add_argument('-v', '--verbose', action='store_true',
help='Show executed commands')
parser.add_argument('-w', '--word-32bit', action='store_true',
@@ -92,6 +106,7 @@ class BuildQemu:
self.sctdir = Path(self.helper.get_setting('sct_dir', '~/dev/efi/sct'))
self.tiano = Path(self.helper.get_setting('tianocore_dir',
'~/dev/tiano'))
self.qboot = Path(self.helper.get_setting('qboot_dir', '~/dev/qboot'))
self.mnt = Path(self.helper.get_setting('sct_mnt', '/mnt/sct'))
self.bitness = 32 if args.word_32bit else 64
@@ -138,6 +153,12 @@ class BuildQemu:
tout.fatal(
'Error: Tianocore BIOS specified (-E) but not found at '
f'{bios_override}')
elif args.use_qboot:
bios_override = Path(self.qboot, 'bios.bin')
if not bios_override.exists():
tout.fatal(
'Error: qboot BIOS specified (-Q) but not found at '
f'{bios_override}')
self.seq_fname = Path(args.sct_seq) if args.sct_seq else None
self.img_fname = Path(args.disk) if args.disk else None
@@ -281,13 +302,15 @@ class BuildQemu:
if not self.bios.exists():
tout.fatal(f"Error: BIOS file '{self.bios}' not found")
cmdline = []
qemu_cmd = [str(self.qemu)]
if self.bios:
qemu_cmd.extend(['-bios', str(self.bios)])
qemu_cmd.extend(self.kvm_params)
qemu_cmd.extend(['-m', self.mem])
if not self.args.sct_run:
if not self.args.sct_run and not self.qboot:
qemu_cmd.extend(['-netdev', 'user,id=net0,hostfwd=tcp::2222-:22',
'-device', 'virtio-net-pci,netdev=net0'])
@@ -301,6 +324,21 @@ class BuildQemu:
if not any(item.startswith('-serial') for item in self.qemu_extra):
qemu_cmd.extend(['-serial', 'mon:stdio'])
if self.args.kernel:
qemu_cmd.extend(['-kernel', self.args.kernel])
if self.args.initrd:
qemu_cmd.extend(['-initrd', self.args.initrd])
if self.args.enable_console:
cmdline.append('console=ttyS0,115200,8n1')
if self.args.root:
cmdline.append(f'root={self.args.root}')
if self.args.uuid:
cmdline.append(f'root=/dev/disk/by-uuid/{self.args.uuid}')
if cmdline:
qemu_cmd.extend(['-append'] + [' '.join(cmdline)])
# Add other parameters gathered from options
qemu_cmd.extend(self.qemu_extra)
if self.os_path:

View File

@@ -34,7 +34,7 @@ class Helper:
self.settings = configparser.ConfigParser()
fname = f'{os.getenv("HOME")}/.u_boot_qemu'
if not os.path.exists(fname):
print('No config file found: {fname}\nCreating one...\n')
print(f'No config file found: {fname}\nCreating one...\n')
tools.write_file(fname, '''# U-Boot QEMU-scripts config
[DEFAULT]

View File

@@ -1417,6 +1417,7 @@ static int bootstd_images(struct unit_test_state *uts)
const struct legacy_img_hdr *hdr;
const struct bootflow_img *img;
const struct bootflow *bflow;
struct bootflow_img *wimg;
struct bootstd_priv *std;
const char **old_order;
struct udevice *dev;
@@ -1504,6 +1505,15 @@ static int bootstd_images(struct unit_test_state *uts)
ut_assertnull(bootflow_img_find(bflow, BFI_CMDLINE));
/* check we can update an image size */
wimg = bootflow_img_findw(bflow, (enum bootflow_img_t)IH_TYPE_SCRIPT);
ut_assertnonnull(wimg);
wimg->size = 123;
img = bootflow_img_find(bflow, (enum bootflow_img_t)IH_TYPE_SCRIPT);
ut_assertnonnull(img);
ut_asserteq(123, img->size);
ut_assert_console_end();
return 0;

View File

@@ -68,6 +68,28 @@ static int lib_test_abuf_init_const(struct unit_test_state *uts)
}
LIB_TEST(lib_test_abuf_init_const, 0);
/* Test abuf_init_const_addr() */
static int lib_test_abuf_init_const_addr(struct unit_test_state *uts)
{
struct abuf buf;
ulong start;
void *ptr;
start = ut_check_free();
ptr = map_sysmem(0x100, 0);
abuf_init_const_addr(&buf, 0x100, 10);
ut_asserteq_ptr(ptr, buf.data);
ut_asserteq(10, buf.size);
/* No memory should have been allocated */
ut_assertok(ut_check_delta(start));
return 0;
}
LIB_TEST(lib_test_abuf_init_const_addr, 0);
/* Test abuf_map_sysmem() and abuf_addr() */
static int lib_test_abuf_map_sysmem(struct unit_test_state *uts)
{