Skip to content

Commit b466e17

Browse files
bonzinimdroth
authored andcommitted
linuxboot: fix loading old kernels
Old kernels that used high memory only allowed the initrd to be in the first 896MB of memory. If you load the initrd above, they complain that "initrd extends beyond end of memory". In order to fix this, while not breaking machines with small amounts of memory fixed by cdebec5 (linuxboot: compute initrd loading address, 2014-10-06), we need to distinguish two cases. If pc.c placed the initrd at end of memory, use the new algorithm based on the e801 memory map. If instead pc.c placed the initrd at the maximum address specified by the bzImage, leave it there. The only interesting part is that the low-memory info block is now loaded very early, in real mode, and thus the 32-bit address has to be converted into a real mode segment. The initrd address is also patched in the info block before entering real mode, it is simpler that way. This fixes booting the RHEL4.8 32-bit installation image with 1GB of RAM. Cc: [email protected] Cc: [email protected] Cc: [email protected] Signed-off-by: Paolo Bonzini <[email protected]> (cherry picked from commit 269e235) Signed-off-by: Michael Roth <[email protected]>
1 parent 6a47ae2 commit b466e17

File tree

2 files changed

+27
-10
lines changed

2 files changed

+27
-10
lines changed

pc-bios/linuxboot.bin

0 Bytes
Binary file not shown.

pc-bios/optionrom/linuxboot.S

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,31 @@ boot_kernel:
7676

7777

7878
copy_kernel:
79-
/* Compute initrd address */
79+
/* Read info block in low memory (0x10000 or 0x90000) */
80+
read_fw FW_CFG_SETUP_ADDR
81+
shr $4, %eax
82+
mov %eax, %es
83+
xor %edi, %edi
84+
read_fw_blob_addr32_edi(FW_CFG_SETUP)
85+
86+
cmpw $0x203, %es:0x206 // if protocol >= 0x203
87+
jae 1f // have initrd_max
88+
movl $0x37ffffff, %es:0x22c // else assume 0x37ffffff
89+
1:
90+
91+
/* Check if using kernel-specified initrd address */
92+
read_fw FW_CFG_INITRD_ADDR
93+
mov %eax, %edi // (load_kernel wants it in %edi)
94+
read_fw FW_CFG_INITRD_SIZE // find end of initrd
95+
add %edi, %eax
96+
xor %es:0x22c, %eax // if it matches es:0x22c
97+
and $-4096, %eax // (apart from padding for page)
98+
jz load_kernel // then initrd is not at top
99+
// of memory
100+
101+
/* pc.c placed the initrd at end of memory. Compute a better
102+
* initrd address based on e801 data.
103+
*/
80104
mov $0xe801, %ax
81105
xor %cx, %cx
82106
xor %dx, %dx
@@ -107,7 +131,9 @@ copy_kernel:
107131
read_fw FW_CFG_INITRD_SIZE
108132
subl %eax, %edi
109133
andl $-4096, %edi /* EDI = start of initrd */
134+
movl %edi, %es:0x218 /* put it in the header */
110135

136+
load_kernel:
111137
/* We need to load the kernel into memory we can't access in 16 bit
112138
mode, so let's get into 32 bit mode, write the kernel and jump
113139
back again. */
@@ -139,19 +165,10 @@ copy_kernel:
139165
/* We're now running in 16-bit CS, but 32-bit ES! */
140166

141167
/* Load kernel and initrd */
142-
pushl %edi
143168
read_fw_blob_addr32_edi(FW_CFG_INITRD)
144169
read_fw_blob_addr32(FW_CFG_KERNEL)
145170
read_fw_blob_addr32(FW_CFG_CMDLINE)
146171

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)
154-
155172
/* And now jump into Linux! */
156173
mov $0, %eax
157174
mov %eax, %cr0

0 commit comments

Comments
 (0)