Skip to content

Commit 6a47ae2

Browse files
bonzinimdroth
authored andcommitted
linuxboot: compute initrd loading address
Even though hw/i386/pc.c tries to compute a valid loading address for the initrd, close to the top of RAM, this does not take into account other data that is malloced into that memory by SeaBIOS. Luckily we can easily look at the memory map to find out how much memory is used up there. This patch places the initrd in the first four gigabytes, below the first hole (as returned by INT 15h, AX=e801h). Without this patch: [ 0.000000] init_memory_mapping: [mem 0x07000000-0x07fdffff] [ 0.000000] RAMDISK: [mem 0x0710a000-0x07fd7fff] With this patch: [ 0.000000] init_memory_mapping: [mem 0x07000000-0x07fdffff] [ 0.000000] RAMDISK: [mem 0x07112000-0x07fdffff] So linuxboot is able to use the 64k that were added as padding for QEMU <= 2.1. Acked-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]> (cherry picked from commit cdebec5) Signed-off-by: Michael Roth <[email protected]>
1 parent 5f0681e commit 6a47ae2

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

pc-bios/linuxboot.bin

0 Bytes
Binary file not shown.

pc-bios/optionrom/linuxboot.S

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,45 @@ boot_kernel:
7676

7777

7878
copy_kernel:
79+
/* Compute initrd address */
80+
mov $0xe801, %ax
81+
xor %cx, %cx
82+
xor %dx, %dx
83+
int $0x15
84+
85+
/* Output could be in AX/BX or CX/DX */
86+
or %cx, %cx
87+
jnz 1f
88+
or %dx, %dx
89+
jnz 1f
90+
mov %ax, %cx
91+
mov %bx, %dx
92+
1:
93+
94+
or %dx, %dx
95+
jnz 2f
96+
addw $1024, %cx /* add 1 MB */
97+
movzwl %cx, %edi
98+
shll $10, %edi /* convert to bytes */
99+
jmp 3f
100+
101+
2:
102+
addw $16777216 >> 16, %dx /* add 16 MB */
103+
movzwl %dx, %edi
104+
shll $16, %edi /* convert to bytes */
105+
106+
3:
107+
read_fw FW_CFG_INITRD_SIZE
108+
subl %eax, %edi
109+
andl $-4096, %edi /* EDI = start of initrd */
79110

80111
/* We need to load the kernel into memory we can't access in 16 bit
81112
mode, so let's get into 32 bit mode, write the kernel and jump
82113
back again. */
83114

84115
/* Reserve space on the stack for our GDT descriptor. */
85-
mov %esp, %ebp
86-
sub $16, %esp
116+
mov %esp, %ebp
117+
sub $16, %esp
87118

88119
/* Now create the GDT descriptor */
89120
movw $((3 * 8) - 1), -16(%bp)
@@ -108,10 +139,18 @@ copy_kernel:
108139
/* We're now running in 16-bit CS, but 32-bit ES! */
109140

110141
/* Load kernel and initrd */
142+
pushl %edi
143+
read_fw_blob_addr32_edi(FW_CFG_INITRD)
111144
read_fw_blob_addr32(FW_CFG_KERNEL)
112-
read_fw_blob_addr32(FW_CFG_INITRD)
113145
read_fw_blob_addr32(FW_CFG_CMDLINE)
114-
read_fw_blob_addr32(FW_CFG_SETUP)
146+
147+
read_fw FW_CFG_SETUP_ADDR
148+
mov %eax, %edi
149+
mov %eax, %ebx
150+
read_fw_blob_addr32_edi(FW_CFG_SETUP)
151+
152+
/* Update the header with the initrd address we chose above */
153+
popl %es:0x218(%ebx)
115154

116155
/* And now jump into Linux! */
117156
mov $0, %eax

pc-bios/optionrom/optionrom.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@
5151
.endm
5252

5353
#define read_fw_blob_pre(var) \
54-
read_fw var ## _ADDR; \
55-
mov %eax, %edi; \
5654
read_fw var ## _SIZE; \
5755
mov %eax, %ecx; \
5856
mov $var ## _DATA, %ax; \
@@ -68,6 +66,8 @@
6866
* Clobbers: %eax, %edx, %es, %ecx, %edi
6967
*/
7068
#define read_fw_blob(var) \
69+
read_fw var ## _ADDR; \
70+
mov %eax, %edi; \
7171
read_fw_blob_pre(var); \
7272
/* old as(1) doesn't like this insn so emit the bytes instead: \
7373
rep insb (%dx), %es:(%edi); \
@@ -80,7 +80,22 @@
8080
*
8181
* Clobbers: %eax, %edx, %es, %ecx, %edi
8282
*/
83-
#define read_fw_blob_addr32(var) \
83+
#define read_fw_blob_addr32(var) \
84+
read_fw var ## _ADDR; \
85+
mov %eax, %edi; \
86+
read_fw_blob_pre(var); \
87+
/* old as(1) doesn't like this insn so emit the bytes instead: \
88+
addr32 rep insb (%dx), %es:(%edi); \
89+
*/ \
90+
.dc.b 0x67,0xf3,0x6c
91+
92+
/*
93+
* Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi.
94+
* Requires _SIZE and _DATA values for the parameter.
95+
*
96+
* Clobbers: %eax, %edx, %edi, %es, %ecx
97+
*/
98+
#define read_fw_blob_addr32_edi(var) \
8499
read_fw_blob_pre(var); \
85100
/* old as(1) doesn't like this insn so emit the bytes instead: \
86101
addr32 rep insb (%dx), %es:(%edi); \

0 commit comments

Comments
 (0)