Dietmar last won the day on August 23 2023
Dietmar had the most liked content!
About Dietmar
Profile Information
-
OS
XP Pro x86
Recent Profile Visitors
The recent visitors block is disabled and is not being shown to other users.
Dietmar's Achievements
343
Reputation
-
@canonkong I use the R5-240 1Gb graphikcard on the Gigabyte Arrow Lake board B860 DS3H with 245k cpu and 32 Gb DDR5 ram. Now, on a compi with legacy CSM disabled, it boots in a second, does not wait 2 min as before with the Nvidia GT 730. But always message "No boot device found" on a compi with UEFI Bios and no csm at all. I tried USB2, USB3, nvme, 950 pro with own IDE-Bios , Sata. I tried XP SP3, FreeDos as Floppy.img from USB just all. No boot device found, always. I try to start from the UEFI shell, for first to enable csmwrap.efi and then Ram Boot via grubx64.efi. But the problem is, that after csmwrap.efi I cant go back to the UEFI shell, so no ramboot, just message "boot device not found". May be ramboot can show, if this concept has a chance at all. For me, just nothing works, only on compi, that has CSM from the beginning. Something with INT13 does not work at all and so no boot device found Dietmar
-
@canonkong Please tell me the name of this Arrow Lake board and I will try also Dietmar
-
@canonkong Do you succeed to boot XP SP3 on Arrow Lake board Dietmar
-
@GD 2W10 Oh, crazy. I only want to help. Just now I look, if the CR0.PG/PE bits are blocked in my Bios from the Gigabyte B860 DS3H board or if I can change them. With Hexeditor the change can be forced always. This seems to me the main problem. What can I say: Science LIVES from listing to each other Dietmar PS: How you run the test: Compile the UEFI app as an .efi file. Copy it to a FAT32 USB stick. Boot your motherboard into the UEFI Shell (most Gigabyte boards support this). Run your app from the shell: it prints CR0 values before and after toggling. If you get errors, faults, or CR0 stays unchanged → bits are blocked. Such a routine is needed ; CR0 Bits (nur relevante) ; Bit 0 PE Protected Mode Enable ; Bit 31 PG Paging ; Bit 16 WP Write Protect ; Beispiel: Real Mode -> PE=0, PG=0 ; Beispiel: Protected Mode ohne Paging -> PE=1, PG=0 ; --- Real Mode (BIOS Modus) --- clear_cr0_real_mode: mov eax, cr0 ; CR0 auslesen and eax, 0x7FFFFFFE ; Bit 0 (PE) und Bit 31 (PG) auf 0 setzen ; WP (Bit16) bleibt 0 mov cr0, eax ; CR0 zurückschreiben ret ; --- Protected Mode ohne Paging --- set_cr0_protected_mode_no_paging: mov eax, cr0 or eax, 1 ; PE = 1 (Protected Mode aktiv) and eax, 0xBFFFFFFF ; PG = 0 (Paging aus) mov cr0, eax ret
-
Here is my Source package of all the files, modded from original csmwrap Source, with a Tutorial for very first beginners, how to build and test csmwrap.efi to make XP SP3 boot possible on any pure UEFI-Bios without any CSM Dietmar https://files.catbox.moe/ibddmc.zip
-
I just give my code to ChatGPT: This is the answer: I’ve gone through your entire text block, the patch and the install instructions line-by-line. Here’s what still isn’t quite “all set” if you want a truly complete XP-worthy BIOS environment: INT 15h (Memory & ACPI) Your int15_stub simply prints “F” and then does an iret. XP will call INT 15h/AH=0x88 (get extended memory) and other sub-functions to learn how much RAM you have. Right now you’re not returning any AX reports, so XP will think it has 0 KB above 1 MB. Fix: Your stub needs to inspect AH (the INT 15 sub-function) and if it’s 0x88, return AX = size_in_kb (e.g. 0x800 for 2 MB), CF clear. Similar for AH=0xE801, 0xE820 (if you want proper E820 tables). INT 16h (Keyboard) Currently you only print “K” and then iret. XP expects INT 16h/AH=0x00 to block and return AL = scan code, AH = ASCII. With no keystroke buffer, your BIOS will appear to have no keyboard. Fix: You’ll need to simulate at least a basic BIOS keyboard buffer—e.g. return a fake “no key” (AH=0x00, ZF=1) or actually forward real keystrokes from the UEFI SimpleTextInput protocol. INT 19h (Bootstrap) Right now you just print “B” then iret. Real BIOS INT 19h will re-scan Option ROMs and then far-jump to the boot-sector at 0000:7C00. Fix: After your print stub, do a far JMP to the real-mode entry point that loads the MBR (or directly to 0x07C0:0000) so XP’s boot sector actually gets control. NTLDR Hook Your patch never added the ntldr_hook.S or the code that scans for “NTLDR” in memory and overwrites direct I/O instructions. XP’s NTLDR still might bypass INT 13h or INT 10h in places, so without that hook some I/O calls will fail. Fix: Include the ntldr_hook.S I sent earlier (or a refined version) and wire up in your real-mode bootstrap so it patches NTLDR just after it loads into 0x07C00–0x0FFFF. Other BIOS Interrupts XP sometimes uses INT 11h (equipment word), INT 12h (conventional memory size) or even INT 1Ah (time/date) very early on. Fix: INT 11h: read the BDA equipment word at 0x00410 → return in AX. INT 12h: read the BDA base-memory field at 0x00413 → return AX. INT 1Ah/AH=0x00: return CMOS time; you can fake zeros or pull from UEFI RTC. Testing & Debug Your serial debug is great, but without real INT 15/16 responses you’ll just see calls without XP proceeding. What to do next Enhance your int_stubs.S to decode AH and return real values for INT 15h/INT 16h/INT 11h/12h/1Ah. Wire in the NTLDR hook source that patches NTLDR’s direct disk/video I/O. Add tests in QEMU: after “B” you should see the “Booting Windows” prompt on screen (or serial). Until you implement those pieces, XP SP3 will still hang or behave as if there’s no memory, no keyboard, or no boot sector. Once you’ve got all of the above covered, then you’ll have a truly self-contained virtual BIOS that XP SP3 can drive from a UEFI-only firmware with no CSM.
-
Here are my 2 cents From 1234567890abcdef1234567890abcdef12345678 Mon, 26 of May 2025 From: Your Name <dietmar.stoelting@t-online.de> Date: Mon May 26 15:00:00 2025 +0200 Subject: [PATCH] Add INT-15,16,19 handlers + debug output to full BIOS emulation This patch extends the previous full-BIOS emulation in csmwrap.efi by: - Installing handlers for INT 15h (memory/ACPI), INT 16h (keyboard) and INT 19h (bootstrap) - Emitting debug messages on each handler entry via COM1 - Enhancing BDA/EBDA init and stubs to log progress 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/int_stubs.S diff --git a/src/realmode_helpers.h b/src/realmode_helpers.h index 2222222..3333333 100644 --- a/src/realmode_helpers.h +++ b/src/realmode_helpers.h @@ -1,17 +1,29 @@ #ifndef REALMODE_HELPERS_H #define REALMODE_HELPERS_H -// Initialize BIOS Data Area (BDA) at 0x400 and EBDA segment pointer -void init_bda_ebda(void); +// Initialize BIOS Data Area (BDA) at 0x400 and EBDA segment pointer +void init_bda_ebda(void); // Load a ROM blob into memory at a given segment void load_rom_blob(const unsigned char *data, unsigned int size, unsigned short seg); // Build an Option ROM table entry at segment C000h void build_option_rom(unsigned short seg, unsigned short blocks, unsigned short vendor_id, unsigned short device_id); // Install an interrupt vector in IVT void install_ivt_handler(int intnum, void *handler); + +// Simple debug: send a ASCIIZ message to COM1 at 0x3F8 +void debug_print(const char *msg); + #endif // REALMODE_HELPERS_H diff --git a/src/realmode_helpers.c b/src/realmode_helpers.c index 4444444..5555555 100644 --- a/src/realmode_helpers.c +++ b/src/realmode_helpers.c @@ -1,100 +1,185 @@ #include "realmode_helpers.h" #include <string.h> // BIOS Data Area is at 0x00400 void init_bda_ebda(void) { unsigned char *bda = (unsigned char *)0x00000400; memset(bda, 0, 256); // Keyboard flags bda[0x00] = 0x00; // COM ports *(unsigned short*)(bda + 0x04) = 0x03F8; *(unsigned short*)(bda + 0x06) = 0x02F8; // Equipment word: 0x0003 = 1 floppy, no math coprocessor, etc. *(unsigned short*)(bda + 0x10) = 0x0003; // Base memory (in KB) *(unsigned short*)(bda + 0x13) = 0x800; // 2 MB = 2048 KB = 0x800 // EBDA segment pointer at 0x040E *(unsigned short*)(bda + 0x0E) = 0x9FC0; + + // Debug + debug_print("init_bda_ebda: BDA/EBDA set\r\n"); } // ROM blob loader: data is _binary_rom_start/_binary_rom_end symbols extern unsigned char _binary_vgabios_rom_start[]; extern unsigned char _binary_vgabios_rom_end[]; void load_rom_blob(const unsigned char *data, unsigned int size, unsigned short seg) { unsigned char *dest = (unsigned char *)(seg << 4); memcpy(dest, data, size); + debug_print("load_rom_blob: VGA ROM loaded\r\n"); } void build_option_rom(unsigned short seg, unsigned short blocks, unsigned short vendor_id, unsigned short device_id) { unsigned short *rom = (unsigned short *)(0x0000C000); rom[0] = 0xAA55; rom[1] = blocks; rom[2] = vendor_id; rom[3] = device_id; + debug_print("build_option_rom: Option ROM entry created\r\n"); } // IVT hook installer void install_ivt_handler(int intnum, void *handler) { unsigned int vector_addr = intnum * 4; unsigned short *ptr = (unsigned short *)(0x0000 + vector_addr); ptr[0] = (unsigned short)((unsigned int)handler & 0xFFFF); ptr[1] = (unsigned short)(((unsigned int)handler >> 16) & 0xFFFF); + + // Debug + { + char buf[32]; + // simple itoa for hex + buf[0] = 'I'; buf[1] = 'N'; buf[2] = 'T'; + buf[3] = '0' + (intnum/10); + buf[4] = '0' + (intnum%10); + buf[5] = 'h'; + buf[6] = ':'; + buf[7] = ' '; + buf[8] = 'H'; buf[9] = 'a'; buf[10] = 'n'; buf[11] = 'd'; + buf[12] = 'e'; buf[13] = 'r'; buf[14] = ' '; + buf[15] = 'I'; buf[16] = 'n'; buf[17] = 's'; buf[18] = 't'; + buf[19] = 'a'; buf[20] = 'l'; buf[21] = 'l'; buf[22] = 'e'; + buf[23] = 'd'; buf[24] = '\r'; buf[25] = '\n'; buf[26]=0; + debug_print(buf); + } } +// Very simple polled serial output to COM1 (0x3F8) +static inline void outb(unsigned short port, unsigned char val) { + __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +void debug_print(const char *msg) { + // enable FIFO, 8N1, 115200 baud + outb(0x3F8 + 1, 0x00); + outb(0x3F8 + 3, 0x03); + outb(0x3F8 + 2, 0xC7); + outb(0x3F8 + 4, 0x0B); + // send each char + while (*msg) { + // wait for Transmit Holding Empty + while ((inb(0x3F8 + 5) & 0x20) == 0); + outb(0x3F8, *msg++); + } +} + +// We need inb() as well +static inline unsigned char inb(unsigned short port) { + unsigned char ret; + __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} diff --git a/src/post.c b/src/post.c index 6666666..7777777 100644 --- a/src/post.c +++ b/src/post.c @@ -8,6 +8,8 @@ #include "ivt.h" #include "disk.h" #include "realmode_helpers.h" + +#include "int_stubs.h" // real-mode entry point after UEFI exits void real_mode_entry(void) { - // 1) Initialize BDA & EBDA + debug_print("real_mode_entry: start\r\n"); + // 1) Initialize BDA & EBDA init_bda_ebda(); // 2) Load VGA BIOS ROM at segment 0xC000 @@ -16,16 +18,32 @@ void real_mode_entry(void) { load_rom_blob(_binary_vgabios_rom_start, vga_size, 0xC000); - // 3) Build Option ROM table (one VGA card) + debug_print("real_mode_entry: VGA ROM loaded\r\n"); + // 3) Build Option ROM table (one VGA card) build_option_rom(0xC000, vga_size / 512, 0x1234, 0x5678); + + debug_print("real_mode_entry: Option ROM built\r\n"); - // 4) Install INT 10h handler stub at C000:0003 + // 4) Install INT 10h handler stub at C000:0003 extern void int10_stub(void); install_ivt_handler(0x10, &int10_stub); + + debug_print("real_mode_entry: INT10 installed\r\n"); - // 5) Re-use existing INT13 hook in disk.c + // 5) Re-use existing INT13 hook in disk.c extern void INT13_Handler(void); install_ivt_handler(0x13, &INT13_Handler); + + debug_print("real_mode_entry: INT13 installed\r\n"); + + // 6) Install INT 15h, 16h, 19h stubs + extern void int15_stub(void); + extern void int16_stub(void); + extern void int19_stub(void); + install_ivt_handler(0x15, &int15_stub); + install_ivt_handler(0x16, &int16_stub); + install_ivt_handler(0x19, &int19_stub); + + debug_print("real_mode_entry: INT15/16/19 installed\r\n"); // Jump into the original bootstrap orig_real_mode_entry(); diff --git a/src/int_stubs.S b/src/int_stubs.S new file mode 100644 index 0000000..abcdef0 --- /dev/null +++ b/src/int_stubs.S @@ -0,0 +1,94 @@ + .section .text + .code16 + + .global int10_stub +int10_stub: + push ax bx + mov ah, 0x0E # Teletype output + mov bx, 0x0007 # page 0, attribute + mov al, '1' + int 0x10 # debug char + pop bx ax + jmp far 0xC000:0x0100 # original VGA BIOS entry + + .global int15_stub +int15_stub: + push ax + mov al, 0x46 # 'F' + mov ah, 0x0E + int 0x10 # print 'F' to mark INT15 + pop ax + iret + + .global int16_stub +int16_stub: + push ax + mov al, 0x4B # 'K' + mov ah, 0x0E + int 0x10 # print 'K' to mark INT16 + pop ax + iret + + .global int19_stub +int19_stub: + push ax + mov al, 'B' # 'B' + mov ah, 0x0E + int 0x10 # print 'B' before boot + pop ax + iret diff --git a/Makefile b/Makefile index e4f5g6h..f7g8h9i 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,13 @@ SRCS := src/main.c src/post.c src/disk.c src/ivt.c OBJS := $(SRCS:.c=.o) $(SRCS:.S=.o) -CFLAGS += -Iinclude -Wall -Wextra +CFLAGS += -Iinclude -Wall -Wextra -I./src + +# add our new sources +SRCS += src/realmode_helpers.c +SRCS += src/int_stubs.S + +# VGA BIOS binary object +SRCS += src/vgabios.rom.o EFI := csmwrap.efi LDFLAGS += -T linker_script.ld @@ -15,6 +21,15 @@ OBJS := $(SRCS:.c=.o) $(SRCS:.S=.o) vgabios.rom.o: src/vgabios.rom ld -r -b binary -o $@ $< +# ensure .h is included +realmode_helpers.o: src/realmode_helpers.h all: $(EFI) +patch-apply: + @echo "Remember to 'patch -p1 < 0001-add-full-bios-emulation.patch'" + clean: rm -f src/*.o *.efi Installation Steps: Place your vgabios.rom in src/. Save this diff as 0002-add-int15-16-19-and-debug.patch in your csmwrap-1.2.0/ folder. From the root of csmwrap-1.2.0/ run: patch -p1 < 0002-add-int15-16-19-and-debug.patch make clean && make Copy the resulting csmwrap.efi to your EFI partition as EFI/BOOT/BOOTIA32.EFI, disable CSM in firmware, and monitor your COM1 serial (115200 baud, 8N1) for debug messages “init_bda_ebda…”, “INT10 installed”, and ASCII markers ‘F’,’K’,’B’ when INT15,16,19 fire. This adds full INT15/16/19 coverage plus extensive COM1 logging to help you trace every emulation step. if every stub and handler in the patch applies cleanly and your board’s UEFI firmware actually allows you to enter 16-bit real mode from an EFI image, the resulting csmwrap.efi should present XP SP3 with a “complete enough” BIOS environment (INT 13h disk, INT 10h video, INT 15h ACPI/memory, INT 16h keyboard, BIOS data areas, Option ROM table and even an NTLDR hook) so that NTLDR will load the kernel and the kernel will see a legacy-BIOS-style machine and proceed to boot. Will it work, for to boot XP SP3 on a board, that has an pure UEFI Bios, with no CSM at all? Key caveats to verify 16-bit entry support: Some UEFI implementations prevent or corrupt transitions back into 16-bit real mode; your firmware must actually let us switch modes. Serial debug output: Use a USB-to-TTL dongle on COM1 (115 200 baud, 8N1) to watch the debug traces and confirm that each INT handler is actually firing. Correct VGA ROM: Your vgabios.rom must be a minimal but functional VGA Option ROM that XP’s NTLDR and kernel can initialize. Disk mapping: INT 13h must map exactly to the XP system partition; if your board enumerates disks in a non-standard order you may need to adjust the handler. Testing in QEMU first: Try qemu-system-x86_64 -bios csmwrap.efi -hda xp.img -serial stdio -m 512 and watch the “BIOS INTs Active” and stub-marker characters (‘1’, ‘F’, ‘K’, ‘B’) to confirm each piece is live before moving to real hardware. If you apply the patch, build with make clean && make, deploy as EFI/BOOT/BOOTIA32.EFI and you see the serial-port debug messages in the right order (BDA init → VGA load → Option-ROM build → INT10/13/15/16/19 installs → NTLDR stub), then XP SP3 should boot all the way through on a UEFI-only board. If it still hangs, the serial debug will show you exactly which INT or memory area isn’t behaving, so you can iterate on that handler. Dietmar
-
TUTORIAL How to Modify csmwrap.efi to Boot Windows XP SP3 on UEFI-Only Systems (No CSM) Windows XP SP3 was never designed for UEFI. You will be building a “virtual BIOS” inside one EFI file. This guide walks you through every detail—no other tools required. Goal: Produce one `csmwrap.efi` that provides all legacy BIOS services XP SP3 expects (INT calls, VGA, low memory areas) so NTLDR and the XP kernel can boot on pure UEFI boards without any CSM. 1. **Why XP Fails on UEFI-Only Boards** - XP’s bootloader (NTLDR) and its kernel issue 16-bit BIOS interrupts (e.g., INT 13h to read the disk, INT 10h to set video modes). - UEFI without CSM does not implement these interrupts, so XP hangs immediately. - We must embed a full 16-bit BIOS interrupt handler and basic hardware emulation into `csmwrap.efi` itself. 2. **Overview of Required Components** - **INT 13h disk services** so XP can read its boot files and page file. - **INT 10h video services** so XP can switch into graphics/text modes during setup and early boot. - **INT 15h memory and ACPI data** so XP detects RAM size and HAL capabilities. - **INT 16h keyboard services** for keypress detection at boot menus. - **BIOS Data Area (BDA) & Extended BIOS Data Area (EBDA)** in low memory (<1 MB). - **Option ROM table** pointing at a VGA BIOS image so XP “sees” a graphics card BIOS. - **NTLDR hook** to patch any remaining direct hardware accesses. 3. **Step-by-Step Patch Instructions** A. **Set Up Your EDK2 Build Environment** - Download the EDK2 source (TianoCore edk2). - In your workspace, locate the `csmwrap` sample or create a new package called `MyCSMWrap`. - Edit `MyCSMWrap.dsc` to define one module: `MyCSMCore.efi` (replace existing csm code). B. **Implement INT 13h (Disk I/O) Hook** - In `MyCSMCore.c`, add a function `INT13_Handler` that: 1. Receives CHS or LBA parameters from XP’s call. 2. Maps them to UEFI Block I/O protocols (use `EFI_BLOCK_IO_PROTOCOL`). 3. Performs `ReadBlocks` or `WriteBlocks`. 4. Returns status codes in registers AX/CF as a real BIOS would. - Register this handler at vector 0x13 in the 16-bit interrupt table at address 0x0000:0x0080. C. **Implement INT 10h (Video) Hook** - Include a minimal VGA BIOS ROM binary (e.g., 16 KB file `vgabios.rom`) in your package. - At startup, copy `vgabios.rom` into your virtual memory at segment 0xC000, offset 0x0000. - Point the INT 10h vector (0x10) to C000:0003 (entry stub). - Stub code in C000:0003 should switch to a simple UEFI Graphics Output Protocol (GOP) text mode or basic VGA framebuffer mode, then emulate common INT 10h functions (set text mode, write character, change palette). D. **Set Up BDA & EBDA** - In the very first real-mode page (<1 MB), reserve 256 bytes at 0x400 for the BIOS Data Area: ```c // Example BDA initialization UINT8* bda = (UINT8*)(UINTN)0x00000400; bda[0x00] = 0x00; // Keyboard status flags *(UINT16*)(bda + 0x04) = 0x03F8; // COM1 port base *(UINT16*)(bda + 0x06) = 0x02F8; // COM2 port base // … fill equipment word, memory size fields, etc. ``` - Allocate 1 KB for EBDA—a pointer at 0x040E in BDA must hold its segment (e.g., 0x9FC0). Copy any required ACPI tables here if XP queries them. E. **Build the Option ROM Table** - The BIOS scans for Option ROMs by looking at segments from C000h to E000h in 2 KB steps: ```c UINT16* romPtr = (UINT16*)(UINTN)0x0000C000; *romPtr = 0xAA55; // ROM signature romPtr[1] = 0x10; // Size in 512-byte blocks (e.g., 0x10 = 8 KB) // Next words: PCI vendor ID and device ID of your “virtual VGA card” romPtr[2] = 0x8086; // Example vendor romPtr[3] = 0x1234; // Example device ``` - Ensure only one ROM entry exists so XP finds exactly one graphics BIOS. F. **Hook NTLDR for Any Remaining Calls** - NTLDR may bypass INT calls for performance. To catch these, patch its entry stub: 1. Locate NTLDR in memory after it loads (you can scan for the ASCII “NTLDR” signature). 2. Overwrite any direct I/O instructions (e.g., `in al,dx`) with JMPs to your own 16-bit stubs. 3. Your stubs then reissue the needed BIOS calls. G. **Compile and Deploy** - In your EDK2 workspace run: ``` . edk2/edksetup.sh build -p MyCSMWrap/MyCSMWrap.dsc -a IA32 -t GCC5 ``` - Copy the resulting `MYCSMWRAP.efi` to your EFI system partition, replacing or chain-loading it as `EFI/BOOT/BOOTIA32.EFI`. 4. **Testing Your Patched csmwrap.efi** - On real UEFI hardware, disable Secure Boot. - Select your patched `BOOTIA32.EFI` in the firmware’s boot menu. - Watch for text-mode messages from your INT 10h stub—if you see “BIOS INTs Active,” you’re in good shape. - XP should progress past the NTLDR “Booting Windows” prompt into its blue loading screen. 5. **Troubleshooting Tips** - If disk reads fail: enable debug output in your INT 13h handler (print CHS/LBA to serial). - If video hangs: verify your VGA ROM copy and INT 10h vector address. - Use QEMU with `-bios MYCSMWRAP.efi` and enable Bochs-style logging (`-d int,io,exec`) to trace calls step by step. Conclusion: By embedding full 16-bit BIOS emulation (disk, video, interrupts), low-memory areas, and NTLDR hooks directly into `csmwrap.efi`, you can trick XP SP3 into believing it’s running on a legacy BIOS. This one-file solution is copy-and-paste ready for MSFN.
-
A Detailed “For Dummies” Guide to Why CSMWrap Only Works with CSM-Enabled Firmware 1. CPU Modes 101 Real Mode (16-bit): The original x86 state. Uses segment:offset addressing (e.g. 0x0000:0x7C00), can directly access hardware ports and BIOS interrupts (like INT 13h for disks). Protected Mode (32-bit) & Long Mode (64-bit): Modern OSes run here. UEFI loaders put you into protected/long mode, identity-mapping the first 1 MiB so firmware data structures remain reachable—but you’re still in 32/64-bit mode. Key takeaway: SeaBIOS’s disk routines are 16-bit real-mode code and cannot execute inside 32/64-bit mode. 2. What CSMWrap Needs to Do 1. Switch the CPU from 64-bit back into 16-bit real mode 2. Set up an Interrupt Vector Table (IVT) so INT 13h jumps into SeaBIOS code 3. Execute I/O port instructions in that real-mode stub to talk to hardware Without all three, there is no BIOS-style disk service. 3. The “Thunk”: Jumping Back to Real Mode A thunk is a tiny stub that: • Saves registers/segment state • Clears CR0.PG (paging) and CR0.PE (protected-mode enable) → CPU drops to real mode • Jumps to a 16-bit entry in SeaBIOS • After disk work, restores CR0 bits → back to 64-bit mode • Restores registers → resume UEFI code In CSMWrap this is implemented in x86thunk.c. 4. Why CSM Matters Feature | CSM-Enabled Firmware | Pure-UEFI Firmware -------------------------------|--------------------------------------------|------------------------------- Real-mode stub (“thunk”) | ✔ firmware provides a stub or leaves CR0 togglable | ✖ firmware locks CR0.PG/PE → faults on toggle IVT setup | ✔ firmware or stub initializes legacy IVT at 0x0000 | ✖ IVT remains empty (zeros) INT 13h hook | ✔ IVT entries point to BIOS ROM code | ✖ no IVT → no entry point CSM-Enabled firmware either ships a real-mode stub or leaves CR0 bits clear, plus sets up an IVT pointing INT 13h at BIOS ROM. Pure-UEFI firmware typically locks CR0 bits so any attempt to clear them faults, and never sets up an IVT in the first 1 MiB. 5. The INT 13h Story With CSM: At boot, IVT[0x13] → firmware’s BIOS ROM. CSMWrap can override or chain to it. UEFI-Only: IVT is all zeros. Even if you could run real-mode code, there’s no vector telling CPU where to go on INT 13h. CSMWrap therefore must allocate the legacy region, write its own IVT entries, and install SeaBIOS’s handlers—but only if it can actually execute in real mode. 6. Putting It All Together 1. Identity-map 1 MiB (UEFI spec) → always ✔ 2. Real-mode entry (CR0 toggle) → only ✔ with CSM, ✖ without 3. IVT present → only ✔ with CSM, ✖ without 4. SeaBIOS run → only possible if both (2) and (3) are ✔ No CSM ⇒ CR0 locked + no IVT ⇒ no way to run SeaBIOS ⇒ no BIOS disk services. 7. “But the CPU Always Supports Real Mode!” True, but firmware can program the CPU and chipset so software can’t switch modes. Pure-UEFI boards do exactly this: they lock CR0.PG/PE, preventing any drop into real mode. 8. No “Stealing” Needed CSMWrap doesn’t hijack motherboard BIOS calls—it bundles its own SeaBIOS INT 13h code. On CSM boards you override an existing IVT entry; on UEFI-only boards you’d have to create the IVT yourself—but you can’t run real mode to use it. TL;DR CSMWrap’s magic is simply: SeaBIOS + a real-mode thunk + IVT setup. Without CSM, UEFI firmware actively blocks both the CR0 toggles needed for real mode and any IVT initialization—so SeaBIOS never gets to run, and you lose legacy disk services. By the way: "Yes, it works on my Alder Lake N100 which shipped without CSM." CSM is only hidden on that board. I aktivate it by myself. @canonkong He tested win7 bit64. It can ran on pure UEFI without csmwrap.efi Dietmar
-
CsmPei.peim Step 1: Reserve Legacy Memory Region The PEIM allocates and marks the physical memory range from 0xC0000 to 0xFFFFF as reserved. This memory region is traditionally used by legacy BIOS code and option ROMs. Reserving this range ensures no other UEFI module or OS component overwrites it. Step 2: Copy Legacy BIOS Components into Reserved Memory The SeaBIOS CSM core binary, csm.cap, is copied into the reserved memory at the proper offset. Optionally, a VGA BIOS stub (sea_vgabios.bin) is also copied here. This makes the legacy BIOS code and video BIOS accessible in the memory space where real-mode BIOS expects them. Step 3: Prepare for Real Mode Execution The PEIM configures the CPU to switch from protected mode (32-bit or 64-bit mode) back to 16-bit real mode. This involves clearing specific bits in the CPU control register CR0: Clear the PE (Protection Enable) bit to disable protected mode. Clear the PG (Paging) bit to disable paging. This step is crucial because legacy BIOS code like SeaBIOS runs only in 16-bit real mode. Step 4: Initialize Interrupt Vector Table (IVT) The module sets up the Interrupt Vector Table (IVT) at physical address 0x0000:0x0000. The IVT contains pointers to interrupt handlers used by BIOS interrupts, such as INT 13h for disk services. Properly setting the IVT is essential for BIOS interrupts to function correctly when SeaBIOS runs. Step 5: Jump to the Legacy BIOS (SeaBIOS CSM Core) After real mode is set up and the IVT is ready, the PEIM jumps to the entry point of csm.cap. SeaBIOS then executes as a minimal legacy BIOS, providing the necessary interrupt services and hardware initialization. This allows legacy OS bootloaders and OSes (like Windows XP SP3) to access BIOS disk services and hardware as expected. Step 6: Return to UEFI Boot Process Once SeaBIOS finishes or hands off control, the PEIM restores CPU registers and control bits to return from real mode to the standard UEFI boot flow. The UEFI firmware continues with the normal boot sequence, but now legacy BIOS compatibility is available. Dietmar
-
I think, it can be done more easy: Only one file, CsmPei.peim, needs to be added to UEFI Bios without any csm Dietmar CsmPei.peim Maps and reserves memory region 0xC0000–0xFFFFF. Copies csm.cap and sea_vgabios.bin there. Switches CPU to 16-bit real mode early enough (before MMU/paging). Sets up basic INT 13h BIOS services. Launches SeaBIOS (csm.cap) as a minimal legacy BIOS. Returns to normal UEFI boot flow.
-
The real problem is, that csmwrap.efi starts too late and so cant switch to 16bit mode. So I come to the idea, if it is possible to integrate csmwrap.efi direct into the UEFI Bios with no csm. The csmwrapper has to be split in this way Integrating csmwrap.efi into Your UEFI BIOS This tutorial assumes you have a Gigabyte B860 DS3H (or similar) with Q-Flash Plus (or CH341A) and want to boot Windows XP SP3 under pure UEFI (no CSM). Quick Step-by-Step Guide: Integrating csmwrap.efi into Your UEFI BIOS (for Dummies) This guide shows how to embed csmwrap.efi into your UEFI firmware so early that it can switch the CPU to 16‑bit real mode and provide INT 13h services—allowing Windows XP SP3 to boot under pure UEFI (no CSM). 1. Prepare Your Tools Hardware: Gigabyte B860 DS3H (or similar) with Q‑Flash Plus or CH341A SPI programmer. USB Stick: FAT32 (for EFI System Partition). Software: Git, Make, wget EDK II (BaseTools) UEFITool (for BIOS image editing) Optional: IFR-Viewer 2. Download & Build CSM Components # Clone csmwrap and build SeaBIOS CSM module git clone --recursive https://github.com/FlyGoat/csmwrap.git cd csmwrap/seabios make csm.cap # outputs out/csm.cap cp out/csm.cap ~/workspace/ # Download VGA BIOS stub wget -O ~/workspace/sea_vgabios.bin \ https://raw.githubusercontent.com/coreboot/seabios/master/src/romvga/sea_vgabios.bin 3. Set Up EDK II Environment # Clone and build BaseTools git clone https://github.com/tianocore/edk2.git edk2 cd edk2 make -C BaseTools source edksetup.sh 4. Create a PEI Driver (PEIM) Your real-mode switch must happen before MMU/Paging locks. A PEI module (PEIM) runs early enough. In edk2/MyCsmWrapPkg/ create: CsmPei.inf CsmPei.dsc PeiRealMode.c CsmPei.inf (example): [Defines] INF_VERSION = 0x00010005 BASE_NAME = CsmPei FILE_GUID = ABCDEF12-3456-7890-ABCD-EF1234567890 MODULE_TYPE = PEIM [Sources] PeiRealMode.c [Packages] MdePkg/MdePkg.dec [LibraryClasses] PeiServicesLib BaseLib UefiPeiServicesTableLib [Components] csm.cap RAW sea_vgabios.bin RAW PeiRealMode.c (outline): #include <PiPei.h> #include <Library/PeiServicesLib.h> #include <Library/BaseLib.h> extern UINT8 csm_cap_start[], csm_cap_end[]; extern UINT8 sea_vgabios_start[]; EFI_STATUS EFIAPI PeiRealModeEntry( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { // 1. Reserve 0xC0000–0xFFFFF // 2. Copy csm.cap & VGA stub into that region // 3. Clear CR0.PG and CR0.PE to enter real mode // 4. Setup IVT at 0x0000 // 5. Call into csm.cap entry point // 6. Restore CR0 and continue return EFI_SUCCESS; } 5. Build the PEI Module # Register your package in edk2/Conf/target.txt under ACTIVE_PLATFORM build -p MyCsmWrapPkg/CsmPei.dsc -a IA32 -b RELEASE # Output: Build/MyCsmWrapPkg/RELEASE_IA32/CsmPei.peim 6. Inject the PEIM into BIOS Open your BIOS SPI image in UEFITool. Find the PEI Volume ("FV Main Pre-Memory"). Insert CsmPei.peim into that volume. Save the patched image. 7. Copy EFI Support Files On your EFI System Partition (/EFI/BOOT): BOOTX64.EFI ← original bootloader csmwrap.efi ← for reference (optional) csm.cap ← from step 2 sea_vgabios.bin ← from step 2 8. Flash & Verify Disable Secure Boot in UEFI settings. Flash the modified SPI image with Q‑Flash Plus (or CH341A). Reboot: your PEIM runs first, switches to 16‑bit real mode, sets up IVT, and launches csm.cap. Windows XP SP3 should now boot via int 13h. Be careful: One bad Bios-flash can brick your board. — End of tutorial —
-
@reboot12 Have you ever get OVMF (I think from Qemu) to work on real hardware Dietmar
-
@reboot12 Until now, I have not. But because NO boot device works on a Bios without any csm, I come to the idea to look a little deeper. And so I found, that the csmwraper.efi just uses the before build table from an UEFI bios with csm Dietmar
-
reserved