Salman Qazi
2008-Jun-16 18:16 UTC
[syslinux] Com32/PXELINUX issues when booting on certain hardware
Hi, It is possible for some BIOSes to return non-page-aligned values for the start and length of regions in the BIOS memory map (through int 15h, e820h). As a result, the initrd is loaded by linux.c32 (a provided com32 module) in a manner such that the last page contains both a portion of initrd and some BIOS data. The linux kernel treats this page as part of the ramdisk file system and ends up corrupting the BIOS data. Here is a patch (against syslinux-3.63) to fix this: --- /a/syslinux/com32/lib/syslinux/memmap.c#2 2008-06-12 18:08:46.000000000 -0700 +++ /b/syslinux/com32/lib/syslinux/memmap.c#3 2008-06-16 11:09:01.000000000 -0700 @@ -42,6 +42,9 @@ #include <com32.h> #include <syslinux/movebits.h> +#define PAGE_SIZE 4096 +#define PAGE_MASK (PAGE_SIZE - 1) + struct e820_entry { uint64_t start; uint64_t len; @@ -87,8 +90,22 @@ break; type = e820buf->type == 1 ? SMT_FREE : SMT_RESERVED; - start = e820buf->start; - len = e820buf->len; + + /* + * If the start and length of a usable region in the BIOS + * memory map are not page-aligned, we may end up passing + * an initrd to the kernel that shares a page with a reserved + * region of memory. This results in the kernel accidentally + * overwriting data in such reserved regions. + */ + /* Align the start to the next page boundary */ + start = ((e820buf->start + PAGE_SIZE - 1) & ~PAGE_MASK); + + /* Adjust length so that the end doesn't drift forward */ + len = e820buf->len - (start - e820buf->start); + + /* Page align the length */ + len = (len & ~PAGE_MASK); if (start < 0x100000000ULL) { /* Don't rely on E820 being valid for low memory. Doing so Kindest Regards, Salman Qazi