diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h
index 359d45a..1e004b7 100644
--- a/hw/fw_cfg.h
+++ b/hw/fw_cfg.h
@@ -17,7 +17,10 @@
 #define FW_CFG_NUMA             0x0d
 #define FW_CFG_BOOT_MENU        0x0e
 #define FW_CFG_MAX_CPUS         0x0f
-#define FW_CFG_MAX_ENTRY        0x10
+#define FW_CFG_KERNEL_ENTRY     0x10
+#define FW_CFG_KERNEL_DATA      0x11
+#define FW_CFG_INITRD_DATA      0x12
+#define FW_CFG_MAX_ENTRY        0x13
 
 #define FW_CFG_WRITE_CHANNEL    0x4000
 #define FW_CFG_ARCH_LOCAL       0x8000
diff --git a/hw/pc.c b/hw/pc.c
index bf4718e..55bd1a4 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -603,6 +603,8 @@ static int load_multiboot(void *fw_cfg,
     uint32_t mb_mod_end;
     uint8_t bootinfo[0x500];
     uint32_t cmdline = 0x200;
+    uint8_t *mb_kernel_data;
+    uint8_t *mb_bootinfo_data;
 
     /* Ok, let's see if it is a multiboot image.
        The header is 12x32bit long, so the latest entry may be 8192 - 48. */
@@ -643,6 +645,12 @@ static int load_multiboot(void *fw_cfg,
         mh_load_addr = mh_entry_addr = elf_entry;
         mb_kernel_size = kernel_size;
 
+        mb_kernel_data = qemu_malloc(mb_kernel_size);
+        if (rom_copy(mb_kernel_data, elf_entry, kernel_size) != kernel_size) {
+            fprintf(stderr, "Error while fetching elf kernel from rom\n");
+            exit(1);
+        }
+
 #ifdef DEBUG_MULTIBOOT
         fprintf(stderr, "qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
                 mb_kernel_size, (size_t)mh_entry_addr);
@@ -656,7 +664,6 @@ static int load_multiboot(void *fw_cfg,
         uint32_t mh_bss_end_addr = ldl_p(header+i+24);
 #endif
         uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
-        uint8_t *kernel;
 
         mh_entry_addr = ldl_p(header+i+28);
         mb_kernel_size = get_file_size(f) - mb_kernel_text_offset;
@@ -676,12 +683,9 @@ static int load_multiboot(void *fw_cfg,
                 mb_kernel_size, mh_load_addr);
 #endif
 
-        kernel = qemu_malloc(mb_kernel_size);
+        mb_kernel_data = qemu_malloc(mb_kernel_size);
         fseek(f, mb_kernel_text_offset, SEEK_SET);
-        fread(kernel, 1, mb_kernel_size, f);
-        rom_add_blob_fixed(kernel_filename, kernel, mb_kernel_size,
-                           mh_load_addr);
-        qemu_free(kernel);
+        fread(mb_kernel_data, 1, mb_kernel_size, f);
         fclose(f);
     }
 
@@ -732,9 +736,14 @@ static int load_multiboot(void *fw_cfg,
                 exit(1);
             }
             mb_mod_end = mb_mod_start + mb_mod_length;
-            rom_add_file_fixed(initrd_filename, mb_mod_start);
-
             mb_mod_count++;
+
+            /* append module data at the end of last module */
+            mb_kernel_data = qemu_realloc(mb_kernel_data,
+                                          mh_load_addr - mb_mod_end);
+            load_image(initrd_filename,
+                       mb_kernel_data + mb_mod_start - mh_load_addr);
+
             stl_p(bootinfo + mb_mod_info + 0, mb_mod_start);
             stl_p(bootinfo + mb_mod_info + 4, mb_mod_start + mb_mod_length);
             stl_p(bootinfo + mb_mod_info + 12, 0x0); /* reserved */
@@ -774,13 +783,21 @@ static int load_multiboot(void *fw_cfg,
     fprintf(stderr, "multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
 #endif
 
+    /* save bootinfo off the stack */
+    mb_bootinfo_data = qemu_malloc(sizeof(bootinfo));
+    memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo));
+
     /* Pass variables to option rom */
-    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_entry_addr);
-    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, mb_bootinfo);
-    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, mmap_addr);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mb_mod_end - mh_load_addr);
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, mb_kernel_data,
+                     mb_mod_end - mh_load_addr);
 
-    rom_add_blob_fixed("multiboot-info", bootinfo, sizeof(bootinfo),
-                       mb_bootinfo);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, mb_bootinfo);
+    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo));
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data,
+                     sizeof(bootinfo));
 
     option_rom[nb_option_roms] = "multiboot.bin";
     nb_option_roms++;
diff --git a/pc-bios/optionrom/multiboot.S b/pc-bios/optionrom/multiboot.S
index e6cbefd..dafac73 100644
--- a/pc-bios/optionrom/multiboot.S
+++ b/pc-bios/optionrom/multiboot.S
@@ -26,6 +26,14 @@
 
 #define MULTIBOOT_MAGIC		0x2badb002
 
+#define GS_PROT_JUMP		0
+#define GS_GDT_DESC		6
+
+/* Break the translation block flow so -d cpu shows us values */
+#define DEBUG_HERE \
+	jmp		1f;				\
+	1:
+	
 /* Read a variable from the fw_cfg device.
    Clobbers:	%edx
    Out:		%eax */
@@ -44,12 +52,31 @@
 	bswap		%eax
 .endm
 
+/*
+ * Read a blob from the fw_cfg device.
+ * Requires _ADDR, _SIZE and _DATA values for the parameter.
+ *
+ * Clobbers:	%eax, %edx, %es, %ecx, %edi
+ */
+#define read_fw_blob(var) \
+	read_fw		var ## _ADDR;			\
+	mov		%eax, %edi;			\
+	read_fw		var ## _SIZE;			\
+	mov		%eax, %ecx;			\
+	mov		$var ## _DATA, %ax;		\
+	mov		$BIOS_CFG_IOPORT_CFG, %edx;	\
+	outw		%ax, (%dx);			\
+	mov		$BIOS_CFG_IOPORT_DATA, %dx;	\
+	cld;						\
+	DEBUG_HERE \
+	rep insb	(%dx), %es:(%edi);
+
 .code16
 .text
 	.global 	_start
 _start:
 	.short		0xaa55
-	.byte		1 /* (_end - _start) / 512 */
+	.byte		(_end - _start) / 512
 	push		%eax
 	push		%ds
 
@@ -57,10 +84,6 @@ _start:
 	xor		%ax, %ax
 	mov		%ax, %ds
 
-	/* save old int 19 */
-	mov		(0x19*4), %eax
-	mov		%eax, %cs:old_int19
-
 	/* install our int 19 handler */
 	movw		$int19_handler, (0x19*4)
 	mov		%cs, (0x19*4+2)
@@ -84,15 +107,34 @@ run_multiboot:
 	mov		%cs, %eax
 	shl		$0x4, %eax
 
-	/* fix the gdt descriptor to be PC relative */
-	mov		(gdt_desc+2), %ebx
-	add		%eax, %ebx
-	mov		%ebx, (gdt_desc+2)
+	/* set up a long jump descriptor that is PC relative */
 
-	/* fix the prot mode indirect jump to be PC relative */
+	/* move stack memory to %gs */
+	mov		%ss, %ecx
+	shl		$0x4, %ecx
+	mov		%esp, %ebx
+	add		%ebx, %ecx
+	sub		$0x20, %ecx
+	sub		$0x30, %esp
+	shr		$0x4, %ecx
+	mov		%cx, %gs
+
+	/* now push the indirect jump decriptor there */
 	mov		(prot_jump), %ebx
 	add		%eax, %ebx
-	mov		%ebx, (prot_jump)
+	movl		%ebx, %gs:GS_PROT_JUMP
+	mov		$8, %bx
+	movw		%bx, %gs:GS_PROT_JUMP + 4
+
+	/* fix the gdt descriptor to be PC relative */
+	movw		(gdt_desc), %bx
+	movw		%bx, %gs:GS_GDT_DESC
+	movl		(gdt_desc+2), %ebx
+	add		%eax, %ebx
+	movl		%ebx, %gs:GS_GDT_DESC + 2
+
+	/* Read the bootinfo struct into RAM */
+	read_fw_blob(FW_CFG_INITRD)
 
 	/* FS = bootinfo_struct */
 	read_fw		FW_CFG_INITRD_ADDR
@@ -100,7 +142,7 @@ run_multiboot:
 	mov		%ax, %fs
 
 	/* ES = mmap_addr */
-	read_fw		FW_CFG_INITRD_SIZE
+	mov		%eax, %fs:0x48
 	shr		$4, %eax
 	mov		%ax, %es
 
@@ -144,7 +186,7 @@ mmap_done:
 real_to_prot:
 	/* Load the GDT before going into protected mode */
 lgdt:
-	data32 lgdt	%cs:gdt_desc
+	data32 lgdt	%gs:GS_GDT_DESC
 
 	/* get us to protected mode now */
 	movl		$1, %eax
@@ -152,7 +194,7 @@ lgdt:
 
 	/* the LJMP sets CS for us and gets us to 32-bit */
 ljmp:
-	data32 ljmp	*%cs:prot_jump
+	data32 ljmp	*%gs:GS_PROT_JUMP
 
 prot_mode:
 .code32
@@ -165,8 +207,11 @@ prot_mode:
 	movl		%eax, %fs
 	movl		%eax, %gs
 
+	/* Read the kernel and modules into RAM */
+	read_fw_blob(FW_CFG_KERNEL)
+
 	/* Jump off to the kernel */
-	read_fw		FW_CFG_KERNEL_ADDR
+	read_fw		FW_CFG_KERNEL_ENTRY
 	mov		%eax, %ecx
 
 	/* EBX contains a pointer to the bootinfo struct */
@@ -180,8 +225,6 @@ ljmp2:
 
 /* Variables */
 .align 4, 0
-old_int19:	.long 0
-
 prot_jump:	.long prot_mode
 		.short 8
 
