diff -Nru qemu-2.11+dfsg/debian/changelog qemu-2.11+dfsg/debian/changelog --- qemu-2.11+dfsg/debian/changelog 2018-08-29 09:46:37.000000000 +0000 +++ qemu-2.11+dfsg/debian/changelog 2018-09-25 11:31:15.000000000 +0000 @@ -1,3 +1,15 @@ +qemu (1:2.11+dfsg-1ubuntu7.7) bionic; urgency=medium + + * Update pxe netboot images for KVM s390x to qemu 3.0 level (LP: #1790901) + The SLOF source pieces in src:qemu are only used for s390x netboot, + which are independent ROMs (no linking). All other binaries out of this + are part of src:slof and independent. + - d/p/ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch + - d/p/ubuntu/lp-1790901-0*: backport s390x pxelinux netboot capabilities + and related fixes + + -- Christian Ehrhardt Tue, 25 Sep 2018 13:31:15 +0200 + qemu (1:2.11+dfsg-1ubuntu7.6) bionic; urgency=medium [ Christian Ehrhardt ] diff -Nru qemu-2.11+dfsg/debian/patches/series qemu-2.11+dfsg/debian/patches/series --- qemu-2.11+dfsg/debian/patches/series 2018-08-29 09:46:37.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/series 2018-09-25 11:31:15.000000000 +0000 @@ -45,3 +45,15 @@ ubuntu/lp-1790457-partial-s390x-linux-headers-update.patch ubuntu/lp-1790457-s390x-kvm-add-etoken-facility.patch ubuntu/lp-1787267-fix-en_us-vnc-pipe.patch +ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch +ubuntu/lp-1790901-0001-ccw-update-libc.patch +ubuntu/lp-1790901-0002-s390-ccw-move-auxiliary-IPL-data-to-separate-locatio.patch +ubuntu/lp-1790901-0101-s390-Do-not-pass-inofficial-IPL-type-to-the-guest.patch +ubuntu/lp-1790901-0102-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch +ubuntu/lp-1790901-0201-pc-bios-s390-ccw-net-Split-up-net_load-into-init-loa.patch +ubuntu/lp-1790901-0202-pc-bios-s390-ccw-net-Use-diag308-to-reset-machine-be.patch +ubuntu/lp-1790901-0203-pc-bios-s390-ccw-net-Add-support-for-.INS-config-fil.patch +ubuntu/lp-1790901-0204-pc-bios-s390-ccw-net-Update-code-for-the-latest-chan.patch +ubuntu/lp-1790901-0205-pc-bios-s390-ccw-net-Add-support-for-pxelinux-style-.patch +ubuntu/lp-1790901-0206-pc-bios-s390-ccw-net-Try-to-load-pxelinux.cfg-file-a.patch +ubuntu/lp-1790901-0207-pc-bios-s390-ccw-Optimize-the-s390-netboot.img-for-s.patch diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0001-ccw-update-libc.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0001-ccw-update-libc.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0001-ccw-update-libc.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0001-ccw-update-libc.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,321 @@ +From fc0e208774364c2a8013aa028b742a8dde6d2c2b Mon Sep 17 00:00:00 2001 +From: "Collin L. Walling" +Date: Fri, 23 Feb 2018 10:43:10 -0500 +Subject: [PATCH] s390-ccw: update libc + +Moved: + memcmp from bootmap.h to libc.h (renamed from _memcmp) + strlen from sclp.c to libc.h (renamed from _strlen) + +Added C standard functions: + isdigit + +Added non C-standard function: + uitoa + atoui + +Signed-off-by: Collin L. Walling +Acked-by: Christian Borntraeger +Reviewed-by: Janosch Frank +Reviewed-by: Thomas Huth +Signed-off-by: Thomas Huth +--- + pc-bios/s390-ccw/Makefile | 2 +- + pc-bios/s390-ccw/bootmap.c | 4 +- + pc-bios/s390-ccw/bootmap.h | 16 +------ + pc-bios/s390-ccw/libc.c | 88 ++++++++++++++++++++++++++++++++++++++ + pc-bios/s390-ccw/libc.h | 37 +++++++++++++++- + pc-bios/s390-ccw/main.c | 17 +------- + pc-bios/s390-ccw/sclp.c | 10 +---- + 7 files changed, 129 insertions(+), 45 deletions(-) + create mode 100644 pc-bios/s390-ccw/libc.c + +diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile +index 6d0c2ee691..9f7904fc20 100644 +--- a/pc-bios/s390-ccw/Makefile ++++ b/pc-bios/s390-ccw/Makefile +@@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) + + .PHONY : all clean build-all + +-OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o ++OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o + QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) + QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float + QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing +diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c +index a94638db2c..092fb355fe 100644 +--- a/pc-bios/s390-ccw/bootmap.c ++++ b/pc-bios/s390-ccw/bootmap.c +@@ -506,7 +506,7 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s) + "Failed to read image sector 0"); + + /* Checking bytes 8 - 32 for S390 Linux magic */ +- return !_memcmp(magic_sec + 8, linux_s390_magic, 24); ++ return !memcmp(magic_sec + 8, linux_s390_magic, 24); + } + + /* Location of the current sector of the directory */ +@@ -635,7 +635,7 @@ static uint32_t find_iso_bc(void) + if (vd->type == VOL_DESC_TYPE_BOOT) { + IsoVdElTorito *et = &vd->vd.boot; + +- if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) { ++ if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) { + return bswap32(et->bc_offset); + } + } +diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h +index 4bd95cd4d2..4cf7e1e463 100644 +--- a/pc-bios/s390-ccw/bootmap.h ++++ b/pc-bios/s390-ccw/bootmap.h +@@ -328,20 +328,6 @@ static inline bool magic_match(const void *data, const void *magic) + return *((uint32_t *)data) == *((uint32_t *)magic); + } + +-static inline int _memcmp(const void *s1, const void *s2, size_t n) +-{ +- int i; +- const uint8_t *p1 = s1, *p2 = s2; +- +- for (i = 0; i < n; i++) { +- if (p1[i] != p2[i]) { +- return p1[i] > p2[i] ? 1 : -1; +- } +- } +- +- return 0; +-} +- + static inline uint32_t iso_733_to_u32(uint64_t x) + { + return (uint32_t)x; +@@ -434,7 +420,7 @@ const uint8_t vol_desc_magic[] = "CD001"; + + static inline bool is_iso_vd_valid(IsoVolDesc *vd) + { +- return !_memcmp(&vd->ident[0], vol_desc_magic, 5) && ++ return !memcmp(&vd->ident[0], vol_desc_magic, 5) && + vd->version == 0x1 && + vd->type <= VOL_DESC_TYPE_PARTITION; + } +diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c +new file mode 100644 +index 0000000000..38ea77d7aa +--- /dev/null ++++ b/pc-bios/s390-ccw/libc.c +@@ -0,0 +1,88 @@ ++/* ++ * libc-style definitions and functions ++ * ++ * Copyright 2018 IBM Corp. ++ * Author(s): Collin L. Walling ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include "libc.h" ++#include "s390-ccw.h" ++ ++/** ++ * atoui: ++ * @str: the string to be converted. ++ * ++ * Given a string @str, convert it to an integer. Leading spaces are ++ * ignored. Any other non-numerical value will terminate the conversion ++ * and return 0. This function only handles numbers between 0 and ++ * UINT64_MAX inclusive. ++ * ++ * Returns: an integer converted from the string @str, or the number 0 ++ * if an error occurred. ++ */ ++uint64_t atoui(const char *str) ++{ ++ int val = 0; ++ ++ if (!str || !str[0]) { ++ return 0; ++ } ++ ++ while (*str == ' ') { ++ str++; ++ } ++ ++ while (*str) { ++ if (!isdigit(*str)) { ++ break; ++ } ++ val = val * 10 + *str - '0'; ++ str++; ++ } ++ ++ return val; ++} ++ ++/** ++ * uitoa: ++ * @num: an integer (base 10) to be converted. ++ * @str: a pointer to a string to store the conversion. ++ * @len: the length of the passed string. ++ * ++ * Given an integer @num, convert it to a string. The string @str must be ++ * allocated beforehand. The resulting string will be null terminated and ++ * returned. This function only handles numbers between 0 and UINT64_MAX ++ * inclusive. ++ * ++ * Returns: the string @str of the converted integer @num ++ */ ++char *uitoa(uint64_t num, char *str, size_t len) ++{ ++ size_t num_idx = 1; /* account for NUL */ ++ uint64_t tmp = num; ++ ++ IPL_assert(str != NULL, "uitoa: no space allocated to store string"); ++ ++ /* Count indices of num */ ++ while ((tmp /= 10) != 0) { ++ num_idx++; ++ } ++ ++ /* Check if we have enough space for num and NUL */ ++ IPL_assert(len > num_idx, "uitoa: array too small for conversion"); ++ ++ str[num_idx--] = '\0'; ++ ++ /* Convert int to string */ ++ while (num_idx >= 0) { ++ str[num_idx--] = num % 10 + '0'; ++ num /= 10; ++ } ++ ++ return str; ++} +diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h +index 0142ea8e7b..63ece70c6b 100644 +--- a/pc-bios/s390-ccw/libc.h ++++ b/pc-bios/s390-ccw/libc.h +@@ -1,6 +1,8 @@ + /* + * libc-style definitions and functions + * ++ * Copyright (c) 2013 Alexander Graf ++ * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your +@@ -19,7 +21,7 @@ typedef unsigned long long uint64_t; + + static inline void *memset(void *s, int c, size_t n) + { +- int i; ++ size_t i; + unsigned char *p = s; + + for (i = 0; i < n; i++) { +@@ -33,7 +35,7 @@ static inline void *memcpy(void *s1, const void *s2, size_t n) + { + uint8_t *dest = s1; + const uint8_t *src = s2; +- int i; ++ size_t i; + + for (i = 0; i < n; i++) { + dest[i] = src[i]; +@@ -42,4 +44,35 @@ static inline void *memcpy(void *s1, const void *s2, size_t n) + return s1; + } + ++static inline int memcmp(const void *s1, const void *s2, size_t n) ++{ ++ size_t i; ++ const uint8_t *p1 = s1, *p2 = s2; ++ ++ for (i = 0; i < n; i++) { ++ if (p1[i] != p2[i]) { ++ return p1[i] > p2[i] ? 1 : -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static inline size_t strlen(const char *str) ++{ ++ size_t i; ++ for (i = 0; *str; i++) { ++ str++; ++ } ++ return i; ++} ++ ++static inline int isdigit(int c) ++{ ++ return (c >= '0') && (c <= '9'); ++} ++ ++uint64_t atoui(const char *str); ++char *uitoa(uint64_t num, char *str, size_t len); ++ + #endif +diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c +index 401e9dbb5f..e857ce4f60 100644 +--- a/pc-bios/s390-ccw/main.c ++++ b/pc-bios/s390-ccw/main.c +@@ -40,22 +40,7 @@ void panic(const char *string) + + unsigned int get_loadparm_index(void) + { +- const char *lp = loadparm; +- int i; +- unsigned int idx = 0; +- +- for (i = 0; i < 8; i++) { +- char c = lp[i]; +- +- if (c < '0' || c > '9') { +- break; +- } +- +- idx *= 10; +- idx += c - '0'; +- } +- +- return idx; ++ return atoui(loadparm); + } + + static bool find_dev(Schib *schib, int dev_no) +diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c +index 90d1bc3147..e6a089889a 100644 +--- a/pc-bios/s390-ccw/sclp.c ++++ b/pc-bios/s390-ccw/sclp.c +@@ -65,14 +65,6 @@ void sclp_setup(void) + sclp_set_write_mask(); + } + +-static int _strlen(const char *str) +-{ +- int i; +- for (i = 0; *str; i++) +- str++; +- return i; +-} +- + long write(int fd, const void *str, size_t len) + { + WriteEventData *sccb = (void *)_sccb; +@@ -113,7 +105,7 @@ long write(int fd, const void *str, size_t len) + + void sclp_print(const char *str) + { +- write(1, str, _strlen(str)); ++ write(1, str, strlen(str)); + } + + void sclp_get_loadparm_ascii(char *loadparm) +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0002-s390-ccw-move-auxiliary-IPL-data-to-separate-locatio.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0002-s390-ccw-move-auxiliary-IPL-data-to-separate-locatio.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0002-s390-ccw-move-auxiliary-IPL-data-to-separate-locatio.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0002-s390-ccw-move-auxiliary-IPL-data-to-separate-locatio.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,201 @@ +From 118ee80f7921e8b062c445ac3986ee11409520d0 Mon Sep 17 00:00:00 2001 +From: "Collin L. Walling" +Date: Fri, 23 Feb 2018 10:43:11 -0500 +Subject: [PATCH] s390-ccw: move auxiliary IPL data to separate location + +The s390-ccw firmware needs some information in support of the +boot process which is not available on the native machine. +Examples are the netboot firmware load address and now the +boot menu parameters. + +While storing that data in unused fields of the IPL parameter block +works, that approach could create problems if the parameter block +definition should change in the future. Because then a guest could +overwrite these fields using the set IPLB diagnose. + +In fact the data in question is of more global nature and not really +tied to an IPL device, so separating it is rather logical. + +This commit introduces a new structure to hold firmware relevant +IPL parameters set by QEMU. The data is stored at location 204 (dec) +and can contain up to 7 32-bit words. This area is available to +programming in the z/Architecture Principles of Operation and +can thus safely be used by the firmware until the IPL has completed. + +Signed-off-by: Viktor Mihajlovski +Signed-off-by: Collin L. Walling +Reviewed-by: Thomas Huth +Acked-by: Christian Borntraeger +[thuth: fixed "4 + 8 * n" comment] +Signed-off-by: Thomas Huth +--- + hw/s390x/ipl.c | 18 +++++++++++++++++- + hw/s390x/ipl.h | 25 +++++++++++++++++++++++-- + pc-bios/s390-ccw/iplb.h | 18 ++++++++++++++++-- + pc-bios/s390-ccw/main.c | 6 +++++- + 4 files changed, 61 insertions(+), 6 deletions(-) + +diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c +index 0d06fc12b6..79f5a58adb 100644 +--- a/hw/s390x/ipl.c ++++ b/hw/s390x/ipl.c +@@ -399,6 +399,21 @@ void s390_reipl_request(void) + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + ++static void s390_ipl_prepare_qipl(S390CPU *cpu) ++{ ++ S390IPLState *ipl = get_ipl_device(); ++ uint8_t *addr; ++ uint64_t len = 4096; ++ ++ addr = cpu_physical_memory_map(cpu->env.psa, &len, 1); ++ if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) { ++ error_report("Cannot set QEMU IPL parameters"); ++ return; ++ } ++ memcpy(addr + QIPL_ADDRESS, &ipl->qipl, sizeof(QemuIplParameters)); ++ cpu_physical_memory_unmap(addr, len, 1, len); ++} ++ + void s390_ipl_prepare_cpu(S390CPU *cpu) + { + S390IPLState *ipl = get_ipl_device(); +@@ -418,8 +433,9 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) + error_report_err(err); + vm_stop(RUN_STATE_INTERNAL_ERROR); + } +- ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr); ++ ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); + } ++ s390_ipl_prepare_qipl(cpu); + } + + static void s390_ipl_reset(DeviceState *dev) +diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h +index 8a705e0428..5cc3b770d4 100644 +--- a/hw/s390x/ipl.h ++++ b/hw/s390x/ipl.h +@@ -16,8 +16,7 @@ + #include "cpu.h" + + struct IplBlockCcw { +- uint64_t netboot_start_addr; +- uint8_t reserved0[77]; ++ uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; +@@ -90,6 +89,27 @@ void s390_ipl_prepare_cpu(S390CPU *cpu); + IplParameterBlock *s390_ipl_get_iplb(void); + void s390_reipl_request(void); + ++#define QIPL_ADDRESS 0xcc ++ ++/* ++ * The QEMU IPL Parameters will be stored at absolute address ++ * 204 (0xcc) which means it is 32-bit word aligned but not ++ * double-word aligned. ++ * Placement of data fields in this area must account for ++ * their alignment needs. E.g., netboot_start_address must ++ * have an offset of 4 + n * 8 bytes within the struct in order ++ * to keep it double-word aligned. ++ * The total size of the struct must never exceed 28 bytes. ++ * This definition must be kept in sync with the defininition ++ * in pc-bios/s390-ccw/iplb.h. ++ */ ++struct QemuIplParameters { ++ uint8_t reserved1[4]; ++ uint64_t netboot_start_addr; ++ uint8_t reserved2[16]; ++} QEMU_PACKED; ++typedef struct QemuIplParameters QemuIplParameters; ++ + #define TYPE_S390_IPL "s390-ipl" + #define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL) + +@@ -105,6 +125,7 @@ struct S390IPLState { + bool iplb_valid; + bool reipl_requested; + bool netboot; ++ QemuIplParameters qipl; + + /*< public >*/ + char *kernel; +diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h +index 890aed9ece..31d2934762 100644 +--- a/pc-bios/s390-ccw/iplb.h ++++ b/pc-bios/s390-ccw/iplb.h +@@ -13,8 +13,7 @@ + #define IPLB_H + + struct IplBlockCcw { +- uint64_t netboot_start_addr; +- uint8_t reserved0[77]; ++ uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; +@@ -73,6 +72,21 @@ typedef struct IplParameterBlock IplParameterBlock; + + extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); + ++#define QIPL_ADDRESS 0xcc ++ ++/* ++ * This definition must be kept in sync with the defininition ++ * in hw/s390x/ipl.h ++ */ ++struct QemuIplParameters { ++ uint8_t reserved1[4]; ++ uint64_t netboot_start_addr; ++ uint8_t reserved2[16]; ++} __attribute__ ((packed)); ++typedef struct QemuIplParameters QemuIplParameters; ++ ++extern QemuIplParameters qipl; ++ + #define S390_IPL_TYPE_FCP 0x00 + #define S390_IPL_TYPE_CCW 0x02 + #define S390_IPL_TYPE_QEMU_SCSI 0xff +diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c +index e857ce4f60..e41b264a6f 100644 +--- a/pc-bios/s390-ccw/main.c ++++ b/pc-bios/s390-ccw/main.c +@@ -16,6 +16,7 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); + static SubChannelId blk_schid = { .one = 1 }; + IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); + static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; ++QemuIplParameters qipl; + + /* + * Priniciples of Operations (SA22-7832-09) chapter 17 requires that +@@ -81,6 +82,7 @@ static void virtio_setup(void) + uint16_t dev_no; + char ldp[] = "LOADPARM=[________]\n"; + VDev *vdev = virtio_get_device(); ++ QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + + /* + * We unconditionally enable mss support. In every sane configuration, +@@ -93,6 +95,8 @@ static void virtio_setup(void) + memcpy(ldp + 10, loadparm, 8); + sclp_print(ldp); + ++ memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); ++ + if (store_iplb(&iplb)) { + switch (iplb.pbt) { + case S390_IPL_TYPE_CCW: +@@ -127,7 +131,7 @@ static void virtio_setup(void) + + if (virtio_get_device_type() == VIRTIO_ID_NET) { + sclp_print("Network boot device detected\n"); +- vdev->netboot_start_addr = iplb.ccw.netboot_start_addr; ++ vdev->netboot_start_addr = qipl.netboot_start_addr; + } else { + virtio_blk_setup_device(blk_schid); + +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0101-s390-Do-not-pass-inofficial-IPL-type-to-the-guest.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0101-s390-Do-not-pass-inofficial-IPL-type-to-the-guest.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0101-s390-Do-not-pass-inofficial-IPL-type-to-the-guest.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0101-s390-Do-not-pass-inofficial-IPL-type-to-the-guest.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,80 @@ +From e8c7ef288abb05b741a95418ee2de85c1071e0db Mon Sep 17 00:00:00 2001 +From: Viktor Mihajlovski +Date: Thu, 5 Apr 2018 17:07:24 +0200 +Subject: [PATCH] s390: Do not pass inofficial IPL type to the guest + +IPL over a virtio-scsi device requires special handling not +available in the real architecture. For this purpose the IPL +type 0xFF has been chosen as means of communication between +QEMU and the pc-bios. However, a guest OS could be confused +by seeing an unknown IPL type. + +This change sets the IPL parameter type to 0x02 (CCW) to prevent +this. Pre-existing Linux has looked up the IPL parameters only in +the case of FCP IPL. This means that the behavior should stay +the same even if Linux checks for the IPL type unconditionally. + +Signed-off-by: Viktor Mihajlovski +Message-Id: <1522940844-12336-4-git-send-email-mihajlov@linux.vnet.ibm.com> +Reviewed-by: Christian Borntraeger +Signed-off-by: Cornelia Huck +--- + pc-bios/s390-ccw/bootmap.c | 7 +++++++ + pc-bios/s390-ccw/iplb.h | 15 +++++++++++++-- + 2 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c +index fc2a9fe33b..9287b7a70f 100644 +--- a/pc-bios/s390-ccw/bootmap.c ++++ b/pc-bios/s390-ccw/bootmap.c +@@ -70,6 +70,13 @@ static void jump_to_IPL_code(uint64_t address) + { + /* store the subsystem information _after_ the bootmap was loaded */ + write_subsystem_identification(); ++ ++ /* prevent unknown IPL types in the guest */ ++ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { ++ iplb.pbt = S390_IPL_TYPE_CCW; ++ set_iplb(&iplb); ++ } ++ + /* + * The IPL PSW is at address 0. We also must not overwrite the + * content of non-BIOS memory after we loaded the guest, so we +diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h +index 7dfce4fbcf..5357a36d51 100644 +--- a/pc-bios/s390-ccw/iplb.h ++++ b/pc-bios/s390-ccw/iplb.h +@@ -97,16 +97,27 @@ extern QemuIplParameters qipl; + #define S390_IPL_TYPE_CCW 0x02 + #define S390_IPL_TYPE_QEMU_SCSI 0xff + +-static inline bool store_iplb(IplParameterBlock *iplb) ++static inline bool manage_iplb(IplParameterBlock *iplb, bool store) + { + register unsigned long addr asm("0") = (unsigned long) iplb; + register unsigned long rc asm("1") = 0; + + asm volatile ("diag %0,%2,0x308\n" + : "+d" (addr), "+d" (rc) +- : "d" (6) ++ : "d" (store ? 6 : 5) + : "memory", "cc"); + return rc == 0x01; + } + ++ ++static inline bool store_iplb(IplParameterBlock *iplb) ++{ ++ return manage_iplb(iplb, true); ++} ++ ++static inline bool set_iplb(IplParameterBlock *iplb) ++{ ++ return manage_iplb(iplb, false); ++} ++ + #endif /* IPLB_H */ +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0102-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0102-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0102-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0102-s390-ccw-force-diag-308-subcode-to-unsigned-long.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,40 @@ +From 63d8b5ace31c1e1f3996fe4cd551d6d377594d5a Mon Sep 17 00:00:00 2001 +From: Cornelia Huck +Date: Wed, 2 May 2018 14:52:21 +0200 +Subject: [PATCH] s390-ccw: force diag 308 subcode to unsigned long + +We currently pass an integer as the subcode parameter. However, +the upper bits of the register containing the subcode need to +be 0, which is not guaranteed unless we explicitly specify the +subcode to be an unsigned long value. + +Fixes: d046c51dad3 ("pc-bios/s390-ccw: Get device address via diag 308/6") +Cc: qemu-stable@nongnu.org +Signed-off-by: Cornelia Huck +Acked-by: Christian Borntraeger +Tested-by: Thomas Huth +Signed-off-by: Thomas Huth +--- + pc-bios/s390-ccw/iplb.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h +index 5357a36d51..ded20c834e 100644 +--- a/pc-bios/s390-ccw/iplb.h ++++ b/pc-bios/s390-ccw/iplb.h +@@ -101,10 +101,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store) + { + register unsigned long addr asm("0") = (unsigned long) iplb; + register unsigned long rc asm("1") = 0; ++ unsigned long subcode = store ? 6 : 5; + + asm volatile ("diag %0,%2,0x308\n" + : "+d" (addr), "+d" (rc) +- : "d" (store ? 6 : 5) ++ : "d" (subcode) + : "memory", "cc"); + return rc == 0x01; + } +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0201-pc-bios-s390-ccw-net-Split-up-net_load-into-init-loa.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0201-pc-bios-s390-ccw-net-Split-up-net_load-into-init-loa.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0201-pc-bios-s390-ccw-net-Split-up-net_load-into-init-loa.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0201-pc-bios-s390-ccw-net-Split-up-net_load-into-init-loa.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,184 @@ +From 0c18822953011ec0a3038c8a5eca1803b72a213e Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Tue, 17 Apr 2018 07:36:10 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Split up net_load() into init, load and + release parts + +When we want to support pxelinux-style network booting later, we've got +to do several TFTP transfers - and we do not want to apply for a new IP +address via DHCP each time. So split up net_load into three parts: + +1. net_init(), which initializes virtio-net, gets an IP address via DHCP + and prints out the related information. + +2. The tftp_load call is now moved directly into the main() function + +3. A new net_release() function which should tear down the network stack + before we are done in the firmware. + +This will make it easier to extend the code in the next patches. + +Acked-by: Christian Borntraeger +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=0c18822953011ec0a3038c8a5eca1803b72a213e +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/netmain.c | 63 ++++++++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 26 deletions(-) + +diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c +index d86d46b03f..8fa9e6c945 100644 +--- a/pc-bios/s390-ccw/netmain.c ++++ b/pc-bios/s390-ccw/netmain.c +@@ -128,13 +128,13 @@ static void seed_rng(uint8_t mac[]) + srand(seed); + } + +-static int tftp_load(filename_ip_t *fnip, void *buffer, int len, +- unsigned int retries, int ip_vers) ++static int tftp_load(filename_ip_t *fnip, void *buffer, int len) + { + tftp_err_t tftp_err; + int rc; + +- rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers); ++ rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, ++ ip_version); + + if (rc > 0) { + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, +@@ -199,20 +199,19 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len, + return rc; + } + +-static int net_load(char *buffer, int len) ++static int net_init(filename_ip_t *fn_ip) + { +- filename_ip_t fn_ip; + uint8_t mac[6]; + int rc; + +- memset(&fn_ip, 0, sizeof(filename_ip_t)); ++ memset(fn_ip, 0, sizeof(filename_ip_t)); + + rc = virtio_net_init(mac); + if (rc < 0) { + puts("Could not initialize network device"); + return -101; + } +- fn_ip.fd = rc; ++ fn_ip->fd = rc; + + printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +@@ -220,10 +219,10 @@ static int net_load(char *buffer, int len) + set_mac_address(mac); /* init ethernet layer */ + seed_rng(mac); + +- rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES); ++ rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES); + if (rc >= 0) { + if (ip_version == 4) { +- set_ipv4_address(fn_ip.own_ip); ++ set_ipv4_address(fn_ip->own_ip); + } + } else { + puts("Could not get IP address"); +@@ -232,18 +231,18 @@ static int net_load(char *buffer, int len) + + if (ip_version == 4) { + printf(" Using IPv4 address: %d.%d.%d.%d\n", +- (fn_ip.own_ip >> 24) & 0xFF, (fn_ip.own_ip >> 16) & 0xFF, +- (fn_ip.own_ip >> 8) & 0xFF, fn_ip.own_ip & 0xFF); ++ (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF, ++ (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF); + } else if (ip_version == 6) { + char ip6_str[40]; +- ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); ++ ipv6_to_str(fn_ip->own_ip6.addr, ip6_str); + printf(" Using IPv6 address: %s\n", ip6_str); + } + + if (rc == -2) { + printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", +- (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, +- (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); ++ (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, ++ (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); + return -102; + } + if (rc == -4 || rc == -3) { +@@ -251,28 +250,31 @@ static int net_load(char *buffer, int len) + return -107; + } + ++ printf(" Using TFTP server: "); + if (ip_version == 4) { +- printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", +- fn_ip.filename, +- (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, +- (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); ++ printf("%d.%d.%d.%d\n", ++ (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, ++ (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); + } else if (ip_version == 6) { + char ip6_str[40]; +- printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); +- ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); ++ ipv6_to_str(fn_ip->server_ip6.addr, ip6_str); + printf("%s\n", ip6_str); + } + +- /* Do the TFTP load and print error message if necessary */ +- rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version); +- +- if (ip_version == 4) { +- dhcp_send_release(fn_ip.fd); ++ if (strlen((char *)fn_ip->filename) > 0) { ++ printf(" Bootfile name: '%s'\n", fn_ip->filename); + } + + return rc; + } + ++static void net_release(filename_ip_t *fn_ip) ++{ ++ if (ip_version == 4) { ++ dhcp_send_release(fn_ip->fd); ++ } ++} ++ + void panic(const char *string) + { + sclp_print(string); +@@ -344,6 +346,7 @@ static void virtio_setup(void) + + void main(void) + { ++ filename_ip_t fn_ip; + int rc; + + sclp_setup(); +@@ -351,7 +354,15 @@ void main(void) + + virtio_setup(); + +- rc = net_load(NULL, (long)_start); ++ rc = net_init(&fn_ip); ++ if (rc) { ++ panic("Network initialization failed. Halting.\n"); ++ } ++ ++ rc = tftp_load(&fn_ip, NULL, (long)_start); ++ ++ net_release(&fn_ip); ++ + if (rc > 0) { + sclp_print("Network loading done, starting kernel...\n"); + asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0202-pc-bios-s390-ccw-net-Use-diag308-to-reset-machine-be.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0202-pc-bios-s390-ccw-net-Use-diag308-to-reset-machine-be.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0202-pc-bios-s390-ccw-net-Use-diag308-to-reset-machine-be.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0202-pc-bios-s390-ccw-net-Use-diag308-to-reset-machine-be.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,299 @@ +From 9a848adf45d6732e62551decb3c0255173090767 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Fri, 20 Apr 2018 11:30:42 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Use diag308 to reset machine before + jumping to the OS + +The netboot firmware so far simply jumped directly into the OS kernel +after the download has been completed. This, however, bears the risk +that the virtio-net device still might be active in the background and +incoming packets are still placed into the buffers - which could destroy +memory of the now-running Linux kernel in case it did not take over the +device fast enough. Also the SCLP console is not put into a well-defined +state here. We should hand over the system in a clean state when jumping +into the kernel, so let's use the same mechanism as it's done in the +main s390-ccw firmware and reset the machine with diag308 into a clean +state before jumping into the OS kernel code. To be able to share the +code with the main s390-ccw firmware, the related functions are now +extracted from bootmap.c into a new file called jump2ipl.c. + +Since we now also set the boot device schid at address 184 for the network +boot device, this patch also slightly changes the way how we detect the +entry points for non-ELF binary images: The code now looks for the "S390EP" +magic first and then jumps to 0x10000 in case it has been found. This is +necessary for booting from network devices, since the normal kernel code +(where the PSW at ddress 0 points to) tries to do a block load from the +boot device. This of course fails for a virtio-net device and causes the +kernel to abort with a panic-PSW silently. + +Acked-by: Christian Borntraeger +Signed-off-by: Thomas Huth + +Backport-Note: there were some changes for local bootloader that changed +the context this applies to. +Author: Christian Ehrhardt +Original-Author: Thomas Huth +Origin: backport, https://git.qemu.org/?p=qemu.git;a=commit;h=9a848adf45d6732e62551decb3c0255173090767 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/Makefile | 4 +- + pc-bios/s390-ccw/bootmap.c | 63 +------------------------ + pc-bios/s390-ccw/bootmap.h | 4 -- + pc-bios/s390-ccw/jump2ipl.c | 91 ++++++++++++++++++++++++++++++++++++ + pc-bios/s390-ccw/netboot.mak | 3 +- + pc-bios/s390-ccw/netmain.c | 11 ++++- + pc-bios/s390-ccw/s390-ccw.h | 4 ++ + 7 files changed, 111 insertions(+), 69 deletions(-) + create mode 100644 pc-bios/s390-ccw/jump2ipl.c + +--- a/pc-bios/s390-ccw/Makefile ++++ b/pc-bios/s390-ccw/Makefile +@@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s3 + + .PHONY : all clean build-all + +-OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o ++OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o + QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) + QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float + QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing +--- a/pc-bios/s390-ccw/bootmap.c ++++ b/pc-bios/s390-ccw/bootmap.c +@@ -29,61 +29,6 @@ + /* Scratch space */ + static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE))); + +-typedef struct ResetInfo { +- uint32_t ipl_mask; +- uint32_t ipl_addr; +- uint32_t ipl_continue; +-} ResetInfo; +- +-static ResetInfo save; +- +-static void jump_to_IPL_2(void) +-{ +- ResetInfo *current = 0; +- +- void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; +- *current = save; +- ipl(); /* should not return */ +-} +- +-static void jump_to_IPL_code(uint64_t address) +-{ +- /* store the subsystem information _after_ the bootmap was loaded */ +- write_subsystem_identification(); +- +- /* prevent unknown IPL types in the guest */ +- if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { +- iplb.pbt = S390_IPL_TYPE_CCW; +- set_iplb(&iplb); +- } +- +- /* +- * The IPL PSW is at address 0. We also must not overwrite the +- * content of non-BIOS memory after we loaded the guest, so we +- * save the original content and restore it in jump_to_IPL_2. +- */ +- ResetInfo *current = 0; +- +- save = *current; +- current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; +- current->ipl_continue = address & 0x7fffffff; +- +- debug_print_int("set IPL addr to", current->ipl_continue); +- +- /* Ensure the guest output starts fresh */ +- sclp_print("\n"); +- +- /* +- * HACK ALERT. +- * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 +- * can then use r15 as its stack pointer. +- */ +- asm volatile("lghi 1,1\n\t" +- "diag 1,1,0x308\n\t" +- : : : "1", "memory"); +- panic("\n! IPL returns !\n"); +-} +- + /*********************************************************************** + * IPL an ECKD DASD (CDL or LDL/CMS format) + */ +@@ -625,13 +570,7 @@ static void load_iso_bc_entry(IsoBcSecti + (void *)((uint64_t)bswap16(s.load_segment)), + blks_to_load); + +- /* Trying to get PSW at zero address */ +- if (*((uint64_t *)0) & IPL_PSW_MASK) { +- jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); +- } +- +- /* Try default linux start address */ +- jump_to_IPL_code(KERN_IMAGE_START); ++ jump_to_low_kernel(); + } + + static uint32_t find_iso_bc(void) +--- a/pc-bios/s390-ccw/bootmap.h ++++ b/pc-bios/s390-ccw/bootmap.h +@@ -318,10 +318,6 @@ static inline uint32_t iso_733_to_u32(ui + #define ISO_SECTOR_SIZE 2048 + /* El Torito specifies boot image size in 512 byte blocks */ + #define ET_SECTOR_SHIFT 2 +-#define KERN_IMAGE_START 0x010000UL +-#define PSW_MASK_64 0x0000000100000000ULL +-#define PSW_MASK_32 0x0000000080000000ULL +-#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) + + #define ISO_PRIMARY_VD_SECTOR 16 + +--- /dev/null ++++ b/pc-bios/s390-ccw/jump2ipl.c +@@ -0,0 +1,91 @@ ++/* ++ * QEMU s390-ccw firmware - jump to IPL code ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or (at ++ * your option) any later version. See the COPYING file in the top-level ++ * directory. ++ */ ++ ++#include "libc.h" ++#include "s390-ccw.h" ++ ++#define KERN_IMAGE_START 0x010000UL ++#define PSW_MASK_64 0x0000000100000000ULL ++#define PSW_MASK_32 0x0000000080000000ULL ++#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) ++ ++typedef struct ResetInfo { ++ uint32_t ipl_mask; ++ uint32_t ipl_addr; ++ uint32_t ipl_continue; ++} ResetInfo; ++ ++static ResetInfo save; ++ ++static void jump_to_IPL_2(void) ++{ ++ ResetInfo *current = 0; ++ ++ void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; ++ *current = save; ++ ipl(); /* should not return */ ++} ++ ++void jump_to_IPL_code(uint64_t address) ++{ ++ /* store the subsystem information _after_ the bootmap was loaded */ ++ write_subsystem_identification(); ++ ++ /* prevent unknown IPL types in the guest */ ++ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { ++ iplb.pbt = S390_IPL_TYPE_CCW; ++ set_iplb(&iplb); ++ } ++ ++ /* ++ * The IPL PSW is at address 0. We also must not overwrite the ++ * content of non-BIOS memory after we loaded the guest, so we ++ * save the original content and restore it in jump_to_IPL_2. ++ */ ++ ResetInfo *current = 0; ++ ++ save = *current; ++ current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; ++ current->ipl_continue = address & 0x7fffffff; ++ ++ debug_print_int("set IPL addr to", current->ipl_continue); ++ ++ /* Ensure the guest output starts fresh */ ++ sclp_print("\n"); ++ ++ /* ++ * HACK ALERT. ++ * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 ++ * can then use r15 as its stack pointer. ++ */ ++ asm volatile("lghi 1,1\n\t" ++ "diag 1,1,0x308\n\t" ++ : : : "1", "memory"); ++ panic("\n! IPL returns !\n"); ++} ++ ++void jump_to_low_kernel(void) ++{ ++ /* ++ * If it looks like a Linux binary, i.e. there is the "S390EP" magic from ++ * arch/s390/kernel/head.S here, then let's jump to the well-known Linux ++ * kernel start address (when jumping to the PSW-at-zero address instead, ++ * the kernel startup code fails when we booted from a network device). ++ */ ++ if (!memcmp((char *)0x10008, "S390EP", 6)) { ++ jump_to_IPL_code(KERN_IMAGE_START); ++ } ++ ++ /* Trying to get PSW at zero address */ ++ if (*((uint64_t *)0) & IPL_PSW_MASK) { ++ jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); ++ } ++ ++ /* No other option left, so use the Linux kernel start address */ ++ jump_to_IPL_code(KERN_IMAGE_START); ++} +--- a/pc-bios/s390-ccw/netboot.mak ++++ b/pc-bios/s390-ccw/netboot.mak +@@ -1,7 +1,8 @@ + + SLOF_DIR := $(SRC_PATH)/roms/SLOF + +-NETOBJS := start.o sclp.o virtio.o virtio-net.o netmain.o libnet.a libc.a ++NETOBJS := start.o sclp.o virtio.o virtio-net.o jump2ipl.o netmain.o \ ++ libnet.a libc.a + + LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include + LIBNET_INC := -I$(SLOF_DIR)/lib/libnet +--- a/pc-bios/s390-ccw/netmain.c ++++ b/pc-bios/s390-ccw/netmain.c +@@ -283,6 +283,15 @@ void panic(const char *string) + } + } + ++void write_subsystem_identification(void) ++{ ++ SubChannelId *schid = (SubChannelId *) 184; ++ uint32_t *zeroes = (uint32_t *) 188; ++ ++ *schid = net_schid; ++ *zeroes = 0; ++} ++ + static bool find_net_dev(Schib *schib, int dev_no) + { + int i, r; +@@ -365,7 +374,7 @@ void main(void) + + if (rc > 0) { + sclp_print("Network loading done, starting kernel...\n"); +- asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); ++ jump_to_low_kernel(); + } + + panic("Failed to load OS from network\n"); +--- a/pc-bios/s390-ccw/s390-ccw.h ++++ b/pc-bios/s390-ccw/s390-ccw.h +@@ -84,6 +84,10 @@ ulong get_second(void); + /* bootmap.c */ + void zipl_load(void); + ++/* jump2ipl.c */ ++void jump_to_IPL_code(uint64_t address); ++void jump_to_low_kernel(void); ++ + static inline void fill_hex(char *out, unsigned char val) + { + const char hex[] = "0123456789abcdef"; diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0203-pc-bios-s390-ccw-net-Add-support-for-.INS-config-fil.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0203-pc-bios-s390-ccw-net-Add-support-for-.INS-config-fil.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0203-pc-bios-s390-ccw-net-Add-support-for-.INS-config-fil.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0203-pc-bios-s390-ccw-net-Add-support-for-.INS-config-fil.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,165 @@ +From c4942ee94271052b68125ec8c06e8c71a967aad3 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Wed, 18 Apr 2018 14:02:37 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Add support for .INS config files + +The .INS config files can normally be found on CD-ROM ISO images, +so by supporting these files, it is now possible to boot directly +when the TFTP server is set up with the contents of such an CD-ROM +image. + +Acked-by: Christian Borntraeger +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=c4942ee94271052b68125ec8c06e8c71a967aad3 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/netmain.c | 100 +++++++++++++++++++++++++++++++++++-- + 1 file changed, 95 insertions(+), 5 deletions(-) + +diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c +index 69a82c0d56..600024155b 100644 +--- a/pc-bios/s390-ccw/netmain.c ++++ b/pc-bios/s390-ccw/netmain.c +@@ -39,8 +39,12 @@ + + extern char _start[]; + ++#define KERNEL_ADDR ((void *)0L) ++#define KERNEL_MAX_SIZE ((long)_start) ++ + 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 int ip_version = 4; +@@ -136,9 +140,15 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) + rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, + ip_version); + +- if (rc > 0) { +- printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, +- rc / 1024); ++ if (rc < 0) { ++ /* Make sure that error messages are put into a new line */ ++ printf("\n "); ++ } ++ ++ if (rc > 1024) { ++ printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024); ++ } else if (rc > 0) { ++ printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc); + } else if (rc == -1) { + puts("unknown TFTP error"); + } else if (rc == -2) { +@@ -275,6 +285,83 @@ static void net_release(filename_ip_t *fn_ip) + } + } + ++/** ++ * Load via information from a .INS file (which can be found on CD-ROMs ++ * for example) ++ */ ++static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize) ++{ ++ char *ptr; ++ int rc = -1, llen; ++ void *destaddr; ++ char *insbuf = cfg; ++ ++ ptr = strchr(insbuf, '\n'); ++ if (!ptr) { ++ puts("Does not seem to be a valid .INS file"); ++ return -1; ++ } ++ ++ *ptr = 0; ++ printf("\nParsing .INS file:\n %s\n", &insbuf[2]); ++ ++ insbuf = ptr + 1; ++ while (*insbuf && insbuf < cfg + cfgsize) { ++ ptr = strchr(insbuf, '\n'); ++ if (ptr) { ++ *ptr = 0; ++ } ++ llen = strlen(insbuf); ++ if (!llen) { ++ insbuf = ptr + 1; ++ continue; ++ } ++ ptr = strchr(insbuf, ' '); ++ if (!ptr) { ++ puts("Missing space separator in .INS file"); ++ return -1; ++ } ++ *ptr = 0; ++ strncpy((char *)fn_ip->filename, insbuf, sizeof(fn_ip->filename)); ++ destaddr = (char *)atol(ptr + 1); ++ rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr); ++ if (rc <= 0) { ++ break; ++ } ++ insbuf += llen + 1; ++ } ++ ++ return rc; ++} ++ ++static int net_try_direct_tftp_load(filename_ip_t *fn_ip) ++{ ++ int rc; ++ void *loadaddr = (void *)0x2000; /* Load right after the low-core */ ++ ++ rc = tftp_load(fn_ip, loadaddr, KERNEL_MAX_SIZE - (long)loadaddr); ++ if (rc < 0) { ++ return rc; ++ } else if (rc < 8) { ++ printf("'%s' is too small (%i bytes only).\n", fn_ip->filename, rc); ++ return -1; ++ } ++ ++ /* Check whether it is a configuration file instead of a kernel */ ++ if (rc < sizeof(cfgbuf) - 1) { ++ memcpy(cfgbuf, loadaddr, rc); ++ cfgbuf[rc] = 0; /* Make sure that it is NUL-terminated */ ++ if (!strncmp("* ", cfgbuf, 2)) { ++ return handle_ins_cfg(fn_ip, cfgbuf, rc); ++ } ++ } ++ ++ /* Move kernel to right location */ ++ memmove(KERNEL_ADDR, loadaddr, rc); ++ ++ return rc; ++} ++ + void panic(const char *string) + { + sclp_print(string); +@@ -356,7 +443,7 @@ static void virtio_setup(void) + void main(void) + { + filename_ip_t fn_ip; +- int rc; ++ int rc, fnlen; + + sclp_setup(); + sclp_print("Network boot starting...\n"); +@@ -368,7 +455,10 @@ void main(void) + panic("Network initialization failed. Halting.\n"); + } + +- rc = tftp_load(&fn_ip, NULL, (long)_start); ++ fnlen = strlen((char *)fn_ip.filename); ++ if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { ++ rc = net_try_direct_tftp_load(&fn_ip); ++ } + + net_release(&fn_ip); + +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0204-pc-bios-s390-ccw-net-Update-code-for-the-latest-chan.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0204-pc-bios-s390-ccw-net-Update-code-for-the-latest-chan.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0204-pc-bios-s390-ccw-net-Update-code-for-the-latest-chan.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0204-pc-bios-s390-ccw-net-Update-code-for-the-latest-chan.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,213 @@ +From 134f0b3d7ca5fbbd17f21fea87066967ce1d6de5 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Fri, 18 May 2018 11:31:27 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Update code for the latest changes in + SLOF + +The ip_version information now has to be stored in the filename_ip_t +structure, and there is now a common function called tftp_get_error_info() +which can be used to get the error string for a TFTP error code. +We can also get rid of some superfluous "(char *)" casts now. + +Acked-by: Christian Borntraeger +Tested-by: Viktor Mihajlovski +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=134f0b3d7ca5fbbd17f21fea87066967ce1d6de5 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/netboot.mak | 2 +- + pc-bios/s390-ccw/netmain.c | 86 +++++++----------------------------- + 2 files changed, 18 insertions(+), 70 deletions(-) + +diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak +index 4f64128c6c..a73be367e6 100644 +--- a/pc-bios/s390-ccw/netboot.mak ++++ b/pc-bios/s390-ccw/netboot.mak +@@ -34,7 +34,7 @@ STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o + %.o : $(SLOF_DIR)/lib/libc/stdlib/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + +-STDIO_OBJS = sprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ ++STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ + printf.o putc.o puts.o putchar.o stdchnls.o fileno.o + %.o : $(SLOF_DIR)/lib/libc/stdio/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") +diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c +index 600024155b..d007fb7a86 100644 +--- a/pc-bios/s390-ccw/netmain.c ++++ b/pc-bios/s390-ccw/netmain.c +@@ -47,7 +47,6 @@ IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); + static char cfgbuf[2048]; + + static SubChannelId net_schid = { .one = 1 }; +-static int ip_version = 4; + static uint64_t dest_timer; + + static uint64_t get_timer_ms(void) +@@ -100,10 +99,10 @@ static int dhcp(struct filename_ip *fn_ip, int retries) + printf("\nGiving up after %d DHCP requests\n", retries); + return -1; + } +- ip_version = 4; ++ fn_ip->ip_version = 4; + rc = dhcpv4(NULL, fn_ip); + if (rc == -1) { +- ip_version = 6; ++ fn_ip->ip_version = 6; + set_ipv6_address(fn_ip->fd, 0); + rc = dhcpv6(NULL, fn_ip); + if (rc == 0) { +@@ -137,8 +136,7 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) + tftp_err_t tftp_err; + int rc; + +- rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428, +- ip_version); ++ rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err); + + if (rc < 0) { + /* Make sure that error messages are put into a new line */ +@@ -149,61 +147,11 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len) + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024); + } else if (rc > 0) { + printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc); +- } else if (rc == -1) { +- puts("unknown TFTP error"); +- } else if (rc == -2) { +- printf("TFTP buffer of %d bytes is too small for %s\n", +- len, fnip->filename); +- } else if (rc == -3) { +- printf("file not found: %s\n", fnip->filename); +- } else if (rc == -4) { +- puts("TFTP access violation"); +- } else if (rc == -5) { +- puts("illegal TFTP operation"); +- } else if (rc == -6) { +- puts("unknown TFTP transfer ID"); +- } else if (rc == -7) { +- puts("no such TFTP user"); +- } else if (rc == -8) { +- puts("TFTP blocksize negotiation failed"); +- } else if (rc == -9) { +- puts("file exceeds maximum TFTP transfer size"); +- } else if (rc <= -10 && rc >= -15) { +- const char *icmp_err_str; +- switch (rc) { +- case -ICMP_NET_UNREACHABLE - 10: +- icmp_err_str = "net unreachable"; +- break; +- case -ICMP_HOST_UNREACHABLE - 10: +- icmp_err_str = "host unreachable"; +- break; +- case -ICMP_PROTOCOL_UNREACHABLE - 10: +- icmp_err_str = "protocol unreachable"; +- break; +- case -ICMP_PORT_UNREACHABLE - 10: +- icmp_err_str = "port unreachable"; +- break; +- case -ICMP_FRAGMENTATION_NEEDED - 10: +- icmp_err_str = "fragmentation needed and DF set"; +- break; +- case -ICMP_SOURCE_ROUTE_FAILED - 10: +- icmp_err_str = "source route failed"; +- break; +- default: +- icmp_err_str = " UNKNOWN"; +- break; +- } +- printf("ICMP ERROR \"%s\"\n", icmp_err_str); +- } else if (rc == -40) { +- printf("TFTP error occurred after %d bad packets received", +- tftp_err.bad_tftp_packets); +- } else if (rc == -41) { +- printf("TFTP error occurred after missing %d responses", +- tftp_err.no_packets); +- } else if (rc == -42) { +- printf("TFTP error missing block %d, expected block was %d", +- tftp_err.blocks_missed, +- tftp_err.blocks_received); ++ } else { ++ const char *errstr = NULL; ++ int ecode; ++ tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); ++ printf("TFTP error: %s\n", errstr ? errstr : "unknown error"); + } + + return rc; +@@ -231,7 +179,7 @@ static int net_init(filename_ip_t *fn_ip) + + rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES); + if (rc >= 0) { +- if (ip_version == 4) { ++ if (fn_ip->ip_version == 4) { + set_ipv4_address(fn_ip->own_ip); + } + } else { +@@ -239,11 +187,11 @@ static int net_init(filename_ip_t *fn_ip) + return -101; + } + +- if (ip_version == 4) { ++ if (fn_ip->ip_version == 4) { + printf(" Using IPv4 address: %d.%d.%d.%d\n", + (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF, + (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF); +- } else if (ip_version == 6) { ++ } else if (fn_ip->ip_version == 6) { + char ip6_str[40]; + ipv6_to_str(fn_ip->own_ip6.addr, ip6_str); + printf(" Using IPv6 address: %s\n", ip6_str); +@@ -261,17 +209,17 @@ static int net_init(filename_ip_t *fn_ip) + } + + printf(" Using TFTP server: "); +- if (ip_version == 4) { ++ if (fn_ip->ip_version == 4) { + printf("%d.%d.%d.%d\n", + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); +- } else if (ip_version == 6) { ++ } else if (fn_ip->ip_version == 6) { + char ip6_str[40]; + ipv6_to_str(fn_ip->server_ip6.addr, ip6_str); + printf("%s\n", ip6_str); + } + +- if (strlen((char *)fn_ip->filename) > 0) { ++ if (strlen(fn_ip->filename) > 0) { + printf(" Bootfile name: '%s'\n", fn_ip->filename); + } + +@@ -280,7 +228,7 @@ static int net_init(filename_ip_t *fn_ip) + + static void net_release(filename_ip_t *fn_ip) + { +- if (ip_version == 4) { ++ if (fn_ip->ip_version == 4) { + dhcp_send_release(fn_ip->fd); + } + } +@@ -322,7 +270,7 @@ static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize) + return -1; + } + *ptr = 0; +- strncpy((char *)fn_ip->filename, insbuf, sizeof(fn_ip->filename)); ++ strncpy(fn_ip->filename, insbuf, sizeof(fn_ip->filename)); + destaddr = (char *)atol(ptr + 1); + rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr); + if (rc <= 0) { +@@ -455,7 +403,7 @@ void main(void) + panic("Network initialization failed. Halting.\n"); + } + +- fnlen = strlen((char *)fn_ip.filename); ++ fnlen = strlen(fn_ip.filename); + if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { + rc = net_try_direct_tftp_load(&fn_ip); + } +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0205-pc-bios-s390-ccw-net-Add-support-for-pxelinux-style-.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0205-pc-bios-s390-ccw-net-Add-support-for-pxelinux-style-.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0205-pc-bios-s390-ccw-net-Add-support-for-pxelinux-style-.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0205-pc-bios-s390-ccw-net-Add-support-for-pxelinux-style-.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,200 @@ +From ec623990b34ab0e271141356af96d67a0c4e980d Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Tue, 22 May 2018 11:37:29 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Add support for pxelinux-style config + files + +Since it is quite cumbersome to manually create a combined kernel with +initrd image for network booting, we now support loading via pxelinux +configuration files, too. In these files, the kernel, initrd and command +line parameters can be specified seperately, and the firmware then takes +care of glueing everything together in memory after the files have been +downloaded. See this URL for details about the config file layout: +https://www.syslinux.org/wiki/index.php?title=PXELINUX + +The user can either specify a config file directly as bootfile via DHCP +(but in this case, the file has to start either with "default" or a "#" +comment so we can distinguish it from binary kernels), or a folder (i.e. +the bootfile name must end with "/") where the firmware should look for +the typical pxelinux.cfg file names, e.g. based on MAC or IP address. +We also support the pxelinux.cfg DHCP options 209 and 210 from RFC 5071. + +Acked-by: Christian Borntraeger +Tested-by: Viktor Mihajlovski +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=ec623990b34ab0e271141356af96d67a0c4e980d +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/netboot.mak | 7 +-- + pc-bios/s390-ccw/netmain.c | 86 +++++++++++++++++++++++++++++++++++- + 2 files changed, 89 insertions(+), 4 deletions(-) + +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); + +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0206-pc-bios-s390-ccw-net-Try-to-load-pxelinux.cfg-file-a.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0206-pc-bios-s390-ccw-net-Try-to-load-pxelinux.cfg-file-a.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0206-pc-bios-s390-ccw-net-Try-to-load-pxelinux.cfg-file-a.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0206-pc-bios-s390-ccw-net-Try-to-load-pxelinux.cfg-file-a.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,106 @@ +From 0d8261b506933c245b79ca6a57422dc81d8989c1 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Tue, 22 May 2018 11:53:51 +0200 +Subject: [PATCH] pc-bios/s390-ccw/net: Try to load pxelinux.cfg file accoring + to the UUID + +With the STSI instruction, we can get the UUID of the current VM instance, +so we can support loading pxelinux config files via UUID in the file name, +too. + +Acked-by: Christian Borntraeger +Tested-by: Viktor Mihajlovski +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=0d8261b506933c245b79ca6a57422dc81d8989c1 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/netmain.c | 56 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 55 insertions(+), 1 deletion(-) + +diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c +index c059546480..0392131c27 100644 +--- a/pc-bios/s390-ccw/netmain.c ++++ b/pc-bios/s390-ccw/netmain.c +@@ -44,6 +44,9 @@ extern char _start[]; + #define KERNEL_MAX_SIZE ((long)_start) + #define ARCH_COMMAND_LINE_SIZE 896 /* Taken from Linux kernel */ + ++/* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */ ++#define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4) ++ + char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); + IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); + static char cfgbuf[2048]; +@@ -235,6 +238,56 @@ static void net_release(filename_ip_t *fn_ip) + } + } + ++/** ++ * Retrieve the Universally Unique Identifier of the VM. ++ * @return UUID string, or NULL in case of errors ++ */ ++static const char *get_uuid(void) ++{ ++ register int r0 asm("0"); ++ register int r1 asm("1"); ++ uint8_t *mem, *buf, uuid[16]; ++ int i, cc, chk = 0; ++ static char uuid_str[37]; ++ ++ mem = malloc(2 * PAGE_SIZE); ++ if (!mem) { ++ puts("Out of memory ... can not get UUID."); ++ return NULL; ++ } ++ buf = (uint8_t *)(((uint64_t)mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); ++ memset(buf, 0, PAGE_SIZE); ++ ++ /* Get SYSIB 3.2.2 */ ++ r0 = (3 << 28) | 2; ++ r1 = 2; ++ asm volatile(" stsi 0(%[addr])\n" ++ " ipm %[cc]\n" ++ " srl %[cc],28\n" ++ : [cc] "=d" (cc) ++ : "d" (r0), "d" (r1), [addr] "a" (buf) ++ : "cc", "memory"); ++ if (cc) { ++ return NULL; ++ } ++ ++ for (i = 0; i < 16; i++) { ++ uuid[i] = buf[STSI322_VMDB_UUID_OFFSET + i]; ++ chk |= uuid[i]; ++ } ++ free(mem); ++ if (!chk) { ++ return NULL; ++ } ++ ++ sprintf(uuid_str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" ++ "%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], ++ uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], ++ uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); ++ ++ return uuid_str; ++} ++ + /** + * Load a kernel with initrd (i.e. with the information that we've got from + * a pxelinux.cfg config file) +@@ -285,7 +338,8 @@ 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, ++ num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), ++ DEFAULT_TFTP_RETRIES, + cfgbuf, sizeof(cfgbuf), + entries, MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent > 0) { +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0207-pc-bios-s390-ccw-Optimize-the-s390-netboot.img-for-s.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0207-pc-bios-s390-ccw-Optimize-the-s390-netboot.img-for-s.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0207-pc-bios-s390-ccw-Optimize-the-s390-netboot.img-for-s.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-0207-pc-bios-s390-ccw-Optimize-the-s390-netboot.img-for-s.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,65 @@ +From 63c93fac18546ef9468c7b522bad0ae43f9f58ba Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Thu, 14 Jun 2018 10:38:22 +0200 +Subject: [PATCH] pc-bios/s390-ccw: Optimize the s390-netboot.img for size + +The -O2 optimization flag is passed via CFLAGS to the firmware Makefile, +but in netbook.mak, we've got some rules that only use QEMU_CFLAGS for +compiling the libc and libnet from SLOF, so these files get compiled +without optimization so far. Use CFLAGS here, too, to create faster +and smaller code. + +We can additionally save some more bytes in the firmware images by compi- +ling the code with -fno-asynchronous-unwind-tables. This will omit some +ELF sections (used for stack unwinding for example) from the image that +we do not need in the firmware. + +Acked-by: Christian Borntraeger +Signed-off-by: Thomas Huth + +Origin: upstream, https://git.qemu.org/?p=qemu.git;a=commit;h=63c93fac18546ef9468c7b522bad0ae43f9f58ba +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2018-09-25 + +--- + pc-bios/s390-ccw/Makefile | 1 + + pc-bios/s390-ccw/netboot.mak | 4 ++-- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile +index 439e3cc9c9..1eb316b02f 100644 +--- a/pc-bios/s390-ccw/Makefile ++++ b/pc-bios/s390-ccw/Makefile +@@ -15,6 +15,7 @@ OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ + QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) + QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float + QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing ++QEMU_CFLAGS += -fno-asynchronous-unwind-tables + QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector) + LDFLAGS += -Wl,-pie -nostdlib + +diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak +index 8af0cfd2da..14e96b2aa6 100644 +--- a/pc-bios/s390-ccw/netboot.mak ++++ b/pc-bios/s390-ccw/netboot.mak +@@ -19,7 +19,7 @@ s390-netboot.img: s390-netboot.elf + + # libc files: + +-LIBC_CFLAGS := $(QEMU_CFLAGS) $(LIBC_INC) $(LIBNET_INC) ++LIBC_CFLAGS := $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) + + CTYPE_OBJS = isdigit.o isxdigit.o toupper.o + %.o : $(SLOF_DIR)/lib/libc/ctype/%.c +@@ -52,7 +52,7 @@ libc.a: $(LIBCOBJS) + + 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 pxelinux.o +-LIBNETCFLAGS := $(QEMU_CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) ++LIBNETCFLAGS := $(QEMU_CFLAGS) $(CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) + + %.o : $(SLOF_DIR)/lib/libnet/%.c + $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") +-- +2.17.1 + diff -Nru qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch --- qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-2.11+dfsg/debian/patches/ubuntu/lp-1790901-partial-SLOF-for-s390x-netboot-2.11-to-3.0.patch 2018-09-25 11:31:15.000000000 +0000 @@ -0,0 +1,1462 @@ +From 7efa6be0241cfac399458ebc1cd9d09f33e56afd Mon Sep 17 00:00:00 2001 +From: Christian Ehrhardt +Date: Tue, 25 Sep 2018 12:04:55 +0200 +Subject: [PATCH] SLOF: update bits for s390x Image build from qemu 2.11 to + qemu 3.0 + +This is the Delta of the following paths from qemu 2.11 to qemu 3.0 +Archive 1: https://download.qemu.org/qemu-2.11.0.tar.xz +Archive 2: https://download.qemu.org/qemu-3.0.0.tar.xz + +Paths: + lib/libc/ctype/* + lib/libc/string/* + lib/libc/stdlib/* + lib/libc/include/* + lib/libc/stdio/* + lib/libnet/* + LICENSE + slof/sbrk.c + +Signed-off-by: Christian Ehrhardt + +Forwarded: not-needed +Author: Christian Ehrhardt +Origin: https://download.qemu.org/qemu-3.0.0.tar.xz +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1790901 +Last-Update: 2019-09-25 + +--- + roms/SLOF/lib/libc/include/assert.h | 36 ++++ + roms/SLOF/lib/libc/include/stdio.h | 1 + + roms/SLOF/lib/libc/stdio/Makefile.inc | 2 +- + roms/SLOF/lib/libc/stdio/snprintf.c | 28 +++ + roms/SLOF/lib/libc/stdlib/free.c | 4 +- + roms/SLOF/lib/libc/string/Makefile.inc | 2 +- + roms/SLOF/lib/libc/string/strrchr.c | 28 +++ + roms/SLOF/lib/libnet/Makefile | 2 +- + roms/SLOF/lib/libnet/bootp.c | 2 +- + roms/SLOF/lib/libnet/dhcp.c | 76 ++++++-- + roms/SLOF/lib/libnet/dhcpv6.c | 3 +- + roms/SLOF/lib/libnet/ipv6.c | 2 +- + roms/SLOF/lib/libnet/libnet.code | 6 +- + roms/SLOF/lib/libnet/netapps.h | 3 +- + roms/SLOF/lib/libnet/netload.c | 252 ++++++++++++++++--------- + roms/SLOF/lib/libnet/ping.c | 14 +- + roms/SLOF/lib/libnet/pxelinux.c | 251 ++++++++++++++++++++++++ + roms/SLOF/lib/libnet/pxelinux.h | 33 ++++ + roms/SLOF/lib/libnet/tftp.c | 132 +++++++++++-- + roms/SLOF/lib/libnet/tftp.h | 16 +- + 20 files changed, 735 insertions(+), 158 deletions(-) + create mode 100644 roms/SLOF/lib/libc/include/assert.h + create mode 100644 roms/SLOF/lib/libc/stdio/snprintf.c + create mode 100644 roms/SLOF/lib/libc/string/strrchr.c + create mode 100644 roms/SLOF/lib/libnet/pxelinux.c + create mode 100644 roms/SLOF/lib/libnet/pxelinux.h + +diff --git a/roms/SLOF/lib/libc/include/assert.h b/roms/SLOF/lib/libc/include/assert.h +new file mode 100644 +index 0000000..01434da +--- /dev/null ++++ b/roms/SLOF/lib/libc/include/assert.h +@@ -0,0 +1,36 @@ ++/***************************************************************************** ++ * assert() macro definition ++ * ++ * Copyright 2018 Red Hat, Inc. ++ * ++ * This program and the accompanying materials are made available under ++ * the terms of the BSD License which accompanies this distribution, and ++ * is available at http://www.opensource.org/licenses/bsd-license.php ++ * ++ * Contributors: ++ * Thomas Huth, Red Hat Inc. - initial implementation ++ *****************************************************************************/ ++ ++#ifndef SLIMLINE_ASSERT_H ++#define SLIMLINE_ASSERT_H ++ ++#ifdef NDEBUG ++ ++#define assert(cond) (void) ++ ++#else ++ ++#define assert(cond) \ ++ do { \ ++ if (!(cond)) { \ ++ fprintf(stderr, \ ++ "ERROR: Assertion '" #cond "' failed!\n" \ ++ "(function %s, file " __FILE__ ", line %i)\n", \ ++ __func__, __LINE__); \ ++ while (1) {} \ ++ } \ ++ } while (0) ++ ++#endif ++ ++#endif /* SLIMLINE_ASSERT_H */ +diff --git a/roms/SLOF/lib/libc/include/stdio.h b/roms/SLOF/lib/libc/include/stdio.h +index c54528f..1cd5faf 100644 +--- a/roms/SLOF/lib/libc/include/stdio.h ++++ b/roms/SLOF/lib/libc/include/stdio.h +@@ -43,6 +43,7 @@ int fileno(FILE *stream); + int printf(const char *format, ...) __attribute__((format (printf, 1, 2))); + int fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); + int sprintf(char *str, const char *format, ...) __attribute__((format (printf, 2, 3))); ++int snprintf(char *str, size_t size, const char *format, ...) __attribute__((format (printf, 3, 4))); + int vfprintf(FILE *stream, const char *format, va_list); + int vsprintf(char *str, const char *format, va_list); + int vsnprintf(char *str, size_t size, const char *format, va_list); +diff --git a/roms/SLOF/lib/libc/stdio/Makefile.inc b/roms/SLOF/lib/libc/stdio/Makefile.inc +index ac5302d..6688317 100644 +--- a/roms/SLOF/lib/libc/stdio/Makefile.inc ++++ b/roms/SLOF/lib/libc/stdio/Makefile.inc +@@ -13,7 +13,7 @@ + + STDIO_SRC_C = fscanf.c sprintf.c vfprintf.c vsnprintf.c vsprintf.c fprintf.c \ + printf.c setvbuf.c putc.c puts.c putchar.c scanf.c stdchnls.c \ +- vfscanf.c vsscanf.c fileno.c ++ vfscanf.c vsscanf.c fileno.c snprintf.c + + STDIO_SRC_ASM = + STDIO_SRCS = $(STDIO_SRC_C:%=$(STDIOCMNDIR)/%) $(STDIO_SRC_ASM:%=$(STDIOCMNDIR)/%) +diff --git a/roms/SLOF/lib/libc/stdio/snprintf.c b/roms/SLOF/lib/libc/stdio/snprintf.c +new file mode 100644 +index 0000000..e3cfa6e +--- /dev/null ++++ b/roms/SLOF/lib/libc/stdio/snprintf.c +@@ -0,0 +1,28 @@ ++/****************************************************************************** ++ * Copyright (c) 2004, 2008 IBM Corporation ++ * All rights reserved. ++ * This program and the accompanying materials ++ * are made available under the terms of the BSD License ++ * which accompanies this distribution, and is available at ++ * http://www.opensource.org/licenses/bsd-license.php ++ * ++ * Contributors: ++ * IBM Corporation - initial implementation ++ *****************************************************************************/ ++ ++#include ++ ++int snprintf(char *buff, size_t size, const char *format, ...) ++{ ++ va_list ar; ++ int count; ++ ++ if (!buff || !format) ++ return -1; ++ ++ va_start(ar, format); ++ count = vsnprintf(buff, size, format, ar); ++ va_end(ar); ++ ++ return count; ++} +diff --git a/roms/SLOF/lib/libc/stdlib/free.c b/roms/SLOF/lib/libc/stdlib/free.c +index 9005450..d276585 100644 +--- a/roms/SLOF/lib/libc/stdlib/free.c ++++ b/roms/SLOF/lib/libc/stdlib/free.c +@@ -19,8 +19,10 @@ free(void *ptr) + { + struct chunk *header; + ++ if (!ptr) ++ return; ++ + header = (struct chunk *) ptr; + header--; + header->inuse = 0; +- + } +diff --git a/roms/SLOF/lib/libc/string/Makefile.inc b/roms/SLOF/lib/libc/string/Makefile.inc +index 7ccf3c4..0a77738 100644 +--- a/roms/SLOF/lib/libc/string/Makefile.inc ++++ b/roms/SLOF/lib/libc/string/Makefile.inc +@@ -13,7 +13,7 @@ + + STRING_SRC_C = strcat.c strchr.c strcmp.c strcpy.c strlen.c strncmp.c \ + strncpy.c strstr.c memset.c memcpy.c memmove.c memchr.c \ +- memcmp.c strcasecmp.c strncasecmp.c strtok.c ++ memcmp.c strcasecmp.c strncasecmp.c strtok.c strrchr.c + STRING_SRC_ASM = + STRING_SRCS = $(STRING_SRC_C:%=$(STRINGCMNDIR)/%) $(STRING_SRC_ASM:%=$(STRINGCMNDIR)/%) + STRING_OBJS = $(STRING_SRC_C:%.c=%.o) $(STRING_SRC_ASM:%.S=%.o) +diff --git a/roms/SLOF/lib/libc/string/strrchr.c b/roms/SLOF/lib/libc/string/strrchr.c +new file mode 100644 +index 0000000..ccfaa9f +--- /dev/null ++++ b/roms/SLOF/lib/libc/string/strrchr.c +@@ -0,0 +1,28 @@ ++/****************************************************************************** ++ * libc strrchr() implementation ++ * ++ * This program and the accompanying materials are made available under ++ * the terms of the BSD License which accompanies this distribution, and ++ * is available at http://www.opensource.org/licenses/bsd-license.php ++ * ++ * Contributors: ++ * Thomas Huth - initial implementation ++ *****************************************************************************/ ++ ++#include ++ ++char * ++strrchr(const char *s, int c) ++{ ++ char cb = c; ++ char *ptr = (char *)s + strlen(s) - 1; ++ ++ while (ptr >= s) { ++ if (*ptr == cb) { ++ return ptr; ++ } ++ --ptr; ++ } ++ ++ return NULL; ++} +diff --git a/roms/SLOF/lib/libnet/Makefile b/roms/SLOF/lib/libnet/Makefile +index dfefea9..a2a6570 100644 +--- a/roms/SLOF/lib/libnet/Makefile ++++ b/roms/SLOF/lib/libnet/Makefile +@@ -19,7 +19,7 @@ include $(TOP)/make.rules + CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/include $(FLAG) + + SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \ +- ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c ++ ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c pxelinux.c + + OBJS = $(SRCS:%.c=%.o) + +diff --git a/roms/SLOF/lib/libnet/bootp.c b/roms/SLOF/lib/libnet/bootp.c +index 6d58cef..464cb66 100644 +--- a/roms/SLOF/lib/libnet/bootp.c ++++ b/roms/SLOF/lib/libnet/bootp.c +@@ -166,7 +166,7 @@ receive_bootp(filename_ip_t * fn_ip) + + fn_ip->own_ip = btph->yiaddr; + fn_ip->server_ip = btph->siaddr; +- strcpy((char *) fn_ip->filename, (char *) btph->file); ++ strcpy(fn_ip->filename, (char *)btph->file); + + #if DEBUG + printf("\nThese are the details of the bootp reply:\n"); +diff --git a/roms/SLOF/lib/libnet/dhcp.c b/roms/SLOF/lib/libnet/dhcp.c +index 0cb4fa4..85cd7c0 100644 +--- a/roms/SLOF/lib/libnet/dhcp.c ++++ b/roms/SLOF/lib/libnet/dhcp.c +@@ -79,6 +79,8 @@ + #define DHCP_TFTP_SERVER 66 + #define DHCP_BOOTFILE 67 + #define DHCP_CLIENT_ARCH 93 ++#define DHCP_PXELINUX_CFGFILE 209 /* See RFC 5071 */ ++#define DHCP_PXELINUX_PREFIX 210 + #define DHCP_ENDOPT 0xFF + #define DHCP_PADOPT 0x00 + +@@ -124,8 +126,8 @@ typedef struct { + uint32_t subnet_mask; /**< o. 1 Subnet mask */ + uint8_t msg_type; /**< o.53 DHCP-message type */ + uint8_t overload; /**< o.52 Overload sname/file fields */ +- int8_t tftp_server[256]; /**< o.66 TFTP server name */ +- int8_t bootfile[256]; /**< o.67 Boot file name */ ++ char tftp_server[256]; /**< o.66 TFTP server name */ ++ char bootfile[256]; /**< o.67 Boot file name */ + uint16_t client_arch; /**< o.93 Client architecture type */ + } dhcp_options_t; + +@@ -167,6 +169,8 @@ static uint32_t dhcp_siaddr_ip = 0; + static char dhcp_filename[256]; + static char dhcp_tftp_name[256]; + static uint32_t dhcp_xid; ++static char *pxelinux_cfgfile; ++static char *pxelinux_prefix; + + static char * response_buffer; + +@@ -185,6 +189,8 @@ int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) + strcpy(dhcp_filename, ""); + strcpy(dhcp_tftp_name, ""); + ++ pxelinux_cfgfile = pxelinux_prefix = NULL; ++ + response_buffer = ret_buffer; + + if (dhcp_attempt(fd) == 0) +@@ -197,7 +203,7 @@ int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) + dhcp_siaddr_ip = fn_ip->server_ip; + } + if(fn_ip->filename[0] != 0) { +- strcpy(dhcp_filename, (char *) fn_ip->filename); ++ strcpy(dhcp_filename, fn_ip->filename); + } + + // TFTP SERVER +@@ -230,7 +236,11 @@ int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) + // Store configuration info into filename_ip strucutre + fn_ip -> own_ip = dhcp_own_ip; + fn_ip -> server_ip = dhcp_tftp_ip; +- strcpy((char *) fn_ip -> filename, dhcp_filename); ++ strcpy(fn_ip->filename, dhcp_filename); ++ ++ fn_ip->pl_cfgfile = pxelinux_cfgfile; ++ fn_ip->pl_prefix = pxelinux_prefix; ++ pxelinux_cfgfile = pxelinux_prefix = NULL; + + return 0; + } +@@ -342,14 +352,14 @@ static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_str + + if (opt_struct -> flag[DHCP_TFTP_SERVER]) { + options[0] = DHCP_TFTP_SERVER; +- options[1] = strlen((char *) opt_struct -> tftp_server) + 1; ++ options[1] = strlen(opt_struct->tftp_server) + 1; + memcpy(options + 2, opt_struct -> tftp_server, options[1]); + options += options[1] + 2; + } + + if (opt_struct -> flag[DHCP_BOOTFILE]) { + options[0] = DHCP_BOOTFILE; +- options[1] = strlen((char *) opt_struct -> bootfile) + 1; ++ options[1] = strlen(opt_struct->bootfile) + 1; + memcpy(options + 2, opt_struct -> bootfile, options[1]); + options += options[1] + 2; + } +@@ -456,6 +466,26 @@ static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + offset += 4; + break; + ++ case DHCP_PXELINUX_CFGFILE: ++ pxelinux_cfgfile = malloc(opt_field[offset + 1] + 1); ++ if (pxelinux_cfgfile) { ++ memcpy(pxelinux_cfgfile, opt_field + offset + 2, ++ opt_field[offset + 1]); ++ pxelinux_cfgfile[opt_field[offset + 1]] = 0; ++ } ++ offset += 2 + opt_field[offset + 1]; ++ break; ++ ++ case DHCP_PXELINUX_PREFIX: ++ pxelinux_prefix = malloc(opt_field[offset + 1] + 1); ++ if (pxelinux_prefix) { ++ memcpy(pxelinux_prefix, opt_field + offset + 2, ++ opt_field[offset + 1]); ++ pxelinux_prefix[opt_field[offset + 1]] = 0; ++ } ++ offset += 2 + opt_field[offset + 1]; ++ break; ++ + case DHCP_PADOPT : + offset++; + break; +@@ -681,6 +711,9 @@ static void dhcp_send_request(int fd) + opt.request_list[DHCP_ROUTER] = 1; + opt.request_list[DHCP_TFTP_SERVER] = 1; + opt.request_list[DHCP_BOOTFILE] = 1; ++ opt.request_list[DHCP_PXELINUX_CFGFILE] = 1; ++ opt.request_list[DHCP_PXELINUX_PREFIX] = 1; ++ + opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + +@@ -716,7 +749,7 @@ void dhcp_send_release(int fd) + btph -> htype = 1; + btph -> hlen = 6; + btph -> xid = dhcp_xid; +- strcpy((char *) btph -> file, ""); ++ btph -> file[0] = 0; + memcpy(btph -> chaddr, get_mac_address(), 6); + btph -> ciaddr = htonl(dhcp_own_ip); + +@@ -774,13 +807,14 @@ int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) + dhcp_server_ip = htonl(iph -> ip_src); + + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { +- strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, +- sizeof(btph -> sname)); ++ strncpy(dhcp_tftp_name, (char *)btph->sname, ++ sizeof(btph->sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { +- strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); ++ strncpy(dhcp_filename, (char *)btph->file, ++ sizeof(btph->file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + +@@ -845,12 +879,14 @@ int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { +- strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname)); ++ strncpy(dhcp_tftp_name, (char *)btph->sname, ++ sizeof(btph->sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { +- strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); ++ strncpy(dhcp_filename, (char *)btph->file, ++ sizeof(btph->file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + +@@ -883,14 +919,14 @@ int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) + dhcp_server_ip = opt.server_ID; + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (opt.flag[DHCP_TFTP_SERVER]) { +- strcpy((char *) dhcp_tftp_name, (char *) opt.tftp_server); ++ strcpy(dhcp_tftp_name, opt.tftp_server); + } + else { +- strcpy((char *) dhcp_tftp_name, ""); ++ dhcp_filename[0] = 0; + if ((opt.overload != DHCP_OVERLOAD_SNAME && + opt.overload != DHCP_OVERLOAD_BOTH) && + !dhcp_siaddr_ip) { +- strncpy((char *) dhcp_tftp_name, ++ strncpy(dhcp_tftp_name, + (char *) btph->sname, + sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph->sname)] = 0; +@@ -898,14 +934,14 @@ int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) + } + + if (opt.flag[DHCP_BOOTFILE]) { +- strcpy((char *) dhcp_filename, (char *) opt.bootfile); ++ strcpy(dhcp_filename, opt.bootfile); + } + else { +- strcpy((char *) dhcp_filename, ""); ++ dhcp_filename[0] = 0; + if (opt.overload != DHCP_OVERLOAD_FILE && +- opt.overload != DHCP_OVERLOAD_BOTH && +- strlen((char *) btph -> file)) { +- strncpy((char *) dhcp_filename, ++ opt.overload != DHCP_OVERLOAD_BOTH && ++ strlen((char *)btph->file)) { ++ strncpy(dhcp_filename, + (char *) btph->file, + sizeof(btph->file)); + dhcp_filename[sizeof(btph -> file)] = 0; +diff --git a/roms/SLOF/lib/libnet/dhcpv6.c b/roms/SLOF/lib/libnet/dhcpv6.c +index 491d540..e92fc0f 100644 +--- a/roms/SLOF/lib/libnet/dhcpv6.c ++++ b/roms/SLOF/lib/libnet/dhcpv6.c +@@ -176,8 +176,7 @@ static void dhcp6_process_options (uint8_t *option, int32_t option_length) + buffer[option_boot_url->length] = 0; + if (parse_tftp_args(buffer, + (char *)my_fn_ip->server_ip6.addr, +- (char *)my_fn_ip->filename, +- (int)my_fn_ip->fd, ++ my_fn_ip->filename, my_fn_ip->fd, + option_boot_url->length) == -1) + return; + break; +diff --git a/roms/SLOF/lib/libnet/ipv6.c b/roms/SLOF/lib/libnet/ipv6.c +index 62a444e..6c6fb54 100644 +--- a/roms/SLOF/lib/libnet/ipv6.c ++++ b/roms/SLOF/lib/libnet/ipv6.c +@@ -543,7 +543,7 @@ int send_ipv6(int fd, void* buffer, int len) + memset(n, 0, sizeof(struct neighbor)); + memcpy(&(n->ip.addr[0]), &ip_dst, 16); + n->status = NB_PROBE; +- n->times_asked += 1; ++ n->times_asked = 1; + neighbor_add(n); + } + +diff --git a/roms/SLOF/lib/libnet/libnet.code b/roms/SLOF/lib/libnet/libnet.code +index 3602543..419419d 100644 +--- a/roms/SLOF/lib/libnet/libnet.code ++++ b/roms/SLOF/lib/libnet/libnet.code +@@ -4,13 +4,9 @@ + PRIM(NET_X2d_LOAD) + int alen = TOS.n; POP; + char *arg = TOS.a; POP; +- int blocksize = TOS.n; POP; +- int hugeload = TOS.n; POP; +- void *replybuf = TOS.a; POP; + long maxlen = TOS.n; POP; + void *loadaddr = TOS.a; +- TOS.n = netload(loadaddr, maxlen, replybuf, hugeload, blocksize, +- arg, alen); ++ TOS.n = netload(loadaddr, maxlen, arg, alen); + MIRP + + PRIM(NET_X2d_PING) +diff --git a/roms/SLOF/lib/libnet/netapps.h b/roms/SLOF/lib/libnet/netapps.h +index 2fea4a7..6e00466 100644 +--- a/roms/SLOF/lib/libnet/netapps.h ++++ b/roms/SLOF/lib/libnet/netapps.h +@@ -18,8 +18,7 @@ + + struct filename_ip; + +-extern int netload(char *buffer, int len, char *ret_buffer, int huge_load, +- int block_size, char *args_fs, int alen); ++extern int netload(char *buffer, int len, char *args_fs, int alen); + extern int ping(char *args_fs, int alen); + extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip, + unsigned int retries, int flags); +diff --git a/roms/SLOF/lib/libnet/netload.c b/roms/SLOF/lib/libnet/netload.c +index cecb2a0..f7ec341 100644 +--- a/roms/SLOF/lib/libnet/netload.c ++++ b/roms/SLOF/lib/libnet/netload.c +@@ -26,6 +26,7 @@ + #include + #include "args.h" + #include "netapps.h" ++#include "pxelinux.h" + + #define IP_INIT_DEFAULT 5 + #define IP_INIT_NONE 0 +@@ -34,6 +35,7 @@ + #define IP_INIT_DHCPV6_STATELESS 3 + #define IP_INIT_IPV6_MANUAL 4 + ++#define MAX_PKT_SIZE 1720 + #define DEFAULT_BOOT_RETRIES 10 + #define DEFAULT_TFTP_RETRIES 20 + static int ip_version = 4; +@@ -403,100 +405,132 @@ static void seed_rng(uint8_t mac[]) + srand(seed); + } + +-static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len, +- unsigned int retries, int32_t mode, +- int32_t blksize, int ip_vers) ++static int tftp_load(filename_ip_t *fnip, void *buffer, int len, ++ unsigned int retries) + { + tftp_err_t tftp_err; + int rc; + +- rc = tftp(fnip, buffer, len, retries, &tftp_err, mode, blksize, ip_vers); ++ rc = tftp(fnip, buffer, len, retries, &tftp_err); + + if (rc > 0) { + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, + rc / 1024); +- } else if (rc == -1) { +- netload_error(0x3003, "unknown TFTP error"); +- return -103; +- } else if (rc == -2) { +- netload_error(0x3004, "TFTP buffer of %d bytes " +- "is too small for %s", +- len, fnip->filename); +- return -104; +- } else if (rc == -3) { +- netload_error(0x3009, "file not found: %s", +- fnip->filename); +- return -108; +- } else if (rc == -4) { +- netload_error(0x3010, "TFTP access violation"); +- return -109; +- } else if (rc == -5) { +- netload_error(0x3011, "illegal TFTP operation"); +- return -110; +- } else if (rc == -6) { +- netload_error(0x3012, "unknown TFTP transfer ID"); +- return -111; +- } else if (rc == -7) { +- netload_error(0x3013, "no such TFTP user"); +- return -112; +- } else if (rc == -8) { +- netload_error(0x3017, "TFTP blocksize negotiation failed"); +- return -116; +- } else if (rc == -9) { +- netload_error(0x3018, "file exceeds maximum TFTP transfer size"); +- return -117; +- } else if (rc <= -10 && rc >= -15) { +- const char *icmp_err_str; +- switch (rc) { +- case -ICMP_NET_UNREACHABLE - 10: +- icmp_err_str = "net unreachable"; +- break; +- case -ICMP_HOST_UNREACHABLE - 10: +- icmp_err_str = "host unreachable"; +- break; +- case -ICMP_PROTOCOL_UNREACHABLE - 10: +- icmp_err_str = "protocol unreachable"; +- break; +- case -ICMP_PORT_UNREACHABLE - 10: +- icmp_err_str = "port unreachable"; +- break; +- case -ICMP_FRAGMENTATION_NEEDED - 10: +- icmp_err_str = "fragmentation needed and DF set"; +- break; +- case -ICMP_SOURCE_ROUTE_FAILED - 10: +- icmp_err_str = "source route failed"; +- break; +- default: +- icmp_err_str = " UNKNOWN"; +- break; ++ } else { ++ int ecode; ++ const char *errstr = NULL; ++ rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); ++ if (errstr) ++ netload_error(ecode, errstr); ++ } ++ ++ return rc; ++} ++ ++static const char *get_uuid(void) ++{ ++ char *addr; ++ int len; ++ ++ if (SLOF_get_property("/", "system-id", &addr, &len)) ++ return NULL; ++ if (len < 37) { /* This should never happen... */ ++ puts("Warning: UUID property is too short."); ++ return NULL; ++ } ++ ++ return addr; ++} ++ ++#define CFG_BUF_SIZE 2048 ++#define MAX_PL_CFG_ENTRIES 16 ++static int net_pxelinux_load(filename_ip_t *fnip, char *loadbase, ++ int maxloadlen, uint8_t *mac, int retries) ++{ ++ struct pl_cfg_entry entries[MAX_PL_CFG_ENTRIES]; ++ int def, rc, ilen; ++ static char *cfgbuf; ++ ++ cfgbuf = malloc(CFG_BUF_SIZE); ++ if (!cfgbuf) { ++ puts("Not enough memory for pxelinux config file buffer!"); ++ return -1; ++ } ++ ++ rc = pxelinux_load_parse_cfg(fnip, mac, get_uuid(), retries, ++ cfgbuf, CFG_BUF_SIZE, ++ entries, MAX_PL_CFG_ENTRIES, &def); ++ if (rc < 0) ++ goto out_free; ++ if (rc == 0) { ++ puts("No valid entries in pxelinux config file."); ++ rc = -1; ++ goto out_free; ++ } ++ ++ /* Load kernel */ ++ strncpy(fnip->filename, entries[def].kernel, ++ sizeof(fnip->filename) - 1); ++ fnip->filename[sizeof(fnip->filename) - 1] = 0; ++ rc = tftp_load(fnip, loadbase, maxloadlen, retries); ++ if (rc <= 0) ++ goto out_free; ++ ++ /* Load ramdisk */ ++ if (entries[def].initrd) { ++ loadbase += rc; ++ maxloadlen -= rc; ++ if (maxloadlen <= 0) { ++ puts(" Not enough space for loading the initrd!"); ++ rc = -1; ++ goto out_free; ++ } ++ strncpy(fnip->filename, entries[def].initrd, ++ sizeof(fnip->filename) - 1); ++ ilen = tftp_load(fnip, loadbase, maxloadlen, retries); ++ if (ilen < 0) { ++ rc = ilen; ++ goto out_free; + } +- netload_error(0x3005, "ICMP ERROR \"%s\"", icmp_err_str); +- return -105; +- } else if (rc == -40) { +- netload_error(0x3014, "TFTP error occurred after " +- "%d bad packets received", +- tftp_err.bad_tftp_packets); +- return -113; +- } else if (rc == -41) { +- netload_error(0x3015, "TFTP error occurred after " +- "missing %d responses", +- tftp_err.no_packets); +- return -114; +- } else if (rc == -42) { +- netload_error(0x3016, "TFTP error missing block %d, " +- "expected block was %d", +- tftp_err.blocks_missed, +- tftp_err.blocks_received); +- return -115; ++ /* The ELF loader will move the kernel to some spot in low mem ++ * later, thus move the initrd to the end of the RAM instead */ ++ memmove(loadbase + maxloadlen - ilen, loadbase, ilen); ++ /* Encode the initrd information in the device tree */ ++ SLOF_set_chosen_int("linux,initrd-start", ++ (long)loadbase + maxloadlen - ilen); ++ SLOF_set_chosen_int("linux,initrd-end", ++ (long)loadbase + maxloadlen); + } + ++ if (entries[def].append) { ++ SLOF_set_chosen_bytes("bootargs", entries[def].append, ++ strlen(entries[def].append) + 1); ++ } ++ ++out_free: ++ free(cfgbuf); + return rc; + } + +-int netload(char *buffer, int len, char *ret_buffer, int huge_load, +- int block_size, char *args_fs, int alen) ++static void encode_response(char *pkt_buffer, size_t size, int ip_init) + { +- int rc; ++ switch(ip_init) { ++ case IP_INIT_BOOTP: ++ SLOF_encode_bootp_response(pkt_buffer, size); ++ break; ++ case IP_INIT_DHCP: ++ case IP_INIT_DHCPV6_STATELESS: ++ case IP_INIT_DEFAULT: ++ SLOF_encode_dhcp_response(pkt_buffer, size); ++ break; ++ default: ++ break; ++ } ++} ++ ++int netload(char *buffer, int len, char *args_fs, int alen) ++{ ++ int rc, filename_len; + filename_ip_t fn_ip; + int fd_device; + obp_tftp_args_t obp_tftp_args; +@@ -506,6 +540,14 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + uint8_t own_mac[6]; ++ char *pkt_buffer; ++ ++ pkt_buffer = SLOF_alloc_mem(MAX_PKT_SIZE); ++ if (!pkt_buffer) { ++ puts("ERROR: Unable to allocate memory"); ++ return -1; ++ } ++ memset(pkt_buffer, 0, MAX_PKT_SIZE); + + puts("\n Initializing NIC"); + memset(&fn_ip, 0, sizeof(filename_ip_t)); +@@ -533,11 +575,13 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + + if (fd_device == -1) { + netload_error(0x3000, "Could not read MAC address"); +- return -100; ++ rc = -100; ++ goto err_out; + } + else if (fd_device == -2) { + netload_error(0x3006, "Could not initialize network device"); +- return -101; ++ rc = -101; ++ goto err_out; + } + + fn_ip.fd = fd_device; +@@ -556,7 +600,8 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + char args[256]; + if (alen > sizeof(args) - 1) { + puts("ERROR: Parameter string is too long."); +- return -7; ++ rc = -7; ++ goto err_out; + } + /* Convert forth string into NUL-terminated C-string */ + strncpy(args, args_fs, alen); +@@ -615,13 +660,13 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + else { + memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4); + } +- rc = bootp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries); ++ rc = bootp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries); + break; + case IP_INIT_DHCP: +- rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4); ++ rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4); + break; + case IP_INIT_DHCPV6_STATELESS: +- rc = dhcp(ret_buffer, &fn_ip, ++ rc = dhcp(pkt_buffer, &fn_ip, + obp_tftp_args.bootp_retries, F_IPV6); + break; + case IP_INIT_IPV6_MANUAL: +@@ -637,7 +682,7 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + } + break; + case IP_INIT_DEFAULT: +- rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0); ++ rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0); + break; + case IP_INIT_NONE: + default: +@@ -668,7 +713,8 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + if (rc == -1) { + netload_error(0x3001, "Could not get IP address"); + close(fn_ip.fd); +- return -101; ++ rc = -101; ++ goto err_out; + } + + if (ip_version == 4) { +@@ -689,12 +735,14 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + close(fn_ip.fd); +- return -102; ++ rc = -102; ++ goto err_out; + } + if (rc == -4 || rc == -3) { + netload_error(0x3008, "Can't obtain TFTP server IP address"); + close(fn_ip.fd); +- return -107; ++ rc = -107; ++ goto err_out; + } + + /*********************************************************** +@@ -704,10 +752,12 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + ***********************************************************/ + + if (obp_tftp_args.filename[0] != 0) { +- strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1); ++ strncpy(fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1); + fn_ip.filename[sizeof(fn_ip.filename)-1] = 0; + } + ++ fn_ip.ip_version = ip_version; ++ + if (ip_version == 4) { + printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", + fn_ip.filename, +@@ -723,14 +773,30 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load, + } + + /* Do the TFTP load and print error message if necessary */ +- rc = tftp_load(&fn_ip, (unsigned char *)buffer, len, +- obp_tftp_args.tftp_retries, huge_load, +- block_size, ip_version); ++ rc = 0; ++ filename_len = strlen(fn_ip.filename); ++ if (filename_len > 0 && fn_ip.filename[filename_len - 1] != '/' && ++ !fn_ip.pl_cfgfile) { ++ rc = tftp_load(&fn_ip, buffer, len, obp_tftp_args.tftp_retries); ++ } ++ ++ if (rc <= 0 && !obp_tftp_args.filename[0] && ++ (!filename_len || fn_ip.filename[filename_len - 1] == '/')) { ++ rc = net_pxelinux_load(&fn_ip, buffer, len, own_mac, ++ obp_tftp_args.tftp_retries); ++ } + + if (obp_tftp_args.ip_init == IP_INIT_DHCP) + dhcp_send_release(fn_ip.fd); + + close(fn_ip.fd); + ++ if (rc >= 0) { ++ encode_response(pkt_buffer, MAX_PKT_SIZE, obp_tftp_args.ip_init); ++ } ++ err_out: ++ SLOF_free_mem(pkt_buffer, MAX_PKT_SIZE); ++ free(fn_ip.pl_cfgfile); ++ free(fn_ip.pl_prefix); + return rc; + } +diff --git a/roms/SLOF/lib/libnet/ping.c b/roms/SLOF/lib/libnet/ping.c +index edad5eb..051269f 100644 +--- a/roms/SLOF/lib/libnet/ping.c ++++ b/roms/SLOF/lib/libnet/ping.c +@@ -115,6 +115,7 @@ int ping(char *args_fs, int alen) + uint8_t own_mac[6]; + uint32_t netmask; + char args[256]; ++ int ret = -1; + + memset(&ping_args, 0, sizeof(struct ping_args)); + +@@ -164,8 +165,7 @@ int ping(char *args_fs, int alen) + + if (arp_failed == -1) { + printf("\n DHCP: Could not get ip address\n"); +- close(fn_ip.fd); +- return -1; ++ goto free_out; + } + + } else { +@@ -210,12 +210,16 @@ int ping(char *args_fs, int alen) + receive_ether(fd_device); + if(pong_ipv4() == 0) { + printf("success\n"); +- close(fn_ip.fd); +- return 0; ++ ret = 0; ++ goto free_out; + } + } + + printf("failed\n"); ++free_out: ++ free(fn_ip.pl_cfgfile); ++ free(fn_ip.pl_prefix); + close(fn_ip.fd); +- return -1; ++ ++ return ret; + } +diff --git a/roms/SLOF/lib/libnet/pxelinux.c b/roms/SLOF/lib/libnet/pxelinux.c +new file mode 100644 +index 0000000..c4ac5d5 +--- /dev/null ++++ b/roms/SLOF/lib/libnet/pxelinux.c +@@ -0,0 +1,251 @@ ++/***************************************************************************** ++ * pxelinux.cfg-style config file support. ++ * ++ * See https://www.syslinux.org/wiki/index.php?title=PXELINUX for information ++ * about the pxelinux config file layout. ++ * ++ * Copyright 2018 Red Hat, Inc. ++ * ++ * This program and the accompanying materials are made available under the ++ * terms of the BSD License which accompanies this distribution, and is ++ * available at http://www.opensource.org/licenses/bsd-license.php ++ * ++ * Contributors: ++ * Thomas Huth, Red Hat Inc. - initial implementation ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include "tftp.h" ++#include "pxelinux.h" ++ ++/** ++ * Call tftp() and report errors (excet "file-not-found" errors) ++ */ ++static int pxelinux_tftp_load(filename_ip_t *fnip, void *buffer, int len, ++ int retries) ++{ ++ tftp_err_t tftp_err; ++ int rc, ecode; ++ ++ rc = tftp(fnip, buffer, len, retries, &tftp_err); ++ ++ if (rc > 0) { ++ printf("\r TFTP: Received %s (%d bytes)\n", ++ fnip->filename, rc); ++ } else if (rc == -3) { ++ /* Ignore file-not-found (since we are probing the files) ++ * and simply erase the "Receiving data: 0 KBytes" string */ ++ printf("\r \r"); ++ } else { ++ const char *errstr = NULL; ++ rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); ++ if (errstr) ++ printf("\r TFTP error: %s\n", errstr); ++ } ++ ++ return rc; ++} ++ ++/** ++ * Try to load a pxelinux.cfg file by probing the possible file names. ++ * Note that this function will overwrite filename_ip_t->filename. ++ */ ++static int pxelinux_load_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid, ++ int retries, char *cfgbuf, int cfgbufsize) ++{ ++ int rc, idx; ++ char *baseptr; ++ ++ /* Did we get a usable base directory via DHCP? */ ++ if (fn_ip->pl_prefix) { ++ idx = strlen(fn_ip->pl_prefix); ++ /* Do we have enough space left to store a UUID file name? */ ++ if (idx > sizeof(fn_ip->filename) - 36) { ++ puts("Error: pxelinux prefix is too long!"); ++ return -1; ++ } ++ strcpy(fn_ip->filename, fn_ip->pl_prefix); ++ baseptr = &fn_ip->filename[idx]; ++ } else { ++ /* Try to get a usable base directory from the DHCP bootfile name */ ++ baseptr = strrchr(fn_ip->filename, '/'); ++ if (!baseptr) ++ baseptr = fn_ip->filename; ++ else ++ ++baseptr; ++ /* Check that we've got enough space to store "pxelinux.cfg/" ++ * and the UUID (which is the longest file name) there */ ++ if (baseptr - fn_ip->filename > sizeof(fn_ip->filename) - 50) { ++ puts("Error: The bootfile string is too long for " ++ "deriving the pxelinux.cfg file name from it."); ++ return -1; ++ } ++ strcpy(baseptr, "pxelinux.cfg/"); ++ baseptr += strlen(baseptr); ++ } ++ ++ puts("Trying pxelinux.cfg files..."); ++ ++ /* Try to load config file according to file name in DHCP option 209 */ ++ if (fn_ip->pl_cfgfile) { ++ if (strlen(fn_ip->pl_cfgfile) + strlen(fn_ip->filename) ++ > sizeof(fn_ip->filename)) { ++ puts("Error: pxelinux.cfg prefix + filename too long!"); ++ return -1; ++ } ++ strcpy(baseptr, fn_ip->pl_cfgfile); ++ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries); ++ if (rc > 0) { ++ return rc; ++ } ++ } ++ ++ /* Try to load config file with name based on the VM UUID */ ++ if (uuid) { ++ strcpy(baseptr, uuid); ++ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries); ++ if (rc > 0) { ++ return rc; ++ } ++ } ++ ++ /* Look for config file with MAC address in its name */ ++ sprintf(baseptr, "01-%02x-%02x-%02x-%02x-%02x-%02x", ++ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); ++ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries); ++ if (rc > 0) { ++ return rc; ++ } ++ ++ /* Look for config file with IP address in its name */ ++ if (fn_ip->ip_version == 4) { ++ sprintf(baseptr, "%02X%02X%02X%02X", ++ (fn_ip->own_ip >> 24) & 0xff, ++ (fn_ip->own_ip >> 16) & 0xff, ++ (fn_ip->own_ip >> 8) & 0xff, ++ fn_ip->own_ip & 0xff); ++ for (idx = 0; idx <= 7; idx++) { ++ baseptr[8 - idx] = 0; ++ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, ++ retries); ++ if (rc > 0) { ++ return rc; ++ } ++ } ++ } ++ ++ /* Try "default" config file */ ++ strcpy(baseptr, "default"); ++ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries); ++ ++ return rc; ++} ++ ++/** ++ * Parse a pxelinux-style configuration file. ++ * The discovered entries are filled into the "struct pl_cfg_entry entries[]" ++ * array. Note that the callers must keep the cfg buffer valid as long as ++ * they wish to access the "struct pl_cfg_entry" entries, since the pointers ++ * in entries point to the original location in the cfg buffer area. The cfg ++ * buffer is altered for this, too, e.g. terminating NUL-characters are put ++ * into the right locations. ++ * @param cfg Pointer to the buffer with contents of the config file. ++ * The caller must make sure that it is NUL-terminated. ++ * @param cfgsize Size of the cfg data (including the terminating NUL) ++ * @param entries Pointer to array where the results should be put into ++ * @param max_entries Number of available slots in the entries array ++ * @param def_ent Used to return the index of the default entry ++ * @return Number of valid entries ++ */ ++int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries, ++ int max_entries, int *def_ent) ++{ ++ int num_entries = 0; ++ char *ptr = cfg, *nextptr, *eol, *arg; ++ char *defaultlabel = NULL; ++ ++ *def_ent = 0; ++ ++ while (ptr < cfg + cfgsize && num_entries < max_entries) { ++ eol = strchr(ptr, '\n'); ++ if (!eol) { ++ eol = cfg + cfgsize - 1; ++ } ++ nextptr = eol + 1; ++ do { ++ *eol-- = '\0'; /* Remove spaces, tabs and returns */ ++ } while (eol >= ptr && ++ (*eol == '\r' || *eol == ' ' || *eol == '\t')); ++ while (*ptr == ' ' || *ptr == '\t') { ++ ptr++; ++ } ++ if (*ptr == 0 || *ptr == '#') { ++ goto nextline; /* Ignore comments and empty lines */ ++ } ++ arg = strchr(ptr, ' '); /* Search space between cmnd and arg */ ++ if (!arg) { ++ arg = strchr(ptr, '\t'); ++ } ++ if (!arg) { ++ printf("Failed to parse this line:\n %s\n", ptr); ++ goto nextline; ++ } ++ *arg++ = 0; ++ while (*arg == ' ' || *arg == '\t') { ++ arg++; ++ } ++ if (!strcasecmp("default", ptr)) { ++ defaultlabel = arg; ++ } else if (!strcasecmp("label", ptr)) { ++ entries[num_entries].label = arg; ++ if (defaultlabel && !strcmp(arg, defaultlabel)) { ++ *def_ent = num_entries; ++ } ++ num_entries++; ++ } else if (!strcasecmp("kernel", ptr) && num_entries) { ++ entries[num_entries - 1].kernel = arg; ++ } else if (!strcasecmp("initrd", ptr) && num_entries) { ++ entries[num_entries - 1].initrd = arg; ++ } else if (!strcasecmp("append", ptr) && num_entries) { ++ entries[num_entries - 1].append = arg; ++ } else { ++ printf("Command '%s' is not supported.\n", ptr); ++ } ++nextline: ++ ptr = nextptr; ++ } ++ ++ return num_entries; ++} ++ ++/** ++ * Try to load and parse a pxelinux-style configuration file. ++ * @param fn_ip must contain server and client IP information ++ * @param mac MAC address which should be used for probing ++ * @param uuid UUID which should be used for probing (can be NULL) ++ * @param retries Amount of TFTP retries before giving up ++ * @param cfgbuf Pointer to the buffer where config file should be loaded ++ * @param cfgsize Size of the cfgbuf buffer ++ * @param entries Pointer to array where the results should be put into ++ * @param max_entries Number of available slots in the entries array ++ * @param def_ent Used to return the index of the default entry ++ * @return Number of valid entries ++ */ ++int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid, ++ int retries, char *cfgbuf, int cfgsize, ++ struct pl_cfg_entry *entries, int max_entries, ++ int *def_ent) ++{ ++ int rc; ++ ++ rc = pxelinux_load_cfg(fn_ip, mac, uuid, retries, cfgbuf, cfgsize - 1); ++ if (rc < 0) ++ return rc; ++ assert(rc < cfgsize); ++ ++ cfgbuf[rc++] = '\0'; /* Make sure it is NUL-terminated */ ++ ++ return pxelinux_parse_cfg(cfgbuf, rc, entries, max_entries, def_ent); ++} +diff --git a/roms/SLOF/lib/libnet/pxelinux.h b/roms/SLOF/lib/libnet/pxelinux.h +new file mode 100644 +index 0000000..0514d0d +--- /dev/null ++++ b/roms/SLOF/lib/libnet/pxelinux.h +@@ -0,0 +1,33 @@ ++/***************************************************************************** ++ * Definitions for pxelinux-style config file support ++ * ++ * Copyright 2018 Red Hat, Inc. ++ * ++ * This program and the accompanying materials ++ * are made available under the terms of the BSD License ++ * which accompanies this distribution, and is available at ++ * http://www.opensource.org/licenses/bsd-license.php ++ * ++ * Contributors: ++ * Thomas Huth, Red Hat Inc. - initial implementation ++ *****************************************************************************/ ++ ++#ifndef LIBNET_PXELINUX_H ++#define LIBNET_PXELINUX_H ++ ++/* This structure holds the data from one pxelinux.cfg file entry */ ++struct pl_cfg_entry { ++ const char *label; ++ const char *kernel; ++ const char *initrd; ++ const char *append; ++}; ++ ++int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries, ++ int max_entries, int *def_ent); ++int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid, ++ int retries, char *cfgbuf, int cfgsize, ++ struct pl_cfg_entry *entries, ++ int max_entries, int *def_ent); ++ ++#endif +diff --git a/roms/SLOF/lib/libnet/tftp.c b/roms/SLOF/lib/libnet/tftp.c +index cda8bf3..9a5817a 100644 +--- a/roms/SLOF/lib/libnet/tftp.c ++++ b/roms/SLOF/lib/libnet/tftp.c +@@ -97,7 +97,7 @@ static void send_rrq(int fd) + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; +- unsigned char mode[] = "octet"; ++ const char mode[] = "octet"; + char *ptr = NULL; + struct iphdr *ip = NULL; + struct ip6hdr *ip6 = NULL; +@@ -110,7 +110,7 @@ static void send_rrq(int fd) + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) +- + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ++ + strlen(fn_ip->filename) + strlen(mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); +@@ -119,7 +119,7 @@ static void send_rrq(int fd) + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) +- + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ++ + strlen(fn_ip->filename) + strlen(mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), +@@ -127,7 +127,7 @@ static void send_rrq(int fd) + + } + udp_len = htons(sizeof(struct udphdr) +- + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 ++ + strlen(fn_ip->filename) + strlen(mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69)); + +@@ -135,12 +135,12 @@ static void send_rrq(int fd) + tftp->th_opcode = htons(RRQ); + + ptr = (char *) &tftp->th_data; +- memcpy(ptr, fn_ip->filename, strlen((char *) fn_ip->filename) + 1); ++ memcpy(ptr, fn_ip->filename, strlen(fn_ip->filename) + 1); + +- ptr += strlen((char *) fn_ip->filename) + 1; +- memcpy(ptr, mode, strlen((char *) mode) + 1); ++ ptr += strlen(fn_ip->filename) + 1; ++ memcpy(ptr, mode, strlen(mode) + 1); + +- ptr += strlen((char *) mode) + 1; ++ ptr += strlen(mode) + 1; + memcpy(ptr, "blksize", strlen("blksize") + 1); + + ptr += strlen("blksize") + 1; +@@ -497,20 +497,16 @@ void handle_tftp_dun(uint8_t err_code) + * @param _len size of destination buffer + * @param _retries max number of retries + * @param _tftp_err contains info about TFTP-errors (e.g. lost packets) +- * @param _mode NON ZERO - multicast, ZERO - unicast +- * @param _blocksize blocksize for DATA-packets + * @return ZERO - error condition occurs + * NON ZERO - size of received file + */ + int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, +- unsigned int _retries, tftp_err_t * _tftp_err, +- int32_t _mode, int32_t _blocksize, int _ip_version) ++ unsigned int _retries, tftp_err_t * _tftp_err) + { + retries = _retries; + fn_ip = _fn_ip; + len = _len; +- huge_load = _mode; +- ip_version = _ip_version; ++ ip_version = _fn_ip->ip_version; + tftp_errno = 0; + tftp_err = _tftp_err; + tftp_err->bad_tftp_packets = 0; +@@ -523,17 +519,14 @@ int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, + port_number = -1; + progress_first = -1; + progress_last_bytes = 0; ++ huge_load = 1; + + /* Default blocksize must be 512 for TFTP servers + * which do not support the RRQ blocksize option */ + blocksize = 512; + + /* Preferred blocksize - used as option for the read request */ +- if (_blocksize < 8) +- _blocksize = 8; +- else if (_blocksize > MAX_BLOCKSIZE) +- _blocksize = MAX_BLOCKSIZE; +- sprintf(blocksize_str, "%d", _blocksize); ++ sprintf(blocksize_str, "%d", MAX_BLOCKSIZE); + + printf(" Receiving data: "); + print_progress(-1, 0); +@@ -697,3 +690,104 @@ int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, + return 0; + } + } ++ ++int tftp_get_error_info(filename_ip_t *fnip, tftp_err_t *tftperr, int rc, ++ const char **errstr, int *ecode) ++{ ++ static char estrbuf[80]; ++ ++ if (rc == -1) { ++ *ecode = 0x3003; ++ *errstr = "unknown TFTP error"; ++ return -103; ++ } else if (rc == -2) { ++ *ecode = 0x3004; ++ snprintf(estrbuf, sizeof(estrbuf), ++ "TFTP buffer of %d bytes is too small for %s", len, ++ fnip->filename); ++ *errstr = estrbuf; ++ return -104; ++ } else if (rc == -3) { ++ *ecode = 0x3009; ++ snprintf(estrbuf, sizeof(estrbuf), "file not found: %s", ++ fnip->filename); ++ *errstr = estrbuf; ++ return -108; ++ } else if (rc == -4) { ++ *ecode = 0x3010; ++ *errstr = "TFTP access violation"; ++ return -109; ++ } else if (rc == -5) { ++ *ecode = 0x3011; ++ *errstr = "illegal TFTP operation"; ++ return -110; ++ } else if (rc == -6) { ++ *ecode = 0x3012; ++ *errstr = "unknown TFTP transfer ID"; ++ return -111; ++ } else if (rc == -7) { ++ *ecode = 0x3013; ++ *errstr = "no such TFTP user"; ++ return -112; ++ } else if (rc == -8) { ++ *ecode = 0x3017; ++ *errstr = "TFTP blocksize negotiation failed"; ++ return -116; ++ } else if (rc == -9) { ++ *ecode = 0x3018; ++ *errstr = "file exceeds maximum TFTP transfer size"; ++ return -117; ++ } else if (rc <= -10 && rc >= -15) { ++ const char *icmp_err_str; ++ switch (rc) { ++ case -ICMP_NET_UNREACHABLE - 10: ++ icmp_err_str = "net unreachable"; ++ break; ++ case -ICMP_HOST_UNREACHABLE - 10: ++ icmp_err_str = "host unreachable"; ++ break; ++ case -ICMP_PROTOCOL_UNREACHABLE - 10: ++ icmp_err_str = "protocol unreachable"; ++ break; ++ case -ICMP_PORT_UNREACHABLE - 10: ++ icmp_err_str = "port unreachable"; ++ break; ++ case -ICMP_FRAGMENTATION_NEEDED - 10: ++ icmp_err_str = "fragmentation needed and DF set"; ++ break; ++ case -ICMP_SOURCE_ROUTE_FAILED - 10: ++ icmp_err_str = "source route failed"; ++ break; ++ default: ++ icmp_err_str = "UNKNOWN"; ++ break; ++ } ++ *ecode = 0x3005; ++ sprintf(estrbuf, "ICMP ERROR \"%s\"", icmp_err_str); ++ *errstr = estrbuf; ++ return -105; ++ } else if (rc == -40) { ++ *ecode = 0x3014; ++ sprintf(estrbuf, ++ "TFTP error occurred after %d bad packets received", ++ tftperr->bad_tftp_packets); ++ *errstr = estrbuf; ++ return -113; ++ } else if (rc == -41) { ++ *ecode = 0x3015; ++ sprintf(estrbuf, ++ "TFTP error occurred after missing %d responses", ++ tftperr->no_packets); ++ *errstr = estrbuf; ++ return -114; ++ } else if (rc == -42) { ++ *ecode = 0x3016; ++ sprintf(estrbuf, ++ "TFTP error missing block %d, expected block was %d", ++ tftperr->blocks_missed, tftperr->blocks_received); ++ *errstr = estrbuf; ++ return -115; ++ } ++ ++ return rc; ++} +diff --git a/roms/SLOF/lib/libnet/tftp.h b/roms/SLOF/lib/libnet/tftp.h +index b1dbc21..d2c0919 100644 +--- a/roms/SLOF/lib/libnet/tftp.h ++++ b/roms/SLOF/lib/libnet/tftp.h +@@ -28,9 +28,12 @@ struct filename_ip { + uint32_t server_ip; + ip6_addr_t server_ip6; + ip6_addr_t dns_ip6; +- int8_t filename[256]; +- int fd; +-} __attribute__ ((packed)); ++ char filename[256]; ++ char *pl_cfgfile; /* For PXELINUX DHCPv4 option 209. Must be free()ed */ ++ char *pl_prefix; /* For PXELINUX DHCPv4 option 210. Must be free()ed */ ++ int fd; ++ int ip_version; ++}; + typedef struct filename_ip filename_ip_t; + + typedef struct { +@@ -40,11 +43,12 @@ typedef struct { + uint32_t blocks_received; + } tftp_err_t; + +-int tftp(filename_ip_t *, unsigned char *, int, unsigned int, +- tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version); +- ++int tftp(filename_ip_t *fnip, unsigned char *buf, int len, ++ unsigned int retries, tftp_err_t *err); + int32_t handle_tftp(int fd, uint8_t *, int32_t); + void handle_tftp_dun(uint8_t err_code); + int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, int len); ++int tftp_get_error_info(filename_ip_t *fnip, tftp_err_t *tftperr, int rc, ++ const char **errstr, int *ecode); + + #endif +-- +2.17.1 +