src/boot.c | 36 ++++++++++++++++++++++++++++++++++++ src/post.c | 4 ++++ src/romlayout.S | 4 ++++ src/util.h | 1 + 4 files changed, 45 insertions(+)
Implement catch-all mechanism to handle invalid boot loaders that execute
random instructions and reach the VGA hole at 0xa0000, which would lead to
VM crashes with KVM_INTERNAL_ERROR.
When a BIOS boot loader gets corrupted, it can end up jumping across
address space and execute stray code. The typical symptom of that is
that it executes 0x0 (addw) instructions until the code reaches an MMIO
region, such as the VGA window. When running in KVM, attempting to
execute code from the MMIO window results in KVM_INTERNAL_ERROR exits
which crash the VM.
To prevent VM crashes before we reach such an MMIO window, introduce an
internal int 0xf0 handler and call it at strategic locations that should
never get executed in the first place. When we now have stray code
executing, these int calls cause an emergency print of "BIOS failed to
boot volume" and cleanly shut down the machine.
This is a nicer experience for users as it prints out why their system
broke and in addition it avoids KVM_INTERNAL_ERROR calls when a virtual
machine attempts to execute from MMIO because of a broken boot loader.
Signed-off-by: Alexander Graf <graf@amazon.com>
---
v1 -> v2:
- Clarify error message
- Move 32bit handler entry to similar other interrupt code
---
src/boot.c | 36 ++++++++++++++++++++++++++++++++++++
src/post.c | 4 ++++
src/romlayout.S | 4 ++++
src/util.h | 1 +
4 files changed, 45 insertions(+)
diff --git a/src/boot.c b/src/boot.c
index 5c37dafd..fd8bdf01 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -1044,3 +1044,39 @@ handle_19(void)
BootSequence = 0;
do_boot(0);
}
+
+// INT f0h Boot Failure Service Entry Point
+void VISIBLE32FLAT
+handle_f0(void)
+{
+ printf("\n\nBIOS failed to boot volume. The boot loader may be corrupted.\n\n ");
+
+ /* Try to shut down. Will busy loop on failure to shut down. */
+ apm_shutdown();
+}
+
+static const u8 catchall[0x10] = {
+ 0xcd, // INT
+ 0xf0, // interrupt number 0xF0
+ 0xeb, // JMP short
+ 0xfc, // -3 (jump back to INT)
+};
+
+static void
+install_bootfail_catchall_one(u8 *catchall_addr)
+{
+ memcpy(catchall_addr, catchall, sizeof(catchall));
+}
+
+/*
+ * Install the catch-all code just before VGA hole at 0xa0000 and at the end
+ * of the PMM zero region.
+ */
+void
+install_bootfail_catchall(void)
+{
+ /* Install just before the VGA hole */
+ install_bootfail_catchall_one((u8*)BUILD_LOWRAM_END - sizeof(catchall));
+ /* and after the PMM zero region */
+ install_bootfail_catchall_one((u8*)BUILD_EBDA_MINIMUM);
+}
diff --git a/src/post.c b/src/post.c
index f93106a1..bdacbdb8 100644
--- a/src/post.c
+++ b/src/post.c
@@ -68,6 +68,9 @@ ivt_init(void)
// set vector 0x79 to zero
// this is used by 'gardian angel' protection system
SET_IVT(0x79, SEGOFF(0, 0));
+
+ // Boot failure catch-all interrupt (INT 0xF0)
+ SET_IVT(0xf0, FUNC16(entry_f0));
}
static void
@@ -115,6 +118,7 @@ interface_init(void)
// Other interfaces
boot_init();
+ install_bootfail_catchall();
bios32_init();
pmm_init();
pnp_init();
diff --git a/src/romlayout.S b/src/romlayout.S
index c4a4635e..e8feae9c 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -580,6 +580,10 @@ entry_19:
entry_18:
ENTRY_INTO32 _cfunc32flat_handle_18
+ // int f0 is SeaBIOS specific and used to handle early boot breakage
+entry_f0:
+ ENTRY_INTO32 _cfunc32flat_handle_f0
+
/****************************************************************
* Fixed position entry points
diff --git a/src/util.h b/src/util.h
index aff8e888..3c5d075c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -47,6 +47,7 @@ int boot_lchs_find_scsi_device(struct pci_device *pci, int target, int lun,
struct chs_s *chs);
int boot_lchs_find_ata_device(struct pci_device *pci, int chanid, int slave,
struct chs_s *chs);
+void install_bootfail_catchall(void);
// bootsplash.c
void enable_vga_console(void);
--
2.47.1
Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
On Fri, Jan 16, 2026 at 10:51:39AM +0000, Alexander Graf via SeaBIOS wrote: > Implement catch-all mechanism to handle invalid boot loaders that execute > random instructions and reach the VGA hole at 0xa0000, which would lead to > VM crashes with KVM_INTERNAL_ERROR. > > When a BIOS boot loader gets corrupted, it can end up jumping across > address space and execute stray code. The typical symptom of that is > that it executes 0x0 (addw) instructions until the code reaches an MMIO > region, such as the VGA window. When running in KVM, attempting to > execute code from the MMIO window results in KVM_INTERNAL_ERROR exits > which crash the VM. > > To prevent VM crashes before we reach such an MMIO window, introduce an > internal int 0xf0 handler and call it at strategic locations that should > never get executed in the first place. When we now have stray code > executing, these int calls cause an emergency print of "BIOS failed to > boot volume" and cleanly shut down the machine. Unless I'm missing something, "int 0xf0" isn't a standard. I don't think it is a good idea to introduce seabios specific real-mode software interrupt handlers. We've avoiding doing this in the past, because of the confusion it can cause. (In short, we never know if some old dos-era program has its own expectations about unusual int handlers.) Ralph Brown's interrupt list has this to say about 0xf0: INT F0 - BASICA.COM, GWBASIC, compiled BASIC - ORIGINAL INT 08 VECTOR Note: BASICA.COM does not restore vector on termination SeeAlso: INT EF"BASIC" If there is a well defined standard "panic" interrupt handler then we could use that. Otherwise, it should also be fine to load code into the ebda/lowmem areas that somehow panics without relying on a new interrupt handler. > > This is a nicer experience for users as it prints out why their system > broke and in addition it avoids KVM_INTERNAL_ERROR calls when a virtual > machine attempts to execute from MMIO because of a broken boot loader. > > Signed-off-by: Alexander Graf <graf@amazon.com> Cheers, -Kevin _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
Hey Kevin, On 20.01.26 18:07, Kevin O'Connor wrote: > On Fri, Jan 16, 2026 at 10:51:39AM +0000, Alexander Graf via SeaBIOS wrote: >> Implement catch-all mechanism to handle invalid boot loaders that execute >> random instructions and reach the VGA hole at 0xa0000, which would lead to >> VM crashes with KVM_INTERNAL_ERROR. >> >> When a BIOS boot loader gets corrupted, it can end up jumping across >> address space and execute stray code. The typical symptom of that is >> that it executes 0x0 (addw) instructions until the code reaches an MMIO >> region, such as the VGA window. When running in KVM, attempting to >> execute code from the MMIO window results in KVM_INTERNAL_ERROR exits >> which crash the VM. >> >> To prevent VM crashes before we reach such an MMIO window, introduce an >> internal int 0xf0 handler and call it at strategic locations that should >> never get executed in the first place. When we now have stray code >> executing, these int calls cause an emergency print of "BIOS failed to >> boot volume" and cleanly shut down the machine. > Unless I'm missing something, "int 0xf0" isn't a standard. I don't > think it is a good idea to introduce seabios specific real-mode > software interrupt handlers. We've avoiding doing this in the past, > because of the confusion it can cause. (In short, we never know if > some old dos-era program has its own expectations about unusual int > handlers.) > > Ralph Brown's interrupt list has this to say about 0xf0: > > INT F0 - BASICA.COM, GWBASIC, compiled BASIC - ORIGINAL INT 08 VECTOR > Note: BASICA.COM does not restore vector on termination > SeeAlso: INT EF"BASIC" > > If there is a well defined standard "panic" interrupt handler then we > could use that. Otherwise, it should also be fine to load code into > the ebda/lowmem areas that somehow panics without relying on a new > interrupt handler. I don't think there is a well defined panic handler as interrupt. I just picked f0 at random because it seemed unused. I do understand your argument that someone else may end up using and and could be getting confused if there is something present. However, I managed to work around all of the interrupt logic by just doing a long jump instead. As bonus, I also managed to get rid of the hand crafted opcodes and instead everything is at least assembly code now. Turned out much nicer anyway :) Thanks! Alex Amazon Web Services Development Center Germany GmbH Tamara-Danz-Str. 13 10243 Berlin Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B Sitz: Berlin Ust-ID: DE 365 538 597 _______________________________________________ SeaBIOS mailing list -- seabios@seabios.org To unsubscribe send an email to seabios-leave@seabios.org
© 2016 - 2026 Red Hat, Inc.