diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index a73be367e6..8af0cfd2da 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -25,8 +25,9 @@ CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") -STRING_OBJS = strcat.o strchr.o strcmp.o strcpy.o strlen.o strncmp.o strncpy.o \ - strstr.o memset.o memcpy.o memmove.o memcmp.o +STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ + strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ + memset.o memcpy.o memmove.o memcmp.o %.o : $(SLOF_DIR)/lib/libc/string/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") @@ -50,7 +51,7 @@ libc.a: $(LIBCOBJS) # libnet files: LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ - dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o + dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o LIBNETCFLAGS := $(QEMU_CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) %.o : $(SLOF_DIR)/lib/libnet/%.c diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index d007fb7a86..c059546480 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "s390-ccw.h" #include "virtio.h" @@ -41,12 +42,14 @@ extern char _start[]; #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) +#define ARCH_COMMAND_LINE_SIZE 896 /* Taken from Linux kernel */ char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; static SubChannelId net_schid = { .one = 1 }; +static uint8_t mac[6]; static uint64_t dest_timer; static uint64_t get_timer_ms(void) @@ -159,7 +162,6 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) static int net_init(filename_ip_t *fn_ip) { - uint8_t mac[6]; int rc; memset(fn_ip, 0, sizeof(filename_ip_t)); @@ -233,6 +235,66 @@ static void net_release(filename_ip_t *fn_ip) } } +/** + * Load a kernel with initrd (i.e. with the information that we've got from + * a pxelinux.cfg config file) + */ +static int load_kernel_with_initrd(filename_ip_t *fn_ip, + struct pl_cfg_entry *entry) +{ + int rc; + + printf("Loading pxelinux.cfg entry '%s'\n", entry->label); + + if (!entry->kernel) { + printf("Kernel entry is missing!\n"); + return -1; + } + + strncpy(fn_ip->filename, entry->kernel, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, KERNEL_ADDR, KERNEL_MAX_SIZE); + if (rc < 0) { + return rc; + } + + if (entry->initrd) { + uint64_t iaddr = (rc + 0xfff) & ~0xfffUL; + + strncpy(fn_ip->filename, entry->initrd, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, (void *)iaddr, KERNEL_MAX_SIZE - iaddr); + if (rc < 0) { + return rc; + } + /* Patch location and size: */ + *(uint64_t *)0x10408 = iaddr; + *(uint64_t *)0x10410 = rc; + rc += iaddr; + } + + if (entry->append) { + strncpy((char *)0x10480, entry->append, ARCH_COMMAND_LINE_SIZE); + } + + return rc; +} + +#define MAX_PXELINUX_ENTRIES 16 + +static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) +{ + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_load_parse_cfg(fn_ip, mac, NULL, DEFAULT_TFTP_RETRIES, + cfgbuf, sizeof(cfgbuf), + entries, MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent > 0) { + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } + + return -1; +} + /** * Load via information from a .INS file (which can be found on CD-ROMs * for example) @@ -302,6 +364,25 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) if (!strncmp("* ", cfgbuf, 2)) { return handle_ins_cfg(fn_ip, cfgbuf, rc); } + /* + * pxelinux.cfg support via bootfile name is just here for developers' + * convenience (it eases testing with the built-in DHCP server of QEMU + * that does not support RFC 5071). The official way to configure a + * pxelinux.cfg file name is to use DHCP options 209 and 210 instead. + * So only use the pxelinux.cfg parser here for files that start with + * a magic comment string. + */ + if (!strncasecmp("# pxelinux", cfgbuf, 10)) { + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, + MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent <= 0) { + return -1; + } + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } } /* Move kernel to right location */ @@ -407,6 +488,9 @@ void main(void) if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { rc = net_try_direct_tftp_load(&fn_ip); } + if (rc <= 0) { + rc = net_try_pxelinux_cfg(&fn_ip); + } net_release(&fn_ip);