summaryrefslogtreecommitdiffstats
path: root/src/arch
diff options
context:
space:
mode:
authorMichael Brown2006-03-16 19:15:48 +0100
committerMichael Brown2006-03-16 19:15:48 +0100
commit9b180172961c0a7bf223579df455e31004d38669 (patch)
tree9a328e5b098919753ac0d7eaf2c54c39c890f4b8 /src/arch
parentMerge from Etherboot 5.4 (diff)
parentImport from Etherboot 5.4 (diff)
downloadipxe-9b180172961c0a7bf223579df455e31004d38669.tar.gz
ipxe-9b180172961c0a7bf223579df455e31004d38669.tar.xz
ipxe-9b180172961c0a7bf223579df455e31004d38669.zip
Merge from Etherboot 5.4
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/i386/core/elf.c2
-rw-r--r--src/arch/i386/core/multiboot_loader.c99
2 files changed, 83 insertions, 18 deletions
diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c
index 40a18a1f..fbb4032f 100644
--- a/src/arch/i386/core/elf.c
+++ b/src/arch/i386/core/elf.c
@@ -74,7 +74,7 @@ struct Elf_Bhdr *prepare_boot_params(void *header)
notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data);
notes.nf1.n_type = EB_BOOTP_DATA;
CP(notes.nf1_name, EB_PARAM_NOTE);
- notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR);
+ notes.nf1_bootp_data = virt_to_phys(&bootp_data);
notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE);
notes.nf2.n_descsz = sizeof(notes.nf2_header);
diff --git a/src/arch/i386/core/multiboot_loader.c b/src/arch/i386/core/multiboot_loader.c
index e9785f1c..56225242 100644
--- a/src/arch/i386/core/multiboot_loader.c
+++ b/src/arch/i386/core/multiboot_loader.c
@@ -56,28 +56,82 @@ struct multiboot_header {
};
static struct multiboot_header *mbheader;
+static unsigned int mbimgoffset, mboffset;
+static unsigned char mbbuffer[12];
static struct multiboot_info mbinfo;
-static void multiboot_probe(unsigned char *data, int len)
+static void multiboot_init(void)
+{
+ mbheader = NULL;
+ mbimgoffset = 0;
+ mboffset = 0;
+}
+
+/* Remember this probing function is actually different from the usual probing
+ * functions, since the Multiboot header is somewhere in the first 8KB of the
+ * image and it is byte aligned, but there is not much more known about how to
+ * find it. In the Etherboot context the most complicated issue is that the
+ * image has to be processed block-by-block, with unknown block size and no
+ * guarantees about block alignment with respect to the image. */
+static void multiboot_peek(unsigned char *data, int len)
{
- int offset;
struct multiboot_header *h;
- /* Multiboot spec requires the header to be in first 8KB of the image */
- if (len > 8192)
- len = 8192;
+ /* If we have already searched the first 8KB of the image or if we have
+ * already found a valid Multiboot header, skip this code. */
+ if ((mboffset == 12) || (mbimgoffset >= 8192))
+ return;
+
+ if (mbimgoffset + len >= 8192)
+ len = 8192 - mbimgoffset;
- for (offset = 0; offset < len; offset += 4) {
- h = (struct multiboot_header *) (data + offset);
- if (h->magic == MULTIBOOT_HEADER_MAGIC
- && h->magic + h->flags + h->checksum == 0) {
- printf("/Multiboot");
- mbheader = h;
- return;
- }
- }
- mbheader = 0;
+ /* This piece of code is pretty stupid, since it always copies data, even
+ * if it is word aligned. This shouldn't matter too much on platforms that
+ * use the Multiboot spec, since the processors are usually reasonably fast
+ * and this code is only executed for the first 8KB of the image. Feel
+ * free to improve it, but be prepared to write quite a lot of code that
+ * deals with non-aligned data with respect to the image to load. */
+ while (len > 0) {
+ mbimgoffset++;
+ memcpy(mbbuffer + mboffset, data, 1);
+ mboffset++;
+ data++;
+ len--;
+ if (mboffset == 4) {
+ /* Accumulated a word into the buffer. */
+ h = (struct multiboot_header *)mbbuffer;
+ if (h->magic != MULTIBOOT_HEADER_MAGIC) {
+ /* Wrong magic, this cannot be the start of the header. */
+ mboffset = 0;
+ }
+ } else if (mboffset == 12) {
+ /* Accumulated the minimum header data into the buffer. */
+ h = (struct multiboot_header *)mbbuffer;
+ if (h->magic + h->flags + h->checksum != 0) {
+ /* Checksum error, not a valid header. Check for a possible
+ * header starting in the current flag/checksum field. */
+ if (h->flags == MULTIBOOT_HEADER_MAGIC) {
+ mboffset -= 4;
+ memmove(mbbuffer, mbbuffer + 4, mboffset);
+ } else if (h->checksum == MULTIBOOT_HEADER_MAGIC) {
+ mboffset -= 8;
+ memmove(mbbuffer, mbbuffer + 8, mboffset);
+ } else {
+ mboffset = 0;
+ }
+ } else {
+ printf("Multiboot... ");
+ mbheader = h;
+ if ((h->flags & 0xfffc) != 0) {
+ printf("\nERROR: Unsupported Multiboot requirements flags\n");
+ longjmp(restart_etherboot, -2);
+ }
+ break;
+ }
+ }
+ }
+ mbimgoffset += len;
}
static inline void multiboot_boot(unsigned long entry)
@@ -94,7 +148,7 @@ static inline void multiboot_boot(unsigned long entry)
* strings of the maximum size are possible. Note this buffer
* can overrun if a stupid file name is chosen. Oh well. */
c = cmdline;
- for (i = 0; KERNEL_BUF[i] != 0; i++) {
+ for (i = 0; KERNEL_BUF[i] != '\0'; i++) {
switch (KERNEL_BUF[i]) {
case ' ':
case '\\':
@@ -106,6 +160,11 @@ static inline void multiboot_boot(unsigned long entry)
}
*c++ = KERNEL_BUF[i];
}
+ if (addparam != NULL) {
+ *c++ = ' ';
+ memcpy(c, addparam, addparamlen);
+ c += addparamlen;
+ }
(void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32));
mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID;
@@ -139,5 +198,11 @@ static inline void multiboot_boot(unsigned long entry)
os_regs.eax = 0x2BADB002;
os_regs.ebx = virt_to_phys(&mbinfo);
xstart32(entry);
- longjmp(restart_etherboot, -2);
+ /* A Multiboot kernel by default never returns - there is nothing in the
+ * specification about what happens to the boot loader after the kernel has
+ * been started. Thus if the kernel returns it is definitely aware of the
+ * semantics involved (i.e. the "-retaddr" parameter). Do not treat this
+ * as an error, but restart with a fresh DHCP request in order to activate
+ * the menu again in case one is used. */
+ longjmp(restart_etherboot, 2);
}