diff options
Diffstat (limited to 'com32/lib')
-rw-r--r-- | com32/lib/Makefile | 4 | ||||
-rw-r--r-- | com32/lib/com32.ld | 33 | ||||
-rw-r--r-- | com32/lib/syslinux/load_linux.c | 49 | ||||
-rw-r--r-- | com32/lib/syslinux/setup_data.c | 47 |
4 files changed, 108 insertions, 25 deletions
diff --git a/com32/lib/Makefile b/com32/lib/Makefile index eace321b..5ab1fac4 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -129,7 +129,9 @@ LIBOBJS = \ syslinux/video/fontquery.o syslinux/video/forcetext.o \ syslinux/video/reportmode.o \ \ - syslinux/disk.o + syslinux/disk.o \ + \ + syslinux/setup_data.o # These are the objects which are also imported into the core LIBCOREOBJS = \ diff --git a/com32/lib/com32.ld b/com32/lib/com32.ld index 37ee46cf..008e4ceb 100644 --- a/com32/lib/com32.ld +++ b/com32/lib/com32.ld @@ -36,36 +36,23 @@ SECTIONS .rodata1 : { *(.rodata1) } __rodata_end = .; - /* Ensure the __preinit_array_start label is properly aligned. We - could instead move the label definition inside the section, but - the linker would then create the section even if it turns out to - be empty, which isn't pretty. */ + /* + * The difference betwee .ctors/.dtors and .init_array/.fini_array + * is the ordering, but we don't use prioritization for libcom32, so + * just lump them all together and hope that's okay. + */ . = ALIGN(4); - .preinit_array : { - PROVIDE (__preinit_array_start = .); - *(.preinit_array) - PROVIDE (__preinit_array_end = .); - } - .init_array : { - PROVIDE (__init_array_start = .); - *(.init_array) - PROVIDE (__init_array_end = .); - } - .fini_array : { - PROVIDE (__fini_array_start = .); - *(.fini_array) - PROVIDE (__fini_array_end = .); - } .ctors : { PROVIDE (__ctors_start = .); - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) + KEEP (*(SORT(.preinit_array*))) + KEEP (*(SORT(.init_array*))) + KEEP (*(SORT(.ctors*))) PROVIDE (__ctors_end = .); } .dtors : { PROVIDE (__dtors_start = .); - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + KEEP (*(SORT(.fini_array*))) + KEEP (*(SORT(.dtors*))) PROVIDE (__dtors_end = .); } diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c index 45cd6965..856141f8 100644 --- a/com32/lib/syslinux/load_linux.c +++ b/com32/lib/syslinux/load_linux.c @@ -38,6 +38,7 @@ #include <inttypes.h> #include <string.h> #include <minmax.h> +#include <errno.h> #include <suffix_number.h> #include <syslinux/align.h> #include <syslinux/linux.h> @@ -180,13 +181,16 @@ static int map_initramfs(struct syslinux_movelist **fraglist, } int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, - struct initramfs *initramfs, char *cmdline) + struct initramfs *initramfs, + struct setup_data *setup_data, + char *cmdline) { struct linux_header hdr, *whdr; size_t real_mode_size, prot_mode_size; addr_t real_mode_base, prot_mode_base; addr_t irf_size; size_t cmdline_size, cmdline_offset; + struct setup_data *sdp; struct syslinux_rm_regs regs; struct syslinux_movelist *fraglist = NULL; struct syslinux_memmap *mmap = NULL; @@ -449,6 +453,49 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, } } + if (setup_data) { + uint64_t *prev_ptr = &whdr->setup_data; + + for (sdp = setup_data->next; sdp != setup_data; sdp = sdp->next) { + struct syslinux_memmap *ml; + const addr_t align_mask = 15; /* Header is 16 bytes */ + addr_t best_addr = 0; + size_t size = sdp->hdr.len + sizeof(sdp->hdr); + + if (!sdp->data || !sdp->hdr.len) + continue; + + if (hdr.version < 0x0209) { + /* Setup data not supported */ + errno = ENXIO; /* Kind of arbitrary... */ + goto bail; + } + + for (ml = amap; ml->type != SMT_END; ml = ml->next) { + addr_t adj_start = (ml->start + align_mask) & ~align_mask; + addr_t adj_end = ml->next->start & ~align_mask; + + if (ml->type == SMT_FREE && adj_end - adj_start >= size) + best_addr = (adj_end - size) & ~align_mask; + } + + if (!best_addr) + goto bail; + + *prev_ptr = best_addr; + prev_ptr = &sdp->hdr.next; + + if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC)) + goto bail; + if (syslinux_add_movelist(&fraglist, best_addr, + (addr_t)&sdp->hdr, sizeof sdp->hdr)) + goto bail; + if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr, + (addr_t)sdp->data, sdp->hdr.len)) + goto bail; + } + } + /* Set up the registers on entry */ memset(®s, 0, sizeof regs); regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4; diff --git a/com32/lib/syslinux/setup_data.c b/com32/lib/syslinux/setup_data.c new file mode 100644 index 00000000..a36c5b61 --- /dev/null +++ b/com32/lib/syslinux/setup_data.c @@ -0,0 +1,47 @@ +#include <stdlib.h> +#include <syslinux/linux.h> +#include <syslinux/loadfile.h> + +struct setup_data *setup_data_init(void) +{ + struct setup_data *setup_data; + + setup_data = zalloc(sizeof(*setup_data)); + if (!setup_data) + return NULL; + + setup_data->prev = setup_data->next = setup_data; + return setup_data; +} + +int setup_data_add(struct setup_data *head, uint32_t type, + const void *data, size_t data_len) +{ + struct setup_data *setup_data; + + setup_data = zalloc(sizeof(*setup_data)); + if (!setup_data) + return -1; + + setup_data->data = data; + setup_data->hdr.len = data_len; + setup_data->hdr.type = type; + setup_data->prev = head->prev; + setup_data->next = head; + head->prev->next = setup_data; + head->prev = setup_data; + + return 0; +} + +int setup_data_load(struct setup_data *head, uint32_t type, + const char *filename) +{ + void *data; + size_t len; + + if (loadfile(filename, &data, &len)) + return -1; + + return setup_data_add(head, type, data, len); +} |