summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--hw/s390x/s390-pci-bus.c26
-rw-r--r--pc-bios/s390-ccw.imgbin96000 -> 87824 bytes
-rw-r--r--pc-bios/s390-ccw/Makefile2
-rw-r--r--pc-bios/s390-ccw/menu.c6
-rw-r--r--pc-bios/s390-ccw/netmain.c66
-rw-r--r--pc-bios/s390-ccw/s390-ccw.h1
-rw-r--r--target/s390x/cpu-system.c6
-rw-r--r--target/s390x/helper.c4
-rw-r--r--target/s390x/kvm/kvm.c4
-rw-r--r--target/s390x/s390x-internal.h13
-rw-r--r--target/s390x/tcg/mem_helper.c10
-rw-r--r--tests/functional/meson.build3
-rwxr-xr-xtests/functional/test_ppc_bamboo.py34
-rwxr-xr-xtests/functional/test_s390x_pxelinux.py119
15 files changed, 227 insertions, 68 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1842c3dd83..e88ed2c0a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1805,6 +1805,7 @@ F: hw/s390x/ipl.*
 F: pc-bios/s390-ccw/
 F: pc-bios/s390-ccw.img
 F: docs/devel/s390-dasd-ipl.rst
+F: tests/functional/test_s390x_pxelinux.py
 T: git https://github.com/borntraeger/qemu.git s390-next
 L: qemu-s390x@nongnu.org
 
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index e6aa44531f..f87d2748b6 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -384,9 +384,9 @@ static uint64_t get_table_index(uint64_t iova, int8_t ett)
         return calc_sx(iova);
     case ZPCI_ETT_RT:
         return calc_rtx(iova);
+    default:
+        g_assert_not_reached();
     }
-
-    return -1;
 }
 
 static bool entry_isvalid(uint64_t entry, int8_t ett)
@@ -397,22 +397,24 @@ static bool entry_isvalid(uint64_t entry, int8_t ett)
     case ZPCI_ETT_ST:
     case ZPCI_ETT_RT:
         return rt_entry_isvalid(entry);
+    default:
+        g_assert_not_reached();
     }
-
-    return false;
 }
 
 /* Return true if address translation is done */
 static bool translate_iscomplete(uint64_t entry, int8_t ett)
 {
     switch (ett) {
-    case 0:
+    case ZPCI_ETT_ST:
         return (entry & ZPCI_TABLE_FC) ? true : false;
-    case 1:
+    case ZPCI_ETT_RT:
         return false;
+    case ZPCI_ETT_PT:
+        return true;
+    default:
+        g_assert_not_reached();
     }
-
-    return true;
 }
 
 static uint64_t get_frame_size(int8_t ett)
@@ -424,9 +426,9 @@ static uint64_t get_frame_size(int8_t ett)
         return 1ULL << 20;
     case ZPCI_ETT_RT:
         return 1ULL << 31;
+    default:
+        g_assert_not_reached();
     }
-
-    return 0;
 }
 
 static uint64_t get_next_table_origin(uint64_t entry, int8_t ett)
@@ -438,9 +440,9 @@ static uint64_t get_next_table_origin(uint64_t entry, int8_t ett)
         return get_st_pto(entry);
     case ZPCI_ETT_RT:
         return get_rt_sto(entry);
+    default:
+        g_assert_not_reached();
     }
-
-    return 0;
 }
 
 /**
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index 47240f0a74..ff60978d28 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differdiff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index dc69dd484f..a0f24c94a8 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -47,7 +47,7 @@ EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables
 EXTRA_CFLAGS += -msoft-float
 EXTRA_CFLAGS += -std=gnu99
 EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
-EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text
+EXTRA_LDFLAGS += -static-pie -nostdlib -z noexecstack -z text
 
 cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null
 cc-option = if $(call cc-test, $1); then \
diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c
index 84062e94af..eeaff78f87 100644
--- a/pc-bios/s390-ccw/menu.c
+++ b/pc-bios/s390-ccw/menu.c
@@ -159,7 +159,7 @@ static void boot_menu_prompt(bool retry)
     }
 }
 
-static int get_boot_index(bool *valid_entries)
+int menu_get_boot_index(bool *valid_entries)
 {
     int boot_index;
     bool retry = false;
@@ -224,7 +224,7 @@ int menu_get_zipl_boot_index(const char *menu_data)
     }
 
     printf("\n");
-    return get_boot_index(valid_entries);
+    return menu_get_boot_index(valid_entries);
 }
 
 int menu_get_enum_boot_index(bool *valid_entries)
@@ -247,7 +247,7 @@ int menu_get_enum_boot_index(bool *valid_entries)
     }
 
     printf("\n");
-    return get_boot_index(valid_entries);
+    return menu_get_boot_index(valid_entries);
 }
 
 void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index 719a547ada..a9521dff41 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -332,22 +332,64 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip,
     return rc;
 }
 
-#define MAX_PXELINUX_ENTRIES 16
+static int net_boot_menu(int num_ent, int def_ent,
+                         struct pl_cfg_entry *entries)
+{
+    bool valid_entries[MAX_BOOT_ENTRIES] = { false };
+    int idx;
+
+    puts("\ns390-ccw pxelinux.cfg boot menu:\n");
+    printf(" [0] default (%d)\n", def_ent + 1);
+    valid_entries[0] = true;
+
+    for (idx = 1; idx <= num_ent; idx++) {
+        printf(" [%d] %s\n", idx, entries[idx - 1].label);
+        valid_entries[idx] = true;
+    }
+    putchar('\n');
+
+    idx = menu_get_boot_index(valid_entries);
+    putchar('\n');
+
+    return idx;
+}
+
+static int net_select_and_load_kernel(filename_ip_t *fn_ip,
+                                      int num_ent, int selected,
+                                      struct pl_cfg_entry *entries)
+{
+    unsigned int loadparm = get_loadparm_index();
+
+    if (num_ent <= 0) {
+        return -1;
+    }
+
+    if (menu_is_enabled_enum() && num_ent > 1) {
+        loadparm = net_boot_menu(num_ent, selected, entries);
+    }
+
+    IPL_assert(loadparm <= num_ent,
+               "loadparm is set to an entry that is not available in the "
+               "pxelinux.cfg file!");
+
+    if (loadparm > 0) {
+        selected = loadparm - 1;
+    }
+
+    return load_kernel_with_initrd(fn_ip, &entries[selected]);
+}
 
 static int net_try_pxelinux_cfg(filename_ip_t *fn_ip)
 {
-    struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES];
+    struct pl_cfg_entry entries[MAX_BOOT_ENTRIES];
     int num_ent, def_ent = 0;
 
     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) {
-        return load_kernel_with_initrd(fn_ip, &entries[def_ent]);
-    }
+                                      entries, MAX_BOOT_ENTRIES, &def_ent);
 
-    return -1;
+    return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries);
 }
 
 /**
@@ -428,15 +470,13 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip)
          * a magic comment string.
          */
         if (!strncasecmp("# pxelinux", cfgbuf, 10)) {
-            struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES];
+            struct pl_cfg_entry entries[MAX_BOOT_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]);
+                                         MAX_BOOT_ENTRIES, &def_ent);
+            return net_select_and_load_kernel(fn_ip, num_ent, def_ent,
+                                              entries);
         }
     }
 
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6cdce3e5e5..b1dc35cded 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -87,6 +87,7 @@ int menu_get_zipl_boot_index(const char *menu_data);
 bool menu_is_enabled_zipl(void);
 int menu_get_enum_boot_index(bool *valid_entries);
 bool menu_is_enabled_enum(void);
+int menu_get_boot_index(bool *valid_entries);
 
 #define MAX_BOOT_ENTRIES  31
 
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index 9b380e343c..709ccd5299 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -196,7 +196,7 @@ static bool disabled_wait(CPUState *cpu)
                             (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK));
 }
 
-static unsigned s390_count_running_cpus(void)
+unsigned s390_count_running_cpus(void)
 {
     CPUState *cpu;
     int nr_running = 0;
@@ -214,7 +214,7 @@ static unsigned s390_count_running_cpus(void)
     return nr_running;
 }
 
-unsigned int s390_cpu_halt(S390CPU *cpu)
+void s390_cpu_halt(S390CPU *cpu)
 {
     CPUState *cs = CPU(cpu);
     trace_cpu_halt(cs->cpu_index);
@@ -223,8 +223,6 @@ unsigned int s390_cpu_halt(S390CPU *cpu)
         cs->halted = 1;
         cs->exception_index = EXCP_HLT;
     }
-
-    return s390_count_running_cpus();
 }
 
 void s390_cpu_unhalt(S390CPU *cpu)
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 3c57c32e47..5c127da1a6 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -91,7 +91,9 @@ void s390_handle_wait(S390CPU *cpu)
 {
     CPUState *cs = CPU(cpu);
 
-    if (s390_cpu_halt(cpu) == 0) {
+    s390_cpu_halt(cpu);
+
+    if (s390_count_running_cpus() == 0) {
         if (is_special_wait_psw(cpu->env.psw.addr)) {
             qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
         } else {
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 67d9a1977c..491cc5f975 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -889,7 +889,7 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
     return 0;
 }
 
-static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr,
+static struct kvm_hw_breakpoint *find_hw_breakpoint(vaddr addr,
                                                     int len, int type)
 {
     int n;
@@ -904,7 +904,7 @@ static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr,
     return NULL;
 }
 
-static int insert_hw_breakpoint(target_ulong addr, int len, int type)
+static int insert_hw_breakpoint(vaddr addr, int len, int type)
 {
     int size;
 
diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h
index a4ba6227ab..56cce2e7f5 100644
--- a/target/s390x/s390x-internal.h
+++ b/target/s390x/s390x-internal.h
@@ -238,7 +238,8 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
 
 /* cpu.c */
 #ifndef CONFIG_USER_ONLY
-unsigned int s390_cpu_halt(S390CPU *cpu);
+unsigned int s390_count_running_cpus(void);
+void s390_cpu_halt(S390CPU *cpu);
 void s390_cpu_unhalt(S390CPU *cpu);
 void s390_cpu_system_init(Object *obj);
 bool s390_cpu_system_realize(DeviceState *dev, Error **errp);
@@ -246,16 +247,6 @@ void s390_cpu_finalize(Object *obj);
 void s390_cpu_system_class_init(CPUClass *cc);
 void s390_cpu_machine_reset_cb(void *opaque);
 bool s390_cpu_has_work(CPUState *cs);
-
-#else
-static inline unsigned int s390_cpu_halt(S390CPU *cpu)
-{
-    return 0;
-}
-
-static inline void s390_cpu_unhalt(S390CPU *cpu)
-{
-}
 #endif /* CONFIG_USER_ONLY */
 
 
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index a03609a140..f1acb1618f 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -126,8 +126,8 @@ static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr,
 
 /* An access covers at most 4096 bytes and therefore at most two pages. */
 typedef struct S390Access {
-    target_ulong vaddr1;
-    target_ulong vaddr2;
+    vaddr vaddr1;
+    vaddr vaddr2;
     void *haddr1;
     void *haddr2;
     uint16_t size1;
@@ -148,7 +148,7 @@ typedef struct S390Access {
  * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec.
  * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr.
  */
-static inline int s390_probe_access(CPUArchState *env, target_ulong addr,
+static inline int s390_probe_access(CPUArchState *env, vaddr addr,
                                     int size, MMUAccessType access_type,
                                     int mmu_idx, bool nonfault,
                                     void **phost, uintptr_t ra)
@@ -258,7 +258,7 @@ static void access_memset(CPUS390XState *env, S390Access *desta,
 static uint8_t access_get_byte(CPUS390XState *env, S390Access *access,
                                int offset, uintptr_t ra)
 {
-    target_ulong vaddr = access->vaddr1;
+    vaddr vaddr = access->vaddr1;
     void *haddr = access->haddr1;
 
     if (unlikely(offset >= access->size1)) {
@@ -278,7 +278,7 @@ static uint8_t access_get_byte(CPUS390XState *env, S390Access *access,
 static void access_set_byte(CPUS390XState *env, S390Access *access,
                             int offset, uint8_t byte, uintptr_t ra)
 {
-    target_ulong vaddr = access->vaddr1;
+    vaddr vaddr = access->vaddr1;
     void *haddr = access->haddr1;
 
     if (unlikely(offset >= access->size1)) {
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 050c9000b9..ae5c52d79f 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -281,6 +281,7 @@ tests_rx_system_thorough = [
 
 tests_s390x_system_thorough = [
   's390x_ccw_virtio',
+  's390x_pxelinux',
   's390x_replay',
   's390x_topology',
   's390x_tuxrun',
@@ -373,7 +374,7 @@ foreach speed : ['quick', 'thorough']
       target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_' + speed, [])
     endif
 
-    test_deps = roms
+    test_deps = [roms, keymap_targets]
     test_env = environment()
     if have_tools
       test_env.set('QEMU_TEST_QEMU_IMG', meson.global_build_root() / 'qemu-img')
diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py
index fddcc24d0d..c634ae7b4a 100755
--- a/tests/functional/test_ppc_bamboo.py
+++ b/tests/functional/test_ppc_bamboo.py
@@ -16,28 +16,32 @@ class BambooMachine(QemuSystemTest):
 
     timeout = 90
 
-    ASSET_IMAGE = Asset(
-        ('http://landley.net/aboriginal/downloads/binaries/'
-         'system-image-powerpc-440fp.tar.gz'),
-        'c12b58f841c775a0e6df4832a55afe6b74814d1565d08ddeafc1fb949a075c5e')
+    ASSET_KERNEL = Asset(
+        ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/'
+         'buildroot/qemu_ppc_bamboo-2023.11-8-gdcd9f0f6eb-20240105/vmlinux'),
+        'a2e12eb45b73491ac62fc0bbeb68dead0dc5c0f22cf83146558389209b420ad1')
+    ASSET_INITRD = Asset(
+        ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/'
+         'buildroot/qemu_ppc_bamboo-2023.11-8-gdcd9f0f6eb-20240105/rootfs.cpio'),
+        'd2a36bdb8763b389765dc8c29d4904cec2bd001c587f92e85ab9eb10d5ddda54')
 
     def test_ppc_bamboo(self):
         self.set_machine('bamboo')
         self.require_accelerator("tcg")
         self.require_netdev('user')
-        self.archive_extract(self.ASSET_IMAGE)
+
+        kernel = self.ASSET_KERNEL.fetch()
+        initrd = self.ASSET_INITRD.fetch()
+
         self.vm.set_console()
-        self.vm.add_args('-kernel',
-                         self.scratch_file('system-image-powerpc-440fp',
-                                           'linux'),
-                         '-initrd',
-                         self.scratch_file('system-image-powerpc-440fp',
-                                           'rootfs.cpio.gz'),
-                         '-nic', 'user,model=rtl8139,restrict=on')
+        self.vm.add_args('-kernel', kernel,
+                         '-initrd', initrd,
+                         '-nic', 'user,model=virtio-net-pci,restrict=on')
         self.vm.launch()
-        wait_for_console_pattern(self, 'Type exit when done')
-        exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2',
-                                          '10.0.2.2 is alive!')
+        wait_for_console_pattern(self, 'buildroot login:')
+        exec_command_and_wait_for_pattern(self, 'root', '#')
+        exec_command_and_wait_for_pattern(self, 'ping -c1 10.0.2.2',
+                '1 packets transmitted, 1 packets received, 0% packet loss')
         exec_command_and_wait_for_pattern(self, 'halt', 'System Halted')
 
 if __name__ == '__main__':
diff --git a/tests/functional/test_s390x_pxelinux.py b/tests/functional/test_s390x_pxelinux.py
new file mode 100755
index 0000000000..4fc33b8c46
--- /dev/null
+++ b/tests/functional/test_s390x_pxelinux.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Functional test that checks the pxelinux.cfg network booting of a s390x VM
+# (TFTP booting without config file is already tested by the pxe qtest, so
+#  we don't repeat that here).
+
+import os
+import shutil
+
+from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern
+
+
+pxelinux_cfg_contents='''# pxelinux.cfg style config file
+default Debian
+label Nonexisting
+kernel kernel.notavailable
+initrd initrd.notavailable
+label Debian
+kernel kernel.debian
+initrd initrd.debian
+append testoption=teststring
+label Fedora
+kernel kernel.fedora
+'''
+
+class S390PxeLinux(QemuSystemTest):
+
+    ASSET_DEBIAN_KERNEL = Asset(
+        ('https://snapshot.debian.org/archive/debian/'
+         '20201126T092837Z/dists/buster/main/installer-s390x/'
+         '20190702+deb10u6/images/generic/kernel.debian'),
+        'd411d17c39ae7ad38d27534376cbe88b68b403c325739364122c2e6f1537e818')
+
+    ASSET_DEBIAN_INITRD = Asset(
+        ('https://snapshot.debian.org/archive/debian/'
+         '20201126T092837Z/dists/buster/main/installer-s390x/'
+         '20190702+deb10u6/images/generic/initrd.debian'),
+        '836bbd0fe6a5ca81274c28c2b063ea315ce1868660866e9b60180c575fef9fd5')
+
+    ASSET_FEDORA_KERNEL = Asset(
+        ('https://archives.fedoraproject.org/pub/archive'
+         '/fedora-secondary/releases/31/Server/s390x/os'
+         '/images/kernel.img'),
+        '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b')
+
+    def pxelinux_launch(self, pl_name='default', extra_opts=None):
+        self.require_netdev('user')
+        self.set_machine('s390-ccw-virtio')
+
+        debian_kernel = self.ASSET_DEBIAN_KERNEL.fetch()
+        debian_initrd = self.ASSET_DEBIAN_INITRD.fetch()
+        fedora_kernel = self.ASSET_FEDORA_KERNEL.fetch()
+
+        # Prepare a folder for the TFTP "server":
+        tftpdir = self.scratch_file('tftp')
+        shutil.rmtree(tftpdir, ignore_errors=True)   # Remove stale stuff
+        os.mkdir(tftpdir)
+        shutil.copy(debian_kernel, os.path.join(tftpdir, 'kernel.debian'))
+        shutil.copy(debian_initrd, os.path.join(tftpdir, 'initrd.debian'))
+        shutil.copy(fedora_kernel, os.path.join(tftpdir, 'kernel.fedora'))
+
+        pxelinuxdir = self.scratch_file('tftp', 'pxelinux.cfg')
+        os.mkdir(pxelinuxdir)
+
+        cfg_fname = self.scratch_file('tftp', 'pxelinux.cfg', pl_name)
+        with open(cfg_fname, 'w', encoding='utf-8') as f:
+            f.write(pxelinux_cfg_contents)
+
+        virtio_net_dev = 'virtio-net-ccw,netdev=n1,bootindex=1'
+        if extra_opts:
+                virtio_net_dev += ',' + extra_opts
+
+        self.vm.add_args('-m', '384',
+                         '-netdev', f'user,id=n1,tftp={tftpdir}',
+                         '-device', virtio_net_dev)
+        self.vm.set_console()
+        self.vm.launch()
+
+
+    def test_default(self):
+        self.pxelinux_launch()
+        # The kernel prints its arguments to the console, so we can use
+        # this to check whether the kernel parameters are correctly handled:
+        wait_for_console_pattern(self, 'testoption=teststring')
+        # Now also check that we've successfully loaded the initrd:
+        wait_for_console_pattern(self, 'Unpacking initramfs...')
+        wait_for_console_pattern(self, 'Run /init as init process')
+
+    def test_mac(self):
+        self.pxelinux_launch(pl_name='01-02-ca-fe-ba-be-42',
+                             extra_opts='mac=02:ca:fe:ba:be:42,loadparm=3')
+        wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x')
+
+    def test_uuid(self):
+        # Also add a non-bootable disk to check the fallback to network boot:
+        self.vm.add_args('-blockdev', 'null-co,size=65536,node-name=d1',
+                         '-device', 'virtio-blk,drive=d1,bootindex=0,loadparm=1',
+                         '-uuid', '550e8400-e29b-11d4-a716-446655441234')
+        self.pxelinux_launch(pl_name='550e8400-e29b-11d4-a716-446655441234')
+        wait_for_console_pattern(self, 'Debian 4.19.146-1 (2020-09-17)')
+
+    def test_ip(self):
+        self.vm.add_args('-M', 'loadparm=3')
+        self.pxelinux_launch(pl_name='0A00020F')
+        wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x')
+
+    def test_menu(self):
+        self.vm.add_args('-boot', 'menu=on,splash-time=10')
+        self.pxelinux_launch(pl_name='0A00')
+        wait_for_console_pattern(self, '[1] Nonexisting')
+        wait_for_console_pattern(self, '[2] Debian')
+        wait_for_console_pattern(self, '[3] Fedora')
+        wait_for_console_pattern(self, 'Debian 4.19.146-1 (2020-09-17)')
+
+
+if __name__ == '__main__':
+    QemuSystemTest.main()