aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-07-13 01:44:51 -0400
committerStefan Hajnoczi <stefanha@redhat.com>2025-07-13 01:44:51 -0400
commit3adbf0bb8a78f17a1e9390b59e51eb1a47d8ac98 (patch)
treea5dbe172f85a5e3b9ec908729bde174a1275b6f9
parent43ec52b4c875f23ab041dd3de906cfacbd0d1a9d (diff)
parent693b3039d77195953e70f008991c80bf9c5b9691 (diff)
downloadqemu-3adbf0bb8a78f17a1e9390b59e51eb1a47d8ac98.zip
qemu-3adbf0bb8a78f17a1e9390b59e51eb1a47d8ac98.tar.gz
qemu-3adbf0bb8a78f17a1e9390b59e51eb1a47d8ac98.tar.bz2
Merge tag 'pull-request-2025-07-11' of https://gitlab.com/thuth/qemu into staging
* s390x: Allow to select different entries when booting via pxelinux.cfg * Link s390-ccw.img statically * Fix broken bamboo functional test * s390x code cleanups and refactorings # -----BEGIN PGP SIGNATURE----- # # iQJFBAABCgAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmhw2i0RHHRodXRoQHJl # ZGhhdC5jb20ACgkQLtnXdP5wLbUGtA//XVr5t2/iH+zFdaHHFglMtYkqwyYspa/O # zGPgcIZptQrzlbR+GFJwd4ae1HWb60E1YDyC7M1iWGQXeMNrDgeJJjUQfhB7693Y # CPT1FCWaqXdrTHQJhf5+EGJZopwY1K4EHs+bMxCpU3ManD+MKuXzCgOMzZATnPUZ # EcvOrzDBfEFEzQn5COUi5FF5Ds4DpOqQY1g1tpG92hQwWeAgdPPXSYlakG64Hm8C # Km6BzAcylrRiHdORk3GeMJ1cPQ3vCjMrjTd87ra/xuH+DvPeyZ31cRIWIP1dn44x # eog5dWo7pNmwfU50c4w/6dTSqwHG/bD/2ZPJH2nnJDLK02WeguantPN43fdoPU0c # NEMldVE5GAqEr7Sbd5YIw9lBqrROIDfeUAxje4VZa1gSY4N/GYMGEZaM5vqYJJTP # 0ndWP83QdamWuE0eOYMA+4oZiPpW79+Igv/PV13lsm9JgvO0WQisPFxE0cZqMTQp # +wgbQ69rpyMiQxpusiL/6LA3khDyC8Z8g7cmjBfpqgwmVAZp7ly+GLk+ctG0zsjE # hB99hkujZVkBZQLnVs0C/pXn1NdJ0wEupiHOSsVlQtqzNHlbweRJoxuGSp4Rl0Et # 0DnTr3YHB6bdvRazaKzlkBHLLAXKEw0/xaRWGbE4tftZIrkOEeE0LMLLaLWLNKhX # rqRoxq00OPs= # =SOH3 # -----END PGP SIGNATURE----- # gpg: Signature made Fri 11 Jul 2025 05:32:29 EDT # gpg: using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5 # gpg: issuer "thuth@redhat.com" # gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full] # gpg: aka "Thomas Huth <thuth@redhat.com>" [full] # gpg: aka "Thomas Huth <huth@tuxfamily.org>" [full] # gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown] # Primary key fingerprint: 27B8 8847 EEE0 2501 18F3 EAB9 2ED9 D774 FE70 2DB5 * tag 'pull-request-2025-07-11' of https://gitlab.com/thuth/qemu: target/s390x: Have s390_cpu_halt() not return anything target/s390x: Expose s390_count_running_cpus() method target/s390x: Remove unused s390_cpu_[un]halt() user stubs tests/functional/test_ppc_bamboo: Replace broken link with working assets tests/functional: Add dependency to the keymap_targets pc-bios: Update the s390 bios images with the pxelinux.cfg loadparm changes pc-bios/s390-ccw: link statically tests/functional: Add a test for s390x pxelinux.cfg network booting pc-bios/s390-ccw: Add a boot menu for booting via pxelinux.cfg pc-bios/s390-ccw: Make get_boot_index() from menu.c global pc-bios/s390-ccw: Allow up to 31 entries for pxelinux.cfg pc-bios/s390-ccw: Allow to select a different pxelinux.cfg entry via loadparm hw/s390x/s390-pci-bus.c: Use g_assert_not_reached() in functions taking an ett target/s390x/tcg: Use vaddr in s390_probe_access() target/s390x/kvm: Use vaddr in find/insert_hw_breakpoint() Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-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 1842c3d..e88ed2c 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 e6aa445..f87d274 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 47240f0..ff60978 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index dc69dd4..a0f24c9 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 84062e9..eeaff78 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 719a547..a9521df 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 6cdce3e..b1dc35c 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 9b380e3..709ccd5 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 3c57c32..5c127da 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 67d9a19..491cc5f 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 a4ba622..56cce2e 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 a03609a..f1acb16 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 050c900..ae5c52d 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 fddcc24..c634ae7 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 0000000..4fc33b8
--- /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()