Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/components/versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ firmware:
libvirt: v10.9.0
edk2: stable202411
core:
3p-kubevirt: v1.6.2-v12n.31
3p-kubevirt: v1.6.2-v12n.32
3p-containerized-data-importer: v1.60.3-v12n.19
distribution: 2.8.3
package:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
index d9f61757cf..d2a0e5f6a1 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
+++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
@@ -2020,6 +2020,14 @@ PlatformBootManagerUnableToBoot (
EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
UINTN Index;

+ {
+ STATIC CONST CHAR8 Msg[] = "No bootable device.\r\n";
+ UINTN i;
+ for (i = 0; i < sizeof (Msg) - 1; i++) {
+ IoWrite8 (0x403, (UINT8)Msg[i]);
+ }
+ }
+
if (FeaturePcdGet (PcdBootRestrictToFirmware)) {
AsciiPrint (
"%a: No bootable option was found.\n",
diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c
index 9bacf9d8c7..ea78559516 100644
--- a/ShellPkg/Application/Shell/Shell.c
+++ b/ShellPkg/Application/Shell/Shell.c
@@ -359,6 +359,14 @@ UefiMain (
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
SPLIT_LIST *Split;

+ {
+ STATIC CONST CHAR8 Msg[] = "No bootable device.\r\n";
+ UINTN i;
+ for (i = 0; i < sizeof (Msg) - 1; i++) {
+ IoWrite8 (0x403, (UINT8)Msg[i]);
+ }
+ }
+
if (PcdGet8 (PcdShellSupportLevel) > 3) {
return (EFI_UNSUPPORTED);
}
diff --git a/ShellPkg/Application/Shell/Shell.h b/ShellPkg/Application/Shell/Shell.h
index 89b4ac6b02..cd82e6f608 100644
--- a/ShellPkg/Application/Shell/Shell.h
+++ b/ShellPkg/Application/Shell/Shell.h
@@ -42,6 +42,7 @@
#include <Library/HandleParsingLib.h>
#include <Library/FileHandleLib.h>
#include <Library/UefiHiiServicesLib.h>
+#include <Library/IoLib.h>

#include "ShellParametersProtocol.h"
#include "ShellProtocol.h"
diff --git a/ShellPkg/Application/Shell/Shell.inf b/ShellPkg/Application/Shell/Shell.inf
index f1e41de133..88b033bc35 100644
--- a/ShellPkg/Application/Shell/Shell.inf
+++ b/ShellPkg/Application/Shell/Shell.inf
@@ -66,6 +66,7 @@
SortLib
HandleParsingLib
UefiHiiServicesLib
+ IoLib

[Guids]
gShellVariableGuid ## SOMETIMES_CONSUMES ## GUID
6 changes: 6 additions & 0 deletions images/edk2/patches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 001-debug-device-no-bootable-device-message.patch
If OVMF cannot find a bootable device, or the firmware drops into the EFI shell,
output `No bootable device.` to the debug port at address `0x403`.

This patch is intended to be used together with the QEMU patch that watches the
debug console and emits a `NO_BOOTABLE_DEVICE` QMP event.
14 changes: 14 additions & 0 deletions images/edk2/werf.inc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ git:
stageDependencies:
install:
- '*.json'
- add: {{ .ModuleDir }}/images/{{ .ImageName }}/patches
to: /src/patches
includePaths:
- '*.patch'
stageDependencies:
install:
- '*.patch'
- add: {{ .ModuleDir }}/images/{{ .ImageName }}/uefi-revocation-list
to: /src/FIRMWARE
includePaths:
Expand Down Expand Up @@ -90,6 +97,13 @@ shell:
submodule update --init --recursive --depth=1
fi

echo "Applying patches..."
for p in /src/patches/*.patch; do
[ -f "$p" ] || continue
echo -n "Apply ${p} ... "
git apply --ignore-space-change --ignore-whitespace ${p} && echo OK || (echo FAIL ; exit 1)
done

---

image: {{ .ModuleNamePrefix }}{{ .ImageName }}
Expand Down
132 changes: 132 additions & 0 deletions images/qemu/patches/002-no-bootable-qmp.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c
index fdb04fe..0cf2325 100644
--- a/hw/char/debugcon.c
+++ b/hw/char/debugcon.c
@@ -26,6 +26,7 @@

#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qapi/qapi-events-control.h"
#include "qemu/module.h"
#include "chardev/char-fe.h"
#include "hw/isa/isa.h"
@@ -34,6 +35,7 @@
#include "qom/object.h"

#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon"
+#define DEBUGCON_NO_BOOTABLE_DEVICE "No bootable device."
OBJECT_DECLARE_SIMPLE_TYPE(ISADebugconState, ISA_DEBUGCON_DEVICE)

//#define DEBUG_DEBUGCON
@@ -42,6 +44,9 @@ typedef struct DebugconState {
MemoryRegion io;
CharBackend chr;
uint32_t readback;
+ bool watch_no_bootable_device;
+ char match_buf[sizeof(DEBUGCON_NO_BOOTABLE_DEVICE) - 1];
+ size_t match_len;
} DebugconState;

struct ISADebugconState {
@@ -51,6 +56,27 @@ struct ISADebugconState {
DebugconState state;
};

+static void debugcon_maybe_emit_no_bootable_device(DebugconState *s,
+ unsigned char ch)
+{
+ if (!s->watch_no_bootable_device) {
+ return;
+ }
+
+ if (s->match_len < sizeof(s->match_buf)) {
+ s->match_buf[s->match_len++] = ch;
+ } else {
+ memmove(s->match_buf, s->match_buf + 1, sizeof(s->match_buf) - 1);
+ s->match_buf[sizeof(s->match_buf) - 1] = ch;
+ }
+
+ if (s->match_len == sizeof(s->match_buf) &&
+ memcmp(s->match_buf, DEBUGCON_NO_BOOTABLE_DEVICE,
+ sizeof(s->match_buf)) == 0) {
+ qapi_event_send_no_bootable_device();
+ }
+}
+
static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val,
unsigned width)
{
@@ -64,6 +90,7 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val,
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&s->chr, &ch, 1);
+ debugcon_maybe_emit_no_bootable_device(s, ch);
}


@@ -118,6 +145,8 @@ static Property debugcon_isa_properties[] = {
DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9),
DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr),
DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9),
+ DEFINE_PROP_BOOL("watch-no-bootable", ISADebugconState,
+ state.watch_no_bootable_device, false),
DEFINE_PROP_END_OF_LIST(),
};

diff --git a/qapi/control.json b/qapi/control.json
index 336386f..e1e727e 100644
--- a/qapi/control.json
+++ b/qapi/control.json
@@ -209,3 +209,13 @@
'*pretty': 'bool',
'chardev': 'str'
} }
+
+##
+# @NO_BOOTABLE_DEVICE:
+#
+# Emitted when `isa-debugcon` with enabled no-bootable watching
+# receives the string "No bootable device.".
+#
+# Since: 9.2
+##
+{ 'event': 'NO_BOOTABLE_DEVICE' }
diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c
index 22957fa..9b4840e 100644
--- a/tests/qtest/qmp-test.c
+++ b/tests/qtest/qmp-test.c
@@ -337,6 +337,25 @@ static void test_qmp_missing_any_arg(void)
qtest_quit(qts);
}

+static void test_qmp_no_bootable_device_event(void)
+{
+ static const char trigger[] = "No bootable device.";
+ QTestState *qts;
+ size_t i;
+
+ qts = qtest_initf("-nodefaults -machine q35 "
+ "-chardev null,id=debugcon "
+ "-device isa-debugcon,iobase=0x402,chardev=debugcon,"
+ "watch-no-bootable=on");
+
+ for (i = 0; i < sizeof(trigger) - 1; i++) {
+ qtest_outb(qts, 0x402, trigger[i]);
+ }
+
+ qtest_qmp_eventwait(qts, "NO_BOOTABLE_DEVICE");
+ qtest_quit(qts);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
@@ -348,6 +367,8 @@ int main(int argc, char *argv[])
#endif
qtest_add_func("qmp/preconfig", test_qmp_preconfig);
qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg);
+ qtest_add_func("qmp/no-bootable-device-event",
+ test_qmp_no_bootable_device_event);

return g_test_run();
}
41 changes: 35 additions & 6 deletions images/qemu/patches/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
# Patches

This directory contains downstream patches applied to the QEMU source during the image build.
Patch files are applied in lexicographical order.

The `seabios/` subdirectory contains firmware patches that are applied separately before the QEMU build.
Its behavior is documented in `images/qemu/patches/seabios/README.md`.

## 001-revert-scsi-disk-serial-truncate.patch

This patch reverts the commit that introduced strict length enforcement for the SCSI disk `serial` property.
Reverts upstream commit
[`75997e182b69`](https://github.com/qemu/qemu/commit/75997e182b695f2e3f0a2d649734952af5caf3ee),
which started rejecting SCSI disk `serial` values that exceed the internal length limits.

Why this patch is kept:

- Older VM definitions relied on the historical QEMU behavior where long serials were accepted.
- The guest-visible value was truncated, but the VM still booted successfully.
- Strict validation turns the same configuration into a startup error and breaks upgrades.

Effect:

- Long `serial` values are accepted again.
- Legacy truncation behavior is preserved instead of failing device initialization.

## 002-no-bootable-qmp.patch

Adds a `NO_BOOTABLE_DEVICE` QMP event that is emitted when `isa-debugcon` device receives the exact
string `No bootable device.` in the debug output stream.

### Background
Why this patch is kept:

Before the reverted commit, scsi-disk accepted serial numbers of arbitrary length, but the value seen by the guest was silently truncated to 36 characters. While this limitation was arbitrary, it ensured compatibility with existing guest behavior. The change to enforce strict length validation introduced potential compatibility issues, making it impossible to upgrade to newer QEMU versions seamlessly.### Why This Revert Is Necessary
- Management components can detect a boot failure through QMP instead of parsing debug logs.
- The event provides a stable signal that can be consumed by automation.
- It is intended to work together with firmware changes that output the marker string to the
debug port.

For the time being, we need to maintain backward compatibility until a seamless migration to the new behavior can be implemented. By reverting the commit, we restore the previous behavior where serial numbers longer than 36 characters are truncated instead of causing an error.
Effect:

### Reverted Commit
[Commit 75997e182b69](https://github.com/qemu/qemu/commit/75997e182b695f2e3f0a2d649734952af5caf3ee)
- `isa-debugcon` gets a new `watch-no-bootable=on` property.
- When enabled, QEMU watches the debug console output and emits `NO_BOOTABLE_DEVICE` after the
full marker string is received.
- The patch also adds a qtest that verifies the event is generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
diff --git a/scripts/layoutrom.py b/scripts/layoutrom.py
index 6616721..4f655dc 100755
--- a/scripts/layoutrom.py
+++ b/scripts/layoutrom.py
@@ -564,7 +564,7 @@ def parseObjDump(file, fileid):

if state == 'section':
try:
- idx, name, size, vma, lma, fileoff, align = line.split()
+ idx, name, size, vma, lma, fileoff, align, *_flags = line.split()
if align[:3] != '2**':
continue
section = Section()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
diff --git a/src/boot.c b/src/boot.c
index 1effd80..ec5992f 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -21,6 +21,7 @@
#include "string.h" // memset
#include "util.h" // irqtimer_calc
#include "tcgbios.h" // tpm_*
+#include "x86.h" // outb

/****************************************************************
* Helper search functions
@@ -960,12 +961,23 @@ boot_rom(u32 vector)
}

// Unable to find bootable device - warn user and eventually retry.
+static void
+write_port_403(const char *s)
+{
+ if (!CONFIG_DEBUG_IO || !runningOnQEMU())
+ return;
+
+ for (; *s; s++)
+ outb(*s, 0x403);
+}
+
static void
boot_fail(void)
{
- if (BootRetryTime == (u32)-1)
+ if (BootRetryTime == (u32)-1) {
printf("No bootable device.\n");
- else
+ write_port_403("No bootable device.\n");
+ } else
printf("No bootable device. Retrying in %d seconds.\n"
, BootRetryTime/1000);
// Wait for 'BootRetryTime' milliseconds and then reboot.
11 changes: 11 additions & 0 deletions images/qemu/patches/seabios/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Patches

## 001-alt-skip-flags-when-parse-objdump-section.patch

This patch makes `scripts/layoutrom.py` tolerate extra flags in `objdump`
section output.

## 002-0x403-debug-port-no-bootable-device-message.patch

If SeaBIOS cannot find a bootable device on QEMU, this patch also outputs
`No bootable device.` to the debug device at address `0x403`.
12 changes: 11 additions & 1 deletion images/qemu/werf.inc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ shell:

fi

echo "Apply SeaBIOS patches"
for p in /patches/seabios/*.patch ; do
[ -f "$p" ] || continue
git -C roms/seabios apply --check --ignore-space-change --ignore-whitespace "$p" >/dev/null 2>&1 || (echo "FAIL" ; exit 1)
echo -n "Apply ${p} to roms/seabios ... "
git -C roms/seabios apply --ignore-space-change --ignore-whitespace "$p" && echo OK || (echo FAIL ; exit 1)
done

---
{{- $name := print .ImageName "-dependencies" -}}
{{- define "$name" -}}
Expand Down Expand Up @@ -201,6 +209,9 @@ shell:

cd /{{ $gitRepoName }}-{{ $version }}

echo "Building patched SeaBIOS..."
make -C roms bios V=1 PYTHON=python3 HOSTCC=gcc -j$(nproc)

for p in /patches/*.patch ; do
echo -n "Apply ${p} ... "
git apply --ignore-space-change --ignore-whitespace ${p} && echo OK || (echo FAIL ; exit 1)
Expand Down Expand Up @@ -346,7 +357,6 @@ shell:

{{- $_ := set $ "ProjectName" (list .ImageName "qemu" | join "/") }}
{{- include "image-build.build" (set $ "BuildCommand" `make -j$(nproc)`) | nindent 6 }}

setup:
- |
/install-qemu.sh --version-num "{{ $version }}" \
Expand Down
Loading