1 /* ----------------------------------------------------------------------- *
3 * Copyright 2008 H. Peter Anvin - All Rights Reserved
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * ----------------------------------------------------------------------- */
31 * This is the initial program run before the Linux kernel.
32 * It serves to hoist the initrd as high as permitted.
34 * This runs in protected mode, but in low memory, and should be kept
35 * as small as possible.
42 static uint32_t initrd_len, initrd_addr;
43 static uint64_t max_addr;
45 static int initrd_fit(uint32_t base, uint32_t end)
49 printf("Fit: base = %08x, end = %08x\n", base, end);
52 return -1; /* Invalid */
55 return -1; /* Not accessible */
63 ibase = (end - initrd_len) & ~0xfff;
67 if (initrd_addr < ibase) {
68 initrd_addr = ibase; /* This is a better one... */
69 printf("Best: initrd_addr = %08x\n", initrd_addr);
75 static int probe_memory_e820(void)
87 memset(®s, 0, sizeof regs);
90 regs.eax.l = 0x0000e820;
91 regs.ecx.l = sizeof buf;
92 regs.edx.l = 0x534d4150;
93 regs.edi.w[0] = OFFS(&buf);
96 intcall(0x15, ®s, ®s);
97 copied = (regs.eflags.l & EFLAGS_CF)
100 if ( regs.eax.l != 0x534d4150 || copied < 20 )
104 continue; /* Not memory */
106 rv &= initrd_fit(buf.base, buf.base+buf.len);
107 } while (regs.ebx.l);
112 static int probe_memory_e801(void)
117 memset(®s, 0, sizeof regs);
118 regs.eax.w[0] = 0xe801;
119 intcall(0x15, ®s, ®s);
121 if (regs.eflags.l & EFLAGS_CF)
122 return -1; /* No e801 */
124 if (regs.eax.w[0] < 15*1024)
125 end = (uint64_t)(regs.eax.w[0] << 10) + 0x100000;
127 end = (uint64_t)(regs.ebx.w[0] << 16) + 0x1000000;
129 return initrd_fit(0x100000, end);
132 static int probe_memory_88(void)
136 memset(®s, 0, sizeof regs);
137 regs.eax.b[1] = 0x88;
138 intcall(0x15, ®s, ®s);
140 if (regs.eflags.l & EFLAGS_CF)
143 return initrd_fit(0x100000, (regs.eax.w[0] << 10)+0x100000);
146 static int place_initrd(void)
150 rv = probe_memory_e820();
154 rv = probe_memory_e801();
158 return probe_memory_88();
163 extern struct startup_info _start[]; /* Cute hack, eh? */
164 struct startup_info *info = _start - 1;
165 struct setup_header *hdr = (void *)(info->setup_addr + 0x1f1);
168 initrd_len = info->rd_len;
169 max_addr = info->rd_maxaddr;
174 /* Move the initrd into place */
175 printf("Moving initrd: 0x%08x -> 0x%08x (0x%08x bytes)\n",
176 info->rd_addr, initrd_addr, info->rd_len);
178 memmove((void *)initrd_addr, (void *)info->rd_addr,
181 hdr->ramdisk_image = initrd_addr;
184 jump_to_kernel(info->setup_addr >> 4,
185 info->cmdline_addr - info->setup_addr);
186 return -1; /* Shouldn't return... */