From 5340eb072ffc36ccd33c67dcea0d5c939b492e98 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:07 -0500 Subject: [PATCH 01/27] s390-ccw: refactor boot map table code Some ECKD bootmap code was using structs designed for SCSI. Even though this works, it confuses readability. Add a new BootMapTable struct to assist with readability in bootmap entry code. Also: - replace ScsiMbr in ECKD code with appropriate structs - fix read_block messages to reflect BootMapTable - fixup ipl_scsi to use BootMapTable (referred to as Program Table) - defined value for maximum table entries Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 60 +++++++++++++++++--------------------- pc-bios/s390-ccw/bootmap.h | 11 ++++++- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 67a6123ed4..a4eaf24c8d 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -182,24 +182,24 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) return block_nr; } -static void run_eckd_boot_script(block_number_t mbr_block_nr) +static void run_eckd_boot_script(block_number_t bmt_block_nr) { int i; unsigned int loadparm = get_loadparm_index(); block_number_t block_nr; uint64_t address; - ScsiMbr *bte = (void *)sec; /* Eckd bootmap table entry */ + BootMapTable *bmt = (void *)sec; BootMapScript *bms = (void *)sec; debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < 31, "loadparm value greater than" + IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(mbr_block_nr, sec, "Cannot read MBR"); + read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); - block_nr = eckd_block_num((void *)&(bte->blockptr[loadparm])); - IPL_assert(block_nr != -1, "No Boot Map"); + block_nr = eckd_block_num(&bmt->entry[loadparm]); + IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(block_nr, sec, "Cannot read Boot Map Script"); @@ -223,7 +223,7 @@ static void ipl_eckd_cdl(void) XEckdMbr *mbr; Ipl2 *ipl2 = (void *)sec; IplVolumeLabel *vlbl = (void *)sec; - block_number_t block_nr; + block_number_t bmt_block_nr; /* we have just read the block #0 and recognized it as "IPL1" */ sclp_print("CDL\n"); @@ -238,8 +238,8 @@ static void ipl_eckd_cdl(void) IPL_assert(mbr->dev_type == DEV_TYPE_ECKD, "Non-ECKD device type in zIPL section of IPL2 record."); - /* save pointer to Boot Script */ - block_nr = eckd_block_num((void *)&(mbr->blockptr)); + /* save pointer to Boot Map Table */ + bmt_block_nr = eckd_block_num(&mbr->blockptr); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(2, vlbl, "Cannot read Volume Label at block 2"); @@ -249,7 +249,7 @@ static void ipl_eckd_cdl(void) "Invalid magic of volser block"); print_volser(vlbl->f.volser); - run_eckd_boot_script(block_nr); + run_eckd_boot_script(bmt_block_nr); /* no return */ } @@ -280,7 +280,7 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) { - block_number_t block_nr; + block_number_t bmt_block_nr; BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */ if (mode != ECKD_LDL_UNLABELED) { @@ -299,8 +299,10 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) } verify_boot_info(bip); - block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr)); - run_eckd_boot_script(block_nr); + /* save pointer to Boot Map Table */ + bmt_block_nr = eckd_block_num((void *)&bip->bp.ipl.bm_ptr.eckd.bptr); + + run_eckd_boot_script(bmt_block_nr); /* no return */ } @@ -325,7 +327,7 @@ static void print_eckd_msg(void) static void ipl_eckd(void) { - ScsiMbr *mbr = (void *)sec; + XEckdMbr *mbr = (void *)sec; LDL_VTOC *vlbl = (void *)sec; print_eckd_msg(); @@ -449,10 +451,8 @@ static void zipl_run(ScsiBlockPtr *pte) static void ipl_scsi(void) { ScsiMbr *mbr = (void *)sec; - uint8_t *ns, *ns_end; int program_table_entries = 0; - const int pte_len = sizeof(ScsiBlockPtr); - ScsiBlockPtr *prog_table_entry = NULL; + BootMapTable *prog_table = (void *)sec; unsigned int loadparm = get_loadparm_index(); /* Grab the MBR */ @@ -467,34 +467,28 @@ static void ipl_scsi(void) debug_print_int("MBR Version", mbr->version_id); IPL_check(mbr->version_id == 1, "Unknown MBR layout version, assuming version 1"); - debug_print_int("program table", mbr->blockptr[0].blockno); - IPL_assert(mbr->blockptr[0].blockno, "No Program Table"); + debug_print_int("program table", mbr->pt.blockno); + IPL_assert(mbr->pt.blockno, "No Program Table"); /* Parse the program table */ - read_block(mbr->blockptr[0].blockno, sec, - "Error reading Program Table"); - + read_block(mbr->pt.blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - debug_print_int("loadparm index", loadparm); - ns_end = sec + virtio_get_block_size(); - for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) { - prog_table_entry = (ScsiBlockPtr *)ns; - if (!prog_table_entry->blockno) { + while (program_table_entries <= MAX_TABLE_ENTRIES) { + if (!prog_table->entry[program_table_entries].scsi.blockno) { break; } - program_table_entries++; - if (program_table_entries == loadparm + 1) { - break; /* selected entry found */ - } } debug_print_int("program table entries", program_table_entries); - IPL_assert(program_table_entries != 0, "Empty Program Table"); - zipl_run(prog_table_entry); /* no return */ + debug_print_int("loadparm", loadparm); + IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + " maximum number of boot entries allowed"); + + zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ } /*********************************************************************** diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index cf99a4c728..486c0f34f1 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -53,6 +53,15 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; +#define MAX_TABLE_ENTRIES 30 + +/* aka Program Table */ +typedef struct BootMapTable { + uint8_t magic[4]; + uint8_t reserved[12]; + BootMapPointer entry[]; +} __attribute__ ((packed)) BootMapTable; + typedef struct ComponentEntry { ScsiBlockPtr data; uint8_t pad[7]; @@ -70,7 +79,7 @@ typedef struct ScsiMbr { uint8_t magic[4]; uint32_t version_id; uint8_t reserved[8]; - ScsiBlockPtr blockptr[]; + ScsiBlockPtr pt; /* block pointer to program table */ } __attribute__ ((packed)) ScsiMbr; #define ZIPL_MAGIC "zIPL" From 80beedcc387062a71c6563dfae0f607b396c2bfb Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:08 -0500 Subject: [PATCH 02/27] s390-ccw: refactor eckd_block_num to use CHS Add new cylinder/head/sector struct. Use it to calculate eckd block numbers instead of a BootMapPointer (which used eckd chs anyway). Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 28 ++++++++++++++-------------- pc-bios/s390-ccw/bootmap.h | 8 ++++++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index a4eaf24c8d..9534f56060 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -95,32 +95,32 @@ static inline void verify_boot_info(BootInfo *bip) "Bad block size in zIPL section of the 1st record."); } -static block_number_t eckd_block_num(BootMapPointer *p) +static block_number_t eckd_block_num(EckdCHS *chs) { const uint64_t sectors = virtio_get_sectors(); const uint64_t heads = virtio_get_heads(); - const uint64_t cylinder = p->eckd.cylinder - + ((p->eckd.head & 0xfff0) << 12); - const uint64_t head = p->eckd.head & 0x000f; + const uint64_t cylinder = chs->cylinder + + ((chs->head & 0xfff0) << 12); + const uint64_t head = chs->head & 0x000f; const block_number_t block = sectors * heads * cylinder + sectors * head - + p->eckd.sector + + chs->sector - 1; /* block nr starts with zero */ return block; } static bool eckd_valid_address(BootMapPointer *p) { - const uint64_t head = p->eckd.head & 0x000f; + const uint64_t head = p->eckd.chs.head & 0x000f; if (head >= virtio_get_heads() - || p->eckd.sector > virtio_get_sectors() - || p->eckd.sector <= 0) { + || p->eckd.chs.sector > virtio_get_sectors() + || p->eckd.chs.sector <= 0) { return false; } if (!virtio_guessed_disk_nature() && - eckd_block_num(p) >= virtio_get_blocks()) { + eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) { return false; } @@ -140,7 +140,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) do { more_data = false; for (j = 0;; j++) { - block_nr = eckd_block_num((void *)&(bprs[j].xeckd)); + block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs); if (is_null_block_number(block_nr)) { /* end of chunk */ break; } @@ -198,7 +198,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr) memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); - block_nr = eckd_block_num(&bmt->entry[loadparm]); + block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -206,7 +206,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr) for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) { address = bms->entry[i].address.load_address; - block_nr = eckd_block_num(&(bms->entry[i].blkptr)); + block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs); do { block_nr = load_eckd_segments(block_nr, &address); @@ -239,7 +239,7 @@ static void ipl_eckd_cdl(void) "Non-ECKD device type in zIPL section of IPL2 record."); /* save pointer to Boot Map Table */ - bmt_block_nr = eckd_block_num(&mbr->blockptr); + bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(2, vlbl, "Cannot read Volume Label at block 2"); @@ -300,7 +300,7 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) verify_boot_info(bip); /* save pointer to Boot Map Table */ - bmt_block_nr = eckd_block_num((void *)&bip->bp.ipl.bm_ptr.eckd.bptr); + bmt_block_nr = eckd_block_num(&bip->bp.ipl.bm_ptr.eckd.bptr.chs); run_eckd_boot_script(bmt_block_nr); /* no return */ diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 486c0f34f1..b361084073 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -32,10 +32,14 @@ typedef struct FbaBlockPtr { uint16_t blockct; } __attribute__ ((packed)) FbaBlockPtr; -typedef struct EckdBlockPtr { - uint16_t cylinder; /* cylinder/head/sector is an address of the block */ +typedef struct EckdCHS { + uint16_t cylinder; uint16_t head; uint8_t sector; +} __attribute__ ((packed)) EckdCHS; + +typedef struct EckdBlockPtr { + EckdCHS chs; /* cylinder/head/sector is an address of the block */ uint16_t size; uint8_t count; /* (size_in_blocks-1); * it's 0 for TablePtr, ScriptPtr, and SectionPtr */ From ac4c5958b1e6165971303cb02598b190485481f5 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:09 -0500 Subject: [PATCH 03/27] s390-ccw: refactor IPL structs ECKD DASDs have different IPL structures for CDL and LDL formats. The current Ipl1 and Ipl2 structs follow the CDL format, so we prepend "EckdCdl" to them. Boot info for LDL has been moved to a new struct: EckdLdlIpl1. Signed-off-by: Collin L. Walling Acked-by: Janosch Frank Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 12 ++++++------ pc-bios/s390-ccw/bootmap.h | 35 ++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 9534f56060..a94638db2c 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -221,7 +221,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr) static void ipl_eckd_cdl(void) { XEckdMbr *mbr; - Ipl2 *ipl2 = (void *)sec; + EckdCdlIpl2 *ipl2 = (void *)sec; IplVolumeLabel *vlbl = (void *)sec; block_number_t bmt_block_nr; @@ -231,7 +231,7 @@ static void ipl_eckd_cdl(void) memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(1, ipl2, "Cannot read IPL2 record at block 1"); - mbr = &ipl2->u.x.mbr; + mbr = &ipl2->mbr; IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record."); IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size), "Bad block size in zIPL section of IPL2 record."); @@ -281,7 +281,7 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) { block_number_t bmt_block_nr; - BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */ + EckdLdlIpl1 *ipl1 = (void *)sec; if (mode != ECKD_LDL_UNLABELED) { print_eckd_ldl_msg(mode); @@ -292,15 +292,15 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(0, sec, "Cannot read block 0 to grab boot info."); if (mode == ECKD_LDL_UNLABELED) { - if (!magic_match(bip->magic, ZIPL_MAGIC)) { + if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) { return; /* not applicable layout */ } sclp_print("unlabeled LDL.\n"); } - verify_boot_info(bip); + verify_boot_info(&ipl1->bip); /* save pointer to Boot Map Table */ - bmt_block_nr = eckd_block_num(&bip->bp.ipl.bm_ptr.eckd.bptr.chs); + bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs); run_eckd_boot_script(bmt_block_nr); /* no return */ diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index b361084073..4bd95cd4d2 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -239,22 +239,27 @@ typedef struct BootInfo { /* @ 0x70, record #0 */ } bp; } __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */ -typedef struct Ipl1 { - unsigned char key[4]; /* == "IPL1" */ - unsigned char data[24]; -} __attribute__((packed)) Ipl1; +/* + * Structs for IPL + */ +#define STAGE2_BLK_CNT_MAX 24 /* Stage 1b can load up to 24 blocks */ -typedef struct Ipl2 { - unsigned char key[4]; /* == "IPL2" */ - union { - unsigned char data[144]; - struct { - unsigned char reserved1[92-4]; - XEckdMbr mbr; - unsigned char reserved2[144-(92-4)-sizeof(XEckdMbr)]; - } x; - } u; -} __attribute__((packed)) Ipl2; +typedef struct EckdCdlIpl1 { + uint8_t key[4]; /* == "IPL1" */ + uint8_t data[24]; +} __attribute__((packed)) EckdCdlIpl1; + +typedef struct EckdCdlIpl2 { + uint8_t key[4]; /* == "IPL2" */ + uint8_t reserved0[88]; + XEckdMbr mbr; + uint8_t reserved[24]; +} __attribute__((packed)) EckdCdlIpl2; + +typedef struct EckdLdlIpl1 { + uint8_t reserved[112]; + BootInfo bip; /* BootInfo is MBR for LDL */ +} __attribute__((packed)) EckdLdlIpl1; typedef struct IplVolumeLabel { unsigned char key[4]; /* == "VOL1" */ From fc0e208774364c2a8013aa028b742a8dde6d2c2b Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:10 -0500 Subject: [PATCH 04/27] 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) From 118ee80f7921e8b062c445ac3986ee11409520d0 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:11 -0500 Subject: [PATCH 05/27] 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); From 26b2a2a49171ff44286722681f5aebdfee23e9cf Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:12 -0500 Subject: [PATCH 06/27] s390-ccw: parse and set boot menu options Set boot menu options for an s390 guest and store them in the iplb. These options are set via the QEMU command line option: -boot menu=on|off[,splash-time=X] or via the libvirt domain xml: Where X represents some positive integer representing milliseconds. Any value set for loadparm will override all boot menu options. If loadparm=PROMPT, then the menu will be enabled without a timeout. Signed-off-by: Collin L. Walling Reviewed-by: Janosch Frank Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 52 +++++++++++++++++++++++++++++++++++++++++ hw/s390x/ipl.h | 9 +++++-- pc-bios/s390-ccw/iplb.h | 9 +++++-- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 79f5a58adb..ee2039dc69 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -23,6 +23,9 @@ #include "hw/s390x/ebcdic.h" #include "ipl.h" #include "qemu/error-report.h" +#include "qemu/config-file.h" +#include "qemu/cutils.h" +#include "qemu/option.h" #define KERN_IMAGE_START 0x010000UL #define KERN_PARM_AREA 0x010480UL @@ -219,6 +222,54 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void s390_ipl_set_boot_menu(S390IPLState *ipl) +{ + QemuOptsList *plist = qemu_find_opts("boot-opts"); + QemuOpts *opts = QTAILQ_FIRST(&plist->head); + uint8_t *flags = &ipl->qipl.qipl_flags; + uint32_t *timeout = &ipl->qipl.boot_menu_timeout; + const char *tmp; + unsigned long splash_time = 0; + + if (!get_boot_device(0)) { + if (boot_menu) { + error_report("boot menu requires a bootindex to be specified for " + "the IPL device."); + } + return; + } + + switch (ipl->iplb.pbt) { + case S390_IPL_TYPE_CCW: + break; + default: + error_report("boot menu is not supported for this device type."); + return; + } + + if (!boot_menu) { + return; + } + + *flags |= QIPL_FLAG_BM_OPTS_CMD; + + tmp = qemu_opt_get(opts, "splash-time"); + + if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { + error_report("splash-time is invalid, forcing it to 0."); + *timeout = 0; + return; + } + + if (splash_time > 0xffffffff) { + error_report("splash-time is too large, forcing it to max value."); + *timeout = 0xffffffff; + return; + } + + *timeout = cpu_to_be32(splash_time); +} + static bool s390_gen_initial_iplb(S390IPLState *ipl) { DeviceState *dev_st; @@ -435,6 +486,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) } ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); } + s390_ipl_set_boot_menu(ipl); s390_ipl_prepare_qipl(cpu); } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 5cc3b770d4..d6c6f75b76 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -91,6 +91,9 @@ void s390_reipl_request(void); #define QIPL_ADDRESS 0xcc +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 + /* * The QEMU IPL Parameters will be stored at absolute address * 204 (0xcc) which means it is 32-bit word aligned but not @@ -104,9 +107,11 @@ void s390_reipl_request(void); * in pc-bios/s390-ccw/iplb.h. */ struct QemuIplParameters { - uint8_t reserved1[4]; + uint8_t qipl_flags; + uint8_t reserved1[3]; uint64_t netboot_start_addr; - uint8_t reserved2[16]; + uint32_t boot_menu_timeout; + uint8_t reserved2[12]; } QEMU_PACKED; typedef struct QemuIplParameters QemuIplParameters; diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 31d2934762..832bb94440 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -74,14 +74,19 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); #define QIPL_ADDRESS 0xcc +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 + /* * This definition must be kept in sync with the defininition * in hw/s390x/ipl.h */ struct QemuIplParameters { - uint8_t reserved1[4]; + uint8_t qipl_flags; + uint8_t reserved1[3]; uint64_t netboot_start_addr; - uint8_t reserved2[16]; + uint32_t boot_menu_timeout; + uint8_t reserved2[12]; } __attribute__ ((packed)); typedef struct QemuIplParameters QemuIplParameters; From 9eaa654ab327b54901f398440258be766c327953 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:13 -0500 Subject: [PATCH 07/27] s390-ccw: set up interactive boot menu parameters Reads boot menu flag and timeout values from the iplb and sets the respective fields for the menu. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/Makefile | 2 +- pc-bios/s390-ccw/main.c | 24 ++++++++++++++++++++++++ pc-bios/s390-ccw/menu.c | 22 ++++++++++++++++++++++ pc-bios/s390-ccw/s390-ccw.h | 3 +++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 pc-bios/s390-ccw/menu.c diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 9f7904fc20..1712c2d95d 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 libc.o +OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.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 diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index e41b264a6f..32ed70ebdd 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -18,6 +18,9 @@ IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; QemuIplParameters qipl; +#define LOADPARM_PROMPT "PROMPT " +#define LOADPARM_EMPTY "........" + /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that * a subsystem-identification is at 184-187 and bytes 188-191 are zero @@ -74,6 +77,26 @@ static bool find_dev(Schib *schib, int dev_no) return false; } +static void menu_setup(void) +{ + if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) { + menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); + return; + } + + /* If loadparm was set to any other value, then do not enable menu */ + if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) { + return; + } + + switch (iplb.pbt) { + case S390_IPL_TYPE_CCW: + menu_set_parms(qipl.qipl_flags & QIPL_FLAG_BM_OPTS_CMD, + qipl.boot_menu_timeout); + return; + } +} + static void virtio_setup(void) { Schib schib; @@ -117,6 +140,7 @@ static void virtio_setup(void) default: panic("List-directed IPL not supported yet!\n"); } + menu_setup(); } else { for (ssid = 0; ssid < 0x3; ssid++) { blk_schid.ssid = ssid; diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c new file mode 100644 index 0000000000..1ce33ddf3d --- /dev/null +++ b/pc-bios/s390-ccw/menu.c @@ -0,0 +1,22 @@ +/* + * QEMU S390 Interactive Boot Menu + * + * Copyright 2018 IBM Corp. + * Author: Collin L. Walling + * + * 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" + +static uint8_t flag; +static uint64_t timeout; + +void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) +{ + flag = boot_menu_flag; + timeout = boot_menu_timeout; +} diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 25d4d213ea..6cfd4b2183 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -84,6 +84,9 @@ ulong get_second(void); /* bootmap.c */ void zipl_load(void); +/* menu.c */ +void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); + static inline void fill_hex(char *out, unsigned char val) { const char hex[] = "0123456789abcdef"; From ba831b25262aa3d0c6d5eb0cd530487d3cb05493 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:14 -0500 Subject: [PATCH 08/27] s390-ccw: read stage2 boot loader data to find menu Read the stage2 boot loader data block-by-block. We scan the current block for the string "zIPL" to detect the start of the boot menu banner. We then load the adjacent blocks (previous block and next block) to account for the possibility of menu data spanning multiple blocks. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.c | 94 +++++++++++++++++++++++++++++++++++-- pc-bios/s390-ccw/bootmap.h | 23 ++++++++- pc-bios/s390-ccw/menu.c | 10 ++++ pc-bios/s390-ccw/s390-ccw.h | 2 + 4 files changed, 122 insertions(+), 7 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 092fb355fe..ae93b552c9 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -83,6 +83,10 @@ static void jump_to_IPL_code(uint64_t address) static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */ static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); +static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE))); +static void *s2_prev_blk = _s2; +static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; +static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; static inline void verify_boot_info(BootInfo *bip) { @@ -182,7 +186,77 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) return block_nr; } -static void run_eckd_boot_script(block_number_t bmt_block_nr) +static bool find_zipl_boot_menu_banner(int *offset) +{ + int i; + + /* Menu banner starts with "zIPL" */ + for (i = 0; i < virtio_get_block_size() - 4; i++) { + if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) { + *offset = i; + return true; + } + } + + return false; +} + +static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) +{ + block_number_t cur_block_nr; + block_number_t prev_block_nr = 0; + block_number_t next_block_nr = 0; + EckdStage1b *s1b = (void *)sec; + int banner_offset; + int i; + + /* Get Stage1b data */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader"); + + memset(_s2, FREE_SPACE_FILLER, sizeof(_s2)); + + /* Get Stage2 data */ + for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) { + cur_block_nr = eckd_block_num(&s1b->seek[i].chs); + + if (!cur_block_nr) { + break; + } + + read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader"); + + if (find_zipl_boot_menu_banner(&banner_offset)) { + /* + * Load the adjacent blocks to account for the + * possibility of menu data spanning multiple blocks. + */ + if (prev_block_nr) { + read_block(prev_block_nr, s2_prev_blk, + "Cannot read stage2 boot loader"); + } + + if (i + 1 < STAGE2_BLK_CNT_MAX) { + next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs); + } + + if (next_block_nr) { + read_block(next_block_nr, s2_next_blk, + "Cannot read stage2 boot loader"); + } + + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + } + + prev_block_nr = cur_block_nr; + } + + sclp_print("No zipl boot menu data found. Booting default entry."); + return 0; +} + +static void run_eckd_boot_script(block_number_t bmt_block_nr, + block_number_t s1b_block_nr) { int i; unsigned int loadparm = get_loadparm_index(); @@ -191,6 +265,10 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr) BootMapTable *bmt = (void *)sec; BootMapScript *bms = (void *)sec; + if (menu_is_enabled_zipl()) { + loadparm = eckd_get_boot_menu_index(s1b_block_nr); + } + debug_print_int("loadparm", loadparm); IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); @@ -223,7 +301,7 @@ static void ipl_eckd_cdl(void) XEckdMbr *mbr; EckdCdlIpl2 *ipl2 = (void *)sec; IplVolumeLabel *vlbl = (void *)sec; - block_number_t bmt_block_nr; + block_number_t bmt_block_nr, s1b_block_nr; /* we have just read the block #0 and recognized it as "IPL1" */ sclp_print("CDL\n"); @@ -241,6 +319,9 @@ static void ipl_eckd_cdl(void) /* save pointer to Boot Map Table */ bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs); + /* save pointer to Stage1b Data */ + s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs); + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(2, vlbl, "Cannot read Volume Label at block 2"); IPL_assert(magic_match(vlbl->key, VOL1_MAGIC), @@ -249,7 +330,7 @@ static void ipl_eckd_cdl(void) "Invalid magic of volser block"); print_volser(vlbl->f.volser); - run_eckd_boot_script(bmt_block_nr); + run_eckd_boot_script(bmt_block_nr, s1b_block_nr); /* no return */ } @@ -280,7 +361,7 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) { - block_number_t bmt_block_nr; + block_number_t bmt_block_nr, s1b_block_nr; EckdLdlIpl1 *ipl1 = (void *)sec; if (mode != ECKD_LDL_UNLABELED) { @@ -302,7 +383,10 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* save pointer to Boot Map Table */ bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs); - run_eckd_boot_script(bmt_block_nr); + /* save pointer to Stage1b Data */ + s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs); + + run_eckd_boot_script(bmt_block_nr, s1b_block_nr); /* no return */ } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 4cf7e1e463..c636626f1a 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -87,6 +87,7 @@ typedef struct ScsiMbr { } __attribute__ ((packed)) ScsiMbr; #define ZIPL_MAGIC "zIPL" +#define ZIPL_MAGIC_EBCDIC "\xa9\xc9\xd7\xd3" #define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */ #define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */ #define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */ @@ -249,15 +250,33 @@ typedef struct EckdCdlIpl1 { uint8_t data[24]; } __attribute__((packed)) EckdCdlIpl1; +typedef struct EckdSeekArg { + uint16_t pad; + EckdCHS chs; + uint8_t pad2; +} __attribute__ ((packed)) EckdSeekArg; + +typedef struct EckdStage1b { + uint8_t reserved[32 * STAGE2_BLK_CNT_MAX]; + struct EckdSeekArg seek[STAGE2_BLK_CNT_MAX]; + uint8_t unused[64]; +} __attribute__ ((packed)) EckdStage1b; + +typedef struct EckdStage1 { + uint8_t reserved[72]; + struct EckdSeekArg seek[2]; +} __attribute__ ((packed)) EckdStage1; + typedef struct EckdCdlIpl2 { uint8_t key[4]; /* == "IPL2" */ - uint8_t reserved0[88]; + struct EckdStage1 stage1; XEckdMbr mbr; uint8_t reserved[24]; } __attribute__((packed)) EckdCdlIpl2; typedef struct EckdLdlIpl1 { - uint8_t reserved[112]; + uint8_t reserved[24]; + struct EckdStage1 stage1; BootInfo bip; /* BootInfo is MBR for LDL */ } __attribute__((packed)) EckdLdlIpl1; diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 1ce33ddf3d..c1d242fed1 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -15,8 +15,18 @@ static uint8_t flag; static uint64_t timeout; +int menu_get_zipl_boot_index(const char *menu_data) +{ + return 0; /* implemented next patch */ +} + void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) { flag = boot_menu_flag; timeout = boot_menu_timeout; } + +bool menu_is_enabled_zipl(void) +{ + return flag & QIPL_FLAG_BM_OPTS_CMD; +} diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 6cfd4b2183..c0dd37f7ba 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -86,6 +86,8 @@ void zipl_load(void); /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); +int menu_get_zipl_boot_index(const char *menu_data); +bool menu_is_enabled_zipl(void); static inline void fill_hex(char *out, unsigned char val) { From f7178910845a73fcb69642476c3222af842e25e9 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:15 -0500 Subject: [PATCH 09/27] s390-ccw: print zipl boot menu When the boot menu options are present and the guest's disk has been configured by the zipl tool, then the user will be presented with an interactive boot menu with labeled entries. An example of what the menu might look like: zIPL v1.37.1-build-20170714 interactive boot menu. 0. default (linux-4.13.0) 1. linux-4.13.0 2. performance 3. kvm Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/menu.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index c1d242fed1..730d44e36b 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -15,11 +15,42 @@ static uint8_t flag; static uint64_t timeout; -int menu_get_zipl_boot_index(const char *menu_data) +static int get_boot_index(int entries) { return 0; /* implemented next patch */ } +static void zipl_println(const char *data, size_t len) +{ + char buf[len + 2]; + + ebcdic_to_ascii(data, buf, len); + buf[len] = '\n'; + buf[len + 1] = '\0'; + + sclp_print(buf); +} + +int menu_get_zipl_boot_index(const char *menu_data) +{ + size_t len; + int entries; + + /* Print and count all menu items, including the banner */ + for (entries = 0; *menu_data; entries++) { + len = strlen(menu_data); + zipl_println(menu_data, len); + menu_data += len + 1; + + if (entries < 2) { + sclp_print("\n"); + } + } + + sclp_print("\n"); + return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ +} + void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) { flag = boot_menu_flag; From ff5dbf1bc3b81248f4f1c253b586491bc8daeda5 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:16 -0500 Subject: [PATCH 10/27] s390-ccw: read user input for boot index via the SCLP console Implements an sclp_read function to capture input from the console and a wrapper function that handles parsing certain characters and adding input to a buffer. The input is checked for any erroneous values and is handled appropriately. A prompt will persist until input is entered or the timeout expires (if one was set). Example: Please choose (default will boot in 10 seconds): Correct input will boot the respective boot index. If the user's input is empty, 0, or if the timeout expires, then the default zipl entry will be chosen. If the input is within the range of available boot entries, then the selection will be booted. Any erroneous input will cancel the timeout and re-prompt the user. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/menu.c | 149 +++++++++++++++++++++++++++++++++++- pc-bios/s390-ccw/s390-ccw.h | 2 + pc-bios/s390-ccw/sclp.c | 19 +++++ pc-bios/s390-ccw/virtio.c | 2 +- 4 files changed, 170 insertions(+), 2 deletions(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 730d44e36b..b99ff03298 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -12,12 +12,159 @@ #include "libc.h" #include "s390-ccw.h" +#define KEYCODE_NO_INP '\0' +#define KEYCODE_ESCAPE '\033' +#define KEYCODE_BACKSP '\177' +#define KEYCODE_ENTER '\r' + +#define TOD_CLOCK_MILLISECOND 0x3e8000 + +#define LOW_CORE_EXTERNAL_INT_ADDR 0x86 +#define CLOCK_COMPARATOR_INT 0X1004 + static uint8_t flag; static uint64_t timeout; +static inline void enable_clock_int(void) +{ + uint64_t tmp = 0; + + asm volatile( + "stctg 0,0,%0\n" + "oi 6+%0, 0x8\n" + "lctlg 0,0,%0" + : : "Q" (tmp) : "memory" + ); +} + +static inline void disable_clock_int(void) +{ + uint64_t tmp = 0; + + asm volatile( + "stctg 0,0,%0\n" + "ni 6+%0, 0xf7\n" + "lctlg 0,0,%0" + : : "Q" (tmp) : "memory" + ); +} + +static inline void set_clock_comparator(uint64_t time) +{ + asm volatile("sckc %0" : : "Q" (time)); +} + +static inline bool check_clock_int(void) +{ + uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR; + + consume_sclp_int(); + + return *code == CLOCK_COMPARATOR_INT; +} + +static int read_prompt(char *buf, size_t len) +{ + char inp[2] = {}; + uint8_t idx = 0; + uint64_t time; + + if (timeout) { + time = get_clock() + timeout * TOD_CLOCK_MILLISECOND; + set_clock_comparator(time); + enable_clock_int(); + timeout = 0; + } + + while (!check_clock_int()) { + + sclp_read(inp, 1); /* Process only one character at a time */ + + switch (inp[0]) { + case KEYCODE_NO_INP: + case KEYCODE_ESCAPE: + continue; + case KEYCODE_BACKSP: + if (idx > 0) { + buf[--idx] = 0; + sclp_print("\b \b"); + } + continue; + case KEYCODE_ENTER: + disable_clock_int(); + return idx; + default: + /* Echo input and add to buffer */ + if (idx < len) { + buf[idx++] = inp[0]; + sclp_print(inp); + } + } + } + + disable_clock_int(); + *buf = 0; + + return 0; +} + +static int get_index(void) +{ + char buf[11]; + int len; + int i; + + memset(buf, 0, sizeof(buf)); + + len = read_prompt(buf, sizeof(buf) - 1); + + /* If no input, boot default */ + if (len == 0) { + return 0; + } + + /* Check for erroneous input */ + for (i = 0; i < len; i++) { + if (!isdigit(buf[i])) { + return -1; + } + } + + return atoui(buf); +} + +static void boot_menu_prompt(bool retry) +{ + char tmp[11]; + + if (retry) { + sclp_print("\nError: undefined configuration" + "\nPlease choose:\n"); + } else if (timeout > 0) { + sclp_print("Please choose (default will boot in "); + sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp))); + sclp_print(" seconds):\n"); + } else { + sclp_print("Please choose:\n"); + } +} + static int get_boot_index(int entries) { - return 0; /* implemented next patch */ + int boot_index; + bool retry = false; + char tmp[5]; + + do { + boot_menu_prompt(retry); + boot_index = get_index(); + retry = true; + } while (boot_index < 0 || boot_index >= entries); + + sclp_print("\nBooting entry #"); + sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); + + return boot_index; } static void zipl_println(const char *data, size_t len) diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index c0dd37f7ba..aeba8b0a99 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -71,6 +71,7 @@ unsigned int get_loadparm_index(void); void sclp_print(const char *string); void sclp_setup(void); void sclp_get_loadparm_ascii(char *loadparm); +int sclp_read(char *str, size_t count); /* virtio.c */ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, @@ -79,6 +80,7 @@ bool virtio_is_supported(SubChannelId schid); void virtio_blk_setup_device(SubChannelId schid); int virtio_read(ulong sector, void *load_addr); int enable_mss_facility(void); +u64 get_clock(void); ulong get_second(void); /* bootmap.c */ diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index e6a089889a..a2f25ebd4f 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -119,3 +119,22 @@ void sclp_get_loadparm_ascii(char *loadparm) ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8); } } + +int sclp_read(char *str, size_t count) +{ + ReadEventData *sccb = (void *)_sccb; + char *buf = (char *)(&sccb->ebh) + 7; + + /* If count exceeds max buffer size, then restrict it to the max size */ + if (count > SCCB_SIZE - 8) { + count = SCCB_SIZE - 8; + } + + sccb->h.length = SCCB_SIZE; + sccb->h.function_code = SCLP_UNCONDITIONAL_READ; + + sclp_service_call(SCLP_CMD_READ_EVENT_DATA, sccb); + memcpy(str, buf, count); + + return sccb->ebh.length - 7; +} diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index c890a0330b..817e7f56ea 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -176,7 +176,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags) } } -static u64 get_clock(void) +u64 get_clock(void) { u64 r; From dbf2091aba8abc312dbab931b12ebc4ff213ffd6 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:17 -0500 Subject: [PATCH 11/27] s390-ccw: set cp_receive mask only when needed and consume pending service irqs It is possible while waiting for multiple types of external interrupts that we might have pending irqs remaining between irq consumption and irq-type disabling. Those interrupts could potentially propagate to the guest after IPL completes and cause unwanted behavior. As it is today, the SCLP will only recognize write events that are enabled by the control program's send and receive masks. To limit the window for, and prevent further irqs from, ASCII console events (specifically keystrokes), we should only enable the control program's receive mask when we need it. While we're at it, remove assignment of the (non control program) send and receive masks, as those are actually set by the SCLP. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/menu.c | 5 +++++ pc-bios/s390-ccw/s390-ccw.h | 1 + pc-bios/s390-ccw/sclp.c | 10 ++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index b99ff03298..8d55869448 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -11,6 +11,7 @@ #include "libc.h" #include "s390-ccw.h" +#include "sclp.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' @@ -116,8 +117,12 @@ static int get_index(void) memset(buf, 0, sizeof(buf)); + sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII); + len = read_prompt(buf, sizeof(buf) - 1); + sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); + /* If no input, boot default */ if (len == 0) { return 0; diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index aeba8b0a99..c4ddf9f228 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -69,6 +69,7 @@ unsigned int get_loadparm_index(void); /* sclp.c */ void sclp_print(const char *string); +void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask); void sclp_setup(void); void sclp_get_loadparm_ascii(char *loadparm); int sclp_read(char *str, size_t count); diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index a2f25ebd4f..3836cb4716 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -46,23 +46,21 @@ static int sclp_service_call(unsigned int command, void *sccb) return 0; } -static void sclp_set_write_mask(void) +void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask) { WriteEventMask *sccb = (void *)_sccb; sccb->h.length = sizeof(WriteEventMask); sccb->mask_length = sizeof(unsigned int); - sccb->receive_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->cp_receive_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->send_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->cp_send_mask = SCLP_EVENT_MASK_MSG_ASCII; + sccb->cp_receive_mask = receive_mask; + sccb->cp_send_mask = send_mask; sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb); } void sclp_setup(void) { - sclp_set_write_mask(); + sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); } long write(int fd, const void *str, size_t len) From 53b310ce539cfadf1f2fad5927a9e8f88ec9db13 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:18 -0500 Subject: [PATCH 12/27] s390-ccw: use zipl values when no boot menu options are present If no boot menu options are present, then flag the boot menu to use the zipl options that were set in the zipl configuration file (and stored on disk by zipl). These options are found at some offset prior to the start of the zipl boot menu banner. The zipl timeout value is limited to a 16-bit unsigned integer and stored as seconds, so we take care to convert it to milliseconds in order to conform to the rest of the boot menu functionality. This is limited to CCW devices. For reference, the zipl configuration file uses the following fields in the menu section: prompt=1 enable the boot menu timeout=X set the timeout to X seconds To explicitly disregard any boot menu options, then menu=off or must be specified. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 5 +++++ hw/s390x/ipl.h | 1 + pc-bios/s390-ccw/iplb.h | 1 + pc-bios/s390-ccw/main.c | 3 ++- pc-bios/s390-ccw/menu.c | 16 +++++++++++++++- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index ee2039dc69..c12e460a7f 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -241,6 +241,11 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) switch (ipl->iplb.pbt) { case S390_IPL_TYPE_CCW: + /* In the absence of -boot menu, use zipl parameters */ + if (!qemu_opt_get(opts, "menu")) { + *flags |= QIPL_FLAG_BM_OPTS_ZIPL; + return; + } break; default: error_report("boot menu is not supported for this device type."); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index d6c6f75b76..0570d0ad75 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -93,6 +93,7 @@ void s390_reipl_request(void); /* Boot Menu flags */ #define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 /* * The QEMU IPL Parameters will be stored at absolute address diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 832bb94440..7dfce4fbcf 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -76,6 +76,7 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); /* Boot Menu flags */ #define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 /* * This definition must be kept in sync with the defininition diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 32ed70ebdd..a7473b0397 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -20,6 +20,7 @@ QemuIplParameters qipl; #define LOADPARM_PROMPT "PROMPT " #define LOADPARM_EMPTY "........" +#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -91,7 +92,7 @@ static void menu_setup(void) switch (iplb.pbt) { case S390_IPL_TYPE_CCW: - menu_set_parms(qipl.qipl_flags & QIPL_FLAG_BM_OPTS_CMD, + menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK, qipl.boot_menu_timeout); return; } diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 8d55869448..ee56939c21 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -18,6 +18,10 @@ #define KEYCODE_BACKSP '\177' #define KEYCODE_ENTER '\r' +/* Offsets from zipl fields to zipl banner start */ +#define ZIPL_TIMEOUT_OFFSET 138 +#define ZIPL_FLAG_OFFSET 140 + #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 @@ -187,6 +191,16 @@ int menu_get_zipl_boot_index(const char *menu_data) { size_t len; int entries; + uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); + uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); + + if (flag == QIPL_FLAG_BM_OPTS_ZIPL) { + if (!zipl_flag) { + return 0; /* Boot default */ + } + /* zipl stores timeout as seconds */ + timeout = zipl_timeout * 1000; + } /* Print and count all menu items, including the banner */ for (entries = 0; *menu_data; entries++) { @@ -211,5 +225,5 @@ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) bool menu_is_enabled_zipl(void) { - return flag & QIPL_FLAG_BM_OPTS_CMD; + return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); } From ffb4a1c80792aa71da900dbb495b21fbc0130073 Mon Sep 17 00:00:00 2001 From: "Collin L. Walling" Date: Fri, 23 Feb 2018 10:43:19 -0500 Subject: [PATCH 13/27] s390-ccw: interactive boot menu for scsi Interactive boot menu for scsi. This follows a similar procedure as the interactive menu for eckd dasd. An example follows: s390x Enumerated Boot Menu. 3 entries detected. Select from index 0 to 2. Signed-off-by: Collin L. Walling Reviewed-by: Thomas Huth [thuth: Added additional "break;" statement to avoid analyzer warnings] Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 2 ++ pc-bios/s390-ccw/bootmap.c | 4 ++++ pc-bios/s390-ccw/main.c | 1 + pc-bios/s390-ccw/menu.c | 20 ++++++++++++++++++++ pc-bios/s390-ccw/s390-ccw.h | 2 ++ 5 files changed, 29 insertions(+) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index c12e460a7f..798e99aadf 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -247,6 +247,8 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) return; } break; + case S390_IPL_TYPE_QEMU_SCSI: + break; default: error_report("boot menu is not supported for this device type."); return; diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index ae93b552c9..29bfd8c875 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -568,6 +568,10 @@ static void ipl_scsi(void) debug_print_int("program table entries", program_table_entries); IPL_assert(program_table_entries != 0, "Empty Program Table"); + if (menu_is_enabled_enum()) { + loadparm = menu_get_enum_boot_index(program_table_entries); + } + debug_print_int("loadparm", loadparm); IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index a7473b0397..9d9f8cf4d3 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -92,6 +92,7 @@ static void menu_setup(void) switch (iplb.pbt) { case S390_IPL_TYPE_CCW: + case S390_IPL_TYPE_QEMU_SCSI: menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK, qipl.boot_menu_timeout); return; diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index ee56939c21..96eec81e84 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -217,6 +217,21 @@ int menu_get_zipl_boot_index(const char *menu_data) return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ } + +int menu_get_enum_boot_index(int entries) +{ + char tmp[4]; + + sclp_print("s390x Enumerated Boot Menu.\n\n"); + + sclp_print(uitoa(entries, tmp, sizeof(tmp))); + sclp_print(" entries detected. Select from boot index 0 to "); + sclp_print(uitoa(entries - 1, tmp, sizeof(tmp))); + sclp_print(".\n\n"); + + return get_boot_index(entries); +} + void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) { flag = boot_menu_flag; @@ -227,3 +242,8 @@ bool menu_is_enabled_zipl(void) { return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); } + +bool menu_is_enabled_enum(void) +{ + return flag & QIPL_FLAG_BM_OPTS_CMD; +} diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index c4ddf9f228..fd18da22c6 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -91,6 +91,8 @@ void zipl_load(void); void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); int menu_get_zipl_boot_index(const char *menu_data); bool menu_is_enabled_zipl(void); +int menu_get_enum_boot_index(int entries); +bool menu_is_enabled_enum(void); static inline void fill_hex(char *out, unsigned char val) { From 9c050f3d15697c4c84c9d6aa7af779a273b71d87 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 26 Feb 2018 11:02:43 +0100 Subject: [PATCH 14/27] pc-bios/s390: Rebuild the s390x firmware images with the boot menu changes Provide a new s390-ccw.img binary with the boot menu patches by Collin. Though there should not be any visible changes for the network booting, the s390-netboot.img binary has been rebuilt, too, since some of the changes affected the shared source files. Signed-off-by: Thomas Huth --- pc-bios/s390-ccw.img | Bin 26416 -> 34568 bytes pc-bios/s390-netboot.img | Bin 83864 -> 83776 bytes 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 pc-bios/s390-netboot.img diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index 97155d2638eebc1220a63287fcada21df65b3a77..fbd76bb55ed01367c9e2e5656e9a3e2eb21f823f 100644 GIT binary patch literal 34568 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 literal 26416 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 literal 83864 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
>ZMo4JE^^*cpAuDMM8YE652zoJKF0$%74vO}-H zOTDX2V-|U%r(>gNyHivL+PlzC4>g_Ik>3{tNDa4$BU%?W>Kj_()Hj4SyKj22Et4XZ6#Q6#8 zlosg_6e!1NX)N}!S9P0*Vw|1ksfX1*k4d0a^O7R1x(j4RVSO2`Q_aF=B29?tHP6Q+ zC#mm7MC|q;r?t?vJ5hz(6Y+{#bOjmf*1+|jv0Xm9ff*WuMB+0^mJ;UTg)Pdl8KJFn zAicFp(ReBBqmeI+dtK~F*YA_mllNd}C%0658Qi3uS<_Bgdf}7awQJDi;`T3w8=scP zNt+X|65@rl8Q18IVIC6S_dVVY9w(Js>x_H9T%HzxS}rc$(@!`m5OLhr3uctviA#b=3sa7wK#m9s#V-&vkr zJ+QofZhPfdN=tL&Ozm8BIM-OId&d(u&J3Meym5v)yE~on>Iyp*%sPq|wUU*V z0=*@*t&Y)0=jM7M>=W2t3AWmq*CQ&ay)^i-*(Bx553rp?F((p|R$8#X)K;u?awpBz z-pN_mdN-QLnC|cv235oduyw*P2^)E7w0W~=fkO&9giWr~8y}r{?eg;K!Gg7>SL;^q zIJV}0btSiu^Hyz!N7x%^n&9_S%zi(uf<1v#+SP6PgV8$C=B*kOEAsU23EUvGMm-u$ zLT8fFFb?cn!huQR02m-8_`qoNzs!epe|&iNKmKYyr2FE-JFGlb9N3%wA$R=$N`Fni z^q=xq(_hmk{U0Q%ad#>G@2drk%hE0Rq+RmtyPKuqJY{(5;Ax@l0Jh(*EY-nxw!WX1 zI-9vSU{ooDxA%TO5a>n1Fz)9RCbL17>=;_KG`GQPb)Dmrs*7@@nRah z$Oc{vrd9d2Wv#w*Ua}33X^4sL)NbLw{OW%I*M(rHF-DRs!|W~QSH)3;{GHl6n?;n_ zn!n5e%TIu1%}z(2MXbt3ch}TYgV>v|q5W$-Y5dc@Hp(OBz6)pz0yNn-)nE;#7qp6y zcVGf?lP?#IF^pu|C&0N2)lQ^phd>=;)$Z)r*%vFCS-m?JLuFX8(YQCgAAU=kGLPD3p&HlBa|r(A zDzo@oZM-8r5h0SE_}O;wp;KR`E~&&NkQGmgmCoa7Ys@p9x7&*w-j4KsO8&W!%jnC~ zb8aO3+Thc|EQprzjGqg0zNozSR0M54W4~e|rEM%E^@3ih@)NWVwY2Otq>8VlsqLiB z=#_dIsndF;o=+;HM;U}GNZqqn>RzNOeq7qxhe(xoQ<}P%y&nAb8#jvw?SfzNue(Ur zfm{Dyd*>e=FgeGd@Ni-= zo{2H6(*ITIZGG)(TC21s>)8u=|6k%KbqxO+EYp%)2gy7kP5<(1cv4qK9XnyY|1xXL zNm}*y450p+QYRYAuI6_iH=dk{AndF*cFy?E%d6P#;bb=G5t!jKWpBRA6YcwD_gCCs z)XVi>(;4{&2U5M!;7s(QZX)2@nP2B@*H;29dDlAtuFsX)8!l?R(T>hoylYQ)!fUzvo}S)hs_)*kkIZEI_ue;f{{wzGkKV9x zQ`3#jH??f`>RWH#a?93jZ6Dq4<8Iw?+wC9QdB>fmT(|zpt2TV_>JQa_*p#(T*x>W6 zYkh1w|A!C#%X1sP@6|hYIaEe{r2b03be-c$Hyxlks?rFo`B(Zd0=igp*12r#s_rI@OPVJey~tCXq~J z6VaZ;194ch_odQ)_&Bb6TPBg*6LfroySDDifri=MD&W~3zqdc0$qJ(kf4bMGwKoxU zn_4!tHMnv(kw|21-BmHC;-_U<{rXlOe*vMq;TX!9Jt4MB?T) zKOUGRp01nQ_HOWNQ8rfV{~;%UxjKNM3rp)Rg2G+BHnWQCO2Ox>3>-Ne)BRGR--AYGcEbyHp9kq&{o37Ioq zJn8^nE8fxS%3SG6^-Fw9rIrxw?3AgpH<9Ww2_Vta6W|Ho|6B!-5IFE$U zmr7>h8LI~$U=rrq*g7{p&=>EN3~Q?Hi)L`W<81XNMMp~VS~g1dP3|GpS2&y7w(Q<< zN863NH*NY@j@#Lq!sR+e2NIedn;BU>sc1~1UZQ$kPN;iNJe`d9XxYs=HA#*Xth?=2 z6NBCi#aY2aNx{oIT^=3HE9ci2v?HRNm#pJj9yH>e`{miF?i;L6bGQmUz72Ed&fSO+ zw{73TT4vYjte1@T#;q2z`XyNzxKt1MJC^E|lr1Q?rfKV~B(?NjB9!?cao-1y>=%z+ ziVS`=s#r#WDUeV^RXKb;7dXx!#c8RN!>{)#RoBb;Xu}m(IQ%({&J|9BAk7iLY2NFp zR%Zsi#!*9g_i;im;T7^KKII|wkeTy2zl3_tN~o+QLKig9aYMs;PMr(ZX4Vc!F6MvG zbH?YOXjRux2SQ#A4RwOb$r+Tfl`)o=Ib_F{Yd8IwxRK_Ef8e#vmy$VVT7Sjqk9bl4 zBVOO+CFgQLUC4zhfLZ*iIle6bmc?I+Bu?bDSZe%spHjtt9|WL}N4$6)zSER_`e>tF z_UrTMqo>afm(Mp-cAPi%q7D}d`cNG`It4l@az@r6&zPjnNO%ZsTZ!pbeW}cxe2M2B zjJ`t z^=DN#Wxq^FLfit~K?OwT7N< zcyIJ)7r!_D+4}!^qmlb>BkwN%7pLAG_lw7h^ZWMy`z(I=xtDi+{btkSXZidx75#Yn z0O#9yd1Gp5==_q8u#P-S{V{ds_kFDVb?S`TN!=K91d2@#w9JK`g4Qb)0$0`zWdzFx zp+nF^P{y}xRH?9(r=a63lM$s#Mxp$^pDe%SZBY2i;h)nEJp!GAPAFA2EY~SYRe+DQ z@H3xv&~~Nx$ucK`{CUWWj4M^?I?mkX&{NQzN-ds%(!QhyItCqtPD9b7q6&Hh3U0|1 zbV#XltDwiBQl3<*`VgO4VBhK%DEh6bgJRd(Ht134H1w2Gm!Q`?2y`5Zo>h)gn}?uv&`D^!Qmyn~g@3km zL-EfoV^I9FwT|DLs)2%Eh<~=izYzaylQRDKDE%$OKesP;oJFou9|OOrO{qKDpo2<% z9C?e5Ds>n7ESgek_bIj^;M%EQ46a?u;G(oI2G>~w1;+yHECClg0tMGi`;uX$60|QF zg$_Z%C1_t-rPST@zqC=Q9&k(hpd-)`rFzk4>9|tKZaykAXrog1c0>D=N+b7N@Um4o7yKU&^SjFtr9ROItyk)kjnHnT zK2-xfq|{(NbV8{I+xV^RLVMa?U#rm3nXo=w}7E&$d9peRc{8 z?%*)=h*FOrcLlgdBW&Ly&~a$JQlIOCc0(tiLrOi~4johK3HYkP9RgR~qSTXhP~?3< z>W?V(my=NBeUbhz0Qcn)DDs{egLW(RH$%`v&}rzHQqK-Rr|9x+6u+b6tlX*8 z-y?VB5ET5%<4S!6JywD{ww!$`xMST=a9@S*LU3QJf`a>6J#>QqB{0p-YdQZ_i2hx0 z{{Y?v_xy1vxaX&!lS+Nlb(~e;zDYl;TKJ`TyvS>3JF$)nIiO8s*kbPOu()BKhh^%vDZ(c_|>O1*RhI>fId_Cb#;^}3WNl^Tz5 zJ39i!jy3g4eHVS#bn{;|>!F9B!_YDQOD64W9VqQ<>-a^>8YsB$qsLm}=gl_ggi>+| za53_Ja0rULAEL*_;C=+(#mGBV2Swh$PC=*nYz8@()bJn8hoInoMf)Z2>O<~{IiUx( zJE4Kf(E0NgjCdOfsjpUNehZJ&EEw;&2D*WIbaLKtnh#WbcJ9H*Bjt~leQr)`)a(_+ju(PV*z>d(G8UHF9=QKT7@2jXmKXzw$CAbD&RXpE~o7 z@HGmb@!(jvoepr9kHenOVQ@#N5TE%6;}F9B-;3=(b($Zp{FC`Z^FDJh5vwU=T;fVo;*9jhu5hyXR5$IT440Jjc>&{TxbYl1;K1(iQ>^)YoF{dSdD}q!G3v)C{@7`@Ep8IcANB|5UW8YIEKKzQ)obRiq%JG@l{npqFYZJGI*T6T8zuKJULN807C84n2>DWt}H{AHr z*tc@APoMasq=&tUSn>R9{y_Wej35~>-yNF&S@;L>=Wk}?ADdGW?kFyR@hidpihBBQ zbG8KIq=`7mpI#ClrPbhfg5O?<&*p`Pj92Lv@aU(nk0JVC9+bwYmvLO-G)t6=$Glb& zAMN|qng2kqP%M8uTPvr1r09eF6)kW!+xbD{HO)U~uTjZMVS;}Xd^_X5G8nH-l@o$4 z30G>opYg!Yna7^64f#I|;#-Lf@Zm8FF7Y&n`vdcVmmab8X{|h^dnkR((g-An^pjO; zA^w>uENVix|llv+vLgveXP@?=w`*Z;vH{McK*MyU(YKf?SE`hRG#*c)lE z>=pbD_V#1=bI`_{IJ(<{i$BZvD^;_cI6ZFT&3J{sXfX)>7}p9T;GeVcCLY2g79RhV zWB=tf*gsX={?^JV6G`9GoVNezFM|EG_}@8Gq`&Z_)t%TA!Tu}oSLl?D_j-BRVh}v` zuiuG1dGYGa(}H-7VE2u0#H6!6WnbSLQFEs1K1BqrINe30t0thi5Huy8p+)kI_Ht z=8V3rUfv2Vu^2@DNu@pn-uXrl-&CpNA^eO5*Ye-yS`vS=zX{0q?DIA_Ifo#>Vhwna zzum?g{WliKuh;>eeC0e^44>z4k-uN554VADxAA&>!fm#E@@~a3@PpvLW9Qu_lXt@} zXpWb2CVZD)cAUfdca8Xob!=wb zWM@H4gm+kQ8HaQBaqTX;vAzcJ+U|WeT;o~)uB`#zQ%wFYn?dBW{$1M*ez+KZumC@g z_3v8r5AC<{he{;Rh0K!gdzEpR$NG1@%lZ_t$Dy^-aFqU9cVQ0%ddnt^Nd${acPREVE_P4dMe|@aDeyx>*b40%qGiM6*drPTXkI>&+LH%02{P

Pj7sR~wvz)gJ9tS^8Jg%|z_142mOJU+; z!Ab6^5$}$br+oi!wcrvT3*H7l4gNq-zQ0~PP=K#m!@WAR0Qs>8Go&`x{?=f4>bd;6>9y}>=jx_6O>{dd%# zy?zU^{|@H&Z2B#1GWRgg*4~9#@a^v+?{V6@X+LJK3uYa8+H$vy=facVr@)T|{o}8O z#|!b;f9DYM>9ru<(2G&m*}Vo@4B> z@;v;D*D19}<^k(g@#_d{1uMuyi+i{?E%r^?_D$-%JYnmLKNdd)ev)x|CWz0C)8iI{ z==Uo3vb)i*W;Xpsjpvsav5$GUWFGgx+t9DrJVgEy?C-_?l6A%9w^j~o`6bJW$j6=~ z*f00W?dxlAA095u!%MKgZyNlOp#6S49J0)pd9Vcg@9hIWV&l!c87{=X1O62FCyL=8 zF2FCv{`CJs|E0UYi~cXz^36K;l|uOkmC6jCrT&c9(i2K$ft&p%c{3bHoQ&7fH)%)T z(ua$Wm)y^kb+gpFbCMtL%c_+cK%ej|{{H*8iQ& z9Wwdtc%l4#eD+jDK6yH*kKFT7)1WFN zT3^4Ohy9V%G24#55u z*#DG_NB(`g1rlH0DEb}-e?IpA`84*|+w%2zbX(H(c$|NYQcnYSYK!3SDuTy;jqkAW zX1;E>8AP9_z}F-HfIa^V-o6}{@jm}m@Q1*^XyeU%Jz6OL9i{%F4*Wwl-pKDMz*nyW zKLozp#vA#C`d9A)KMMXG8*k+Qv{3#*rACIqzh&d~uByoXp!x)#MYn+ejqOi^FCzam zpIKLd-&YJ@XzvBp;M>7l*Y*B&(t!f~FW3Tp9Q^le{Z0NmS%}}qGlzrVTWq|(u1VW) z8Q%+@1ux@!vc}Tg*!t@DDhb;+xCDQiX9~oBdH36N{O0Fdu6r)520se^nd14X$*fei=jrFd9Xzv8 z51(~C?&oQH+ajMMUAP~7H~3liY18@&!EDpKge82~|5fq-f!XxaBOSJuM&aZ7?>U(V z+)oK!&-punhqR;Z%Q=ZZcO86v@cmtIe0;8_t>Mvl#{an~(@$_c9FhG@*!1G3UyWz{ zzqXTnZQZxat)CIA1JMWjk2fO!w5^YsZ*N*~EuZoKI&dfNeyGVucDM+B732T)cJz5Z zC_lH#9kDtP{1&CYfxV#(L3~dCH5Q)raMeDZU76lI`f}K#qbeN zRsE0+`xtVgKIHov9AR>;KK!TCZ}ZCDBS-vvra6{+)Fug5()r0IhjwLR32nffUq zxd+e6AJS#1(?|J(8ZPH0x*~_vrT5>?+5`HLruXW-QW>evhi>hZ?*N3@YMSo?7$wZP z{$53}A367*r+PPSgFHN)(p>|%&0~t0>(*br z{>m%YyEWVS5@$;^Yw9mwUt3q3=;*zaYv)XNCY#PiJDl2Vd?4%8CivQ5y3eWQYlQLI zJ<0ysj{ZbX?D9m+(WUNarrW8F-Ji_d-)m?#?bN2@JyEITEEVGmSWa!cdpF;~?v3y6 zj?wOyG)J^I(TT7Wof&LLCgaq0rh4UTJD!>EnaEuC{iO3x`JgYe$~*bBf8sYp{1fOx zUOvA!X8Ku3s(m{1og0tO$4l`%US8hv$2>eJ4e$?YBKLXhqcl)lN8b94SN8iqV#>KZ zyM?lsYjsOd|EsC-^PSJ{k?8$Tv}e#xWP@iKWP*Y4+YpE`d22C9#s{ms<) z_IUFB{Owbnzl-TF{Y$+2@-%<>pf_Hjus3d1gJ6P(ufNpo}+}{XH37 z<-Pg(O0kPq9)F}!8n`?%)LAWh29IF>eKd$~<fvx;YKjQ!{?W}{k;6L*T2UW+BmG4F8bnxrn$GO p+!-gI;pO*V7i@5Reuw)K_Tl4&%1ifaL!@Qn3tru0d2K$w^FQM3%Jl#M From 4ada99ade213ebe62749e8f4987f2491f2f66a44 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 9 Feb 2018 12:25:43 +0000 Subject: [PATCH 15/27] s390x/cpu: expose the guest crash information This patch is the s390 implementation of guest crash information, similar to commit d187e08dc4 ("i386/cpu: add crash-information QOM property") and the related commits. We will detect several crash reasons, with the "disabled wait" being the most important one, since this is used by all s390 guests as a "panic like" notification. Demonstrate these ways with examples as follows. 1. crash-information QOM property; Run qemu with -qmp unix:qmp-sock,server, then use utility "qmp-shell" to execute "qom-get" command, and might get the result like, (QEMU) (QEMU) qom-get path=/machine/unattached/device[0] \ property=crash-information {"return": {"core": 0, "reason": "disabled-wait", "psw-mask": 562956395872256, \ "type": "s390", "psw-addr": 1102832}} 2. GUEST_PANICKED event reporting; Run qemu with a socket option, and telnet or nc to that, -chardev socket,id=qmp,port=4444,host=localhost,server \ -mon chardev=qmp,mode=control,pretty=on \ Negotiating the mode by { "execute": "qmp_capabilities" }, and the crash information will be reported on a guest crash event like, { "timestamp": { "seconds": 1518004739, "microseconds": 552563 }, "event": "GUEST_PANICKED", "data": { "action": "pause", "info": { "core": 0, "psw-addr": 1102832, "reason": "disabled-wait", "psw-mask": 562956395872256, "type": "s390" } } } 3. log; Run qemu with the parameters: -D -d guest_errors, to specify the logfile and log item. The results might be, Guest crashed on cpu 0: disabled-wait PSW: 0x0002000180000000 0x000000000010d3f0 Co-authored-by: Jing Liu Signed-off-by: Christian Borntraeger Message-Id: <20180209122543.25755-1-borntraeger@de.ibm.com> Reviewed-by: Eric Blake [CH: tweaked qapi comment] Signed-off-by: Cornelia Huck --- qapi/run-state.json | 55 +++++++++++++++++++++++++++++++++++++++++-- target/s390x/cpu.c | 45 +++++++++++++++++++++++++++++++++++ target/s390x/cpu.h | 2 ++ target/s390x/helper.c | 5 +++- target/s390x/kvm.c | 15 ++++++------ vl.c | 11 +++++++-- 6 files changed, 120 insertions(+), 13 deletions(-) diff --git a/qapi/run-state.json b/qapi/run-state.json index bca46a8785..92d29fd571 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -320,22 +320,29 @@ # # An enumeration of the guest panic information types # +# @hyper-v: hyper-v guest panic information type +# +# @s390: s390 guest panic information type (Since: 2.12) +# # Since: 2.9 ## { 'enum': 'GuestPanicInformationType', - 'data': [ 'hyper-v'] } + 'data': [ 'hyper-v', 's390' ] } ## # @GuestPanicInformation: # # Information about a guest panic # +# @type: Crash type that defines the hypervisor specific information +# # Since: 2.9 ## {'union': 'GuestPanicInformation', 'base': {'type': 'GuestPanicInformationType'}, 'discriminator': 'type', - 'data': { 'hyper-v': 'GuestPanicInformationHyperV' } } + 'data': { 'hyper-v': 'GuestPanicInformationHyperV', + 's390': 'GuestPanicInformationS390' } } ## # @GuestPanicInformationHyperV: @@ -350,3 +357,47 @@ 'arg3': 'uint64', 'arg4': 'uint64', 'arg5': 'uint64' } } + +## +# @S390CrashReason: +# +# Reason why the CPU is in a crashed state. +# +# @unknown: no crash reason was set +# +# @disabled-wait: the CPU has entered a disabled wait state +# +# @extint-loop: clock comparator or cpu timer interrupt with new PSW enabled +# for external interrupts +# +# @pgmint-loop: program interrupt with BAD new PSW +# +# @opint-loop: operation exception interrupt with invalid code at the program +# interrupt new PSW +# +# Since: 2.12 +## +{ 'enum': 'S390CrashReason', + 'data': [ 'unknown', + 'disabled-wait', + 'extint-loop', + 'pgmint-loop', + 'opint-loop' ] } + +## +# @GuestPanicInformationS390: +# +# S390 specific guest panic information (PSW) +# +# @core: core id of the CPU that crashed +# @psw-mask: control fields of guest PSW +# @psw-addr: guest instruction address +# @reason: guest crash reason +# +# Since: 2.12 +## +{'struct': 'GuestPanicInformationS390', + 'data': { 'core': 'uint32', + 'psw-mask': 'uint64', + 'psw-addr': 'uint64', + 'reason': 'S390CrashReason' } } diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a665b9e60e..3e1be56536 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -35,6 +35,8 @@ #include "qemu/error-report.h" #include "trace.h" #include "qapi/visitor.h" +#include "qapi-visit.h" +#include "sysemu/hw_accel.h" #include "exec/exec-all.h" #include "hw/qdev-properties.h" #ifndef CONFIG_USER_ONLY @@ -228,6 +230,46 @@ out: error_propagate(errp, err); } +static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) +{ + GuestPanicInformation *panic_info; + S390CPU *cpu = S390_CPU(cs); + + cpu_synchronize_state(cs); + panic_info = g_malloc0(sizeof(GuestPanicInformation)); + + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; +#if !defined(CONFIG_USER_ONLY) + panic_info->u.s390.core = cpu->env.core_id; +#else + panic_info->u.s390.core = 0; /* sane default for non system emulation */ +#endif + panic_info->u.s390.psw_mask = cpu->env.psw.mask; + panic_info->u.s390.psw_addr = cpu->env.psw.addr; + panic_info->u.s390.reason = cpu->env.crash_reason; + + return panic_info; +} + +static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occurred"); + return; + } + + panic_info = s390_cpu_get_crash_info(cs); + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + static void s390_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -240,6 +282,8 @@ static void s390_cpu_initfn(Object *obj) cs->env_ptr = env; cs->halted = 1; cs->exception_index = EXCP_HLT; + object_property_add(obj, "crash-information", "GuestPanicInformation", + s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); s390_cpu_model_register_props(obj); #if !defined(CONFIG_USER_ONLY) qemu_get_timedate(&tm, 0); @@ -473,6 +517,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = s390_cpu_do_interrupt; #endif cc->dump_state = s390_cpu_dump_state; + cc->get_crash_info = s390_cpu_get_crash_info; cc->set_pc = s390_cpu_set_pc; cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 96df2fe5c9..09ec8a9b76 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -83,6 +83,8 @@ struct CPUS390XState { PSW psw; + S390CrashReason crash_reason; + uint64_t cc_src; uint64_t cc_dst; uint64_t cc_vr; diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 84aaef3a53..615fa24ab9 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -83,12 +83,15 @@ static inline bool is_special_wait_psw(uint64_t psw_addr) void s390_handle_wait(S390CPU *cpu) { + CPUState *cs = CPU(cpu); + if (s390_cpu_halt(cpu) == 0) { #ifndef CONFIG_USER_ONLY if (is_special_wait_psw(cpu->env.psw.addr)) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } else { - qemu_system_guest_panicked(NULL); + cpu->env.crash_reason = S390_CRASH_REASON_DISABLED_WAIT; + qemu_system_guest_panicked(cpu_get_crash_info(cs)); } #endif } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index e13c8907df..83e4eaf45d 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1541,15 +1541,14 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) return r; } -static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) +static void unmanageable_intercept(S390CPU *cpu, S390CrashReason reason, + int pswoffset) { CPUState *cs = CPU(cpu); - error_report("Unmanageable %s! CPU%i new PSW: 0x%016lx:%016lx", - str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset), - ldq_phys(cs->as, cpu->env.psa + pswoffset + 8)); s390_cpu_halt(cpu); - qemu_system_guest_panicked(NULL); + cpu->env.crash_reason = reason; + qemu_system_guest_panicked(cpu_get_crash_info(cs)); } /* try to detect pgm check loops */ @@ -1579,7 +1578,7 @@ static int handle_oper_loop(S390CPU *cpu, struct kvm_run *run) !(oldpsw.mask & PSW_MASK_PSTATE) && (newpsw.mask & PSW_MASK_ASC) == (oldpsw.mask & PSW_MASK_ASC) && (newpsw.mask & PSW_MASK_DAT) == (oldpsw.mask & PSW_MASK_DAT)) { - unmanageable_intercept(cpu, "operation exception loop", + unmanageable_intercept(cpu, S390_CRASH_REASON_OPINT_LOOP, offsetof(LowCore, program_new_psw)); return EXCP_HALTED; } @@ -1600,12 +1599,12 @@ static int handle_intercept(S390CPU *cpu) r = handle_instruction(cpu, run); break; case ICPT_PROGRAM: - unmanageable_intercept(cpu, "program interrupt", + unmanageable_intercept(cpu, S390_CRASH_REASON_PGMINT_LOOP, offsetof(LowCore, program_new_psw)); r = EXCP_HALTED; break; case ICPT_EXT_INT: - unmanageable_intercept(cpu, "external interrupt", + unmanageable_intercept(cpu, S390_CRASH_REASON_EXTINT_LOOP, offsetof(LowCore, external_new_psw)); r = EXCP_HALTED; break; diff --git a/vl.c b/vl.c index 9e7235df6d..3018433706 100644 --- a/vl.c +++ b/vl.c @@ -1736,7 +1736,7 @@ void qemu_system_reset(ShutdownCause reason) void qemu_system_guest_panicked(GuestPanicInformation *info) { - qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed\n"); + qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed"); if (current_cpu) { current_cpu->crash_occurred = true; @@ -1752,13 +1752,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) if (info) { if (info->type == GUEST_PANIC_INFORMATION_TYPE_HYPER_V) { - qemu_log_mask(LOG_GUEST_ERROR, "HV crash parameters: (%#"PRIx64 + qemu_log_mask(LOG_GUEST_ERROR, "\nHV crash parameters: (%#"PRIx64 " %#"PRIx64" %#"PRIx64" %#"PRIx64" %#"PRIx64")\n", info->u.hyper_v.arg1, info->u.hyper_v.arg2, info->u.hyper_v.arg3, info->u.hyper_v.arg4, info->u.hyper_v.arg5); + } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_S390) { + qemu_log_mask(LOG_GUEST_ERROR, " on cpu %d: %s\n" + "PSW: 0x%016" PRIx64 " 0x%016" PRIx64"\n", + info->u.s390.core, + S390CrashReason_str(info->u.s390.reason), + info->u.s390.psw_mask, + info->u.s390.psw_addr); } qapi_free_GuestPanicInformation(info); } From 571729a00a2ec8514c350dae830a73a09698a043 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 12 Feb 2018 15:49:03 +0000 Subject: [PATCH 16/27] s390/stattrib: Make SaveVMHandlers data static There's no need for this to be dynamic, make it static. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180212154903.8859-1-dgilbert@redhat.com> Reviewed-by: David Hildenbrand Reviewed-by: Thomas Huth Reviewed-by: Claudio Imbrenda Reviewed-by: Richard Henderson Signed-off-by: Cornelia Huck --- hw/s390x/s390-stattrib.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index a1d2135a60..adf07ef312 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -365,22 +365,22 @@ static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, s->migration_enabled = value; } +static SaveVMHandlers savevm_s390_stattrib_handlers = { + .save_setup = cmma_save_setup, + .save_live_iterate = cmma_save_iterate, + .save_live_complete_precopy = cmma_save_complete, + .save_live_pending = cmma_save_pending, + .save_cleanup = cmma_save_cleanup, + .load_state = cmma_load, + .is_active = cmma_active, +}; + static void s390_stattrib_instance_init(Object *obj) { S390StAttribState *sas = S390_STATTRIB(obj); - SaveVMHandlers *ops; - /* ops will always be freed by qemu when unregistering */ - ops = g_new0(SaveVMHandlers, 1); - - ops->save_setup = cmma_save_setup; - ops->save_live_iterate = cmma_save_iterate; - ops->save_live_complete_precopy = cmma_save_complete; - ops->save_live_pending = cmma_save_pending; - ops->save_cleanup = cmma_save_cleanup; - ops->load_state = cmma_load; - ops->is_active = cmma_active; - register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, ops, sas); + register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, + &savevm_s390_stattrib_handlers, sas); object_property_add_bool(obj, "migration-enabled", s390_stattrib_get_migration_enabled, From f26852aa31d49bf83a8defd65538137a2f9da82c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 13 Feb 2018 17:12:40 +0100 Subject: [PATCH 17/27] s390x/tcg: fix disabling/enabling DAT Currently, all memory accesses go via the MMU of the address space (primary, secondary, ...). This is bad, because we don't flush the TLB when disabling/enabling DAT. So we could add a tlb flush. However it is easier to simply select the MMU we already have in place for real memory access. All we have to do is point at the right MMU and allow to execute these pages. Signed-off-by: David Hildenbrand Message-Id: <20180213161240.19891-1-david@redhat.com> Reviewed-by: Richard Henderson [CH: get rid of tabs] Signed-off-by: Cornelia Huck --- target/s390x/cpu.h | 7 ++++++- target/s390x/mmu_helper.c | 2 +- target/s390x/translate.c | 10 +++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 09ec8a9b76..d4641663ef 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -312,11 +312,12 @@ extern const struct VMStateDescription vmstate_s390_cpu; #define FLAG_MASK_PSW_SHIFT 31 #define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT) +#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_PSTATE \ +#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) /* Control register 0 bits */ @@ -340,6 +341,10 @@ extern const struct VMStateDescription vmstate_s390_cpu; static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) { + if (!(env->psw.mask & PSW_MASK_DAT)) { + return MMU_REAL_IDX; + } + switch (env->psw.mask & PSW_MASK_ASC) { case PSW_ASC_PRIMARY: return MMU_PRIMARY_IDX; diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index f477cc006a..23fb2e7501 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -544,7 +544,7 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, { const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT; - *flags = PAGE_READ | PAGE_WRITE; + *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; if (is_low_address(raddr & TARGET_PAGE_MASK) && lowprot_enabled) { /* see comment in mmu_translate() how this works */ *flags |= PAGE_WRITE_INV; diff --git a/target/s390x/translate.c b/target/s390x/translate.c index b470d691d3..5aea3bbca6 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -252,13 +252,17 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc) static int get_mem_index(DisasContext *s) { + if (!(s->tb->flags & FLAG_MASK_DAT)) { + return MMU_REAL_IDX; + } + switch (s->tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: - return 0; + return MMU_PRIMARY_IDX; case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT: - return 1; + return MMU_SECONDARY_IDX; case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT: - return 2; + return MMU_HOME_IDX; default: tcg_abort(); break; From 21fc97c5fff10433d839047da44eee652f050041 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 15 Feb 2018 11:38:22 +0100 Subject: [PATCH 18/27] s390x/tcg: add various alignment checks Let's add proper alignment checks for a handful of instructions that require a SPECIFICATION exception in case alignment is violated. Introduce new wout/in functions. As we are right now only using them for privileged instructions, we have to add ugly ifdefs to silence compilers. Convert STORE CPU ID right away to make use of the wout function. Signed-off-by: David Hildenbrand Message-Id: <20180215103822.15179-1-david@redhat.com> Reviewed-by: Richard Henderson Signed-off-by: Cornelia Huck --- target/s390x/insn-data.def | 16 +++++++------- target/s390x/mem_helper.c | 25 ++++++++++++++++++++++ target/s390x/translate.c | 43 +++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 621e10d615..157619403d 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -1000,13 +1000,13 @@ /* ??? Not implemented - is it necessary? */ C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0) /* SET CLOCK COMPARATOR */ - C(0xb206, SCKC, S, Z, 0, m2_64, 0, 0, sckc, 0) + C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0) /* SET CLOCK PROGRAMMABLE FIELD */ C(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0) /* SET CPU TIMER */ - C(0xb208, SPT, S, Z, 0, m2_64, 0, 0, spt, 0) + C(0xb208, SPT, S, Z, 0, m2_64a, 0, 0, spt, 0) /* SET PREFIX */ - C(0xb210, SPX, S, Z, 0, m2_32u, 0, 0, spx, 0) + C(0xb210, SPX, S, Z, 0, m2_32ua, 0, 0, spx, 0) /* SET PSW KEY FROM ADDRESS */ C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0) /* SET STORAGE KEY EXTENDED */ @@ -1021,20 +1021,20 @@ /* STORE CLOCK EXTENDED */ C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) /* STORE CLOCK COMPARATOR */ - C(0xb207, STCKC, S, Z, la2, 0, new, m1_64, stckc, 0) + C(0xb207, STCKC, S, Z, la2, 0, new, m1_64a, stckc, 0) /* STORE CONTROL */ C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0) C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0) /* STORE CPU ADDRESS */ - C(0xb212, STAP, S, Z, la2, 0, new, m1_16, stap, 0) + C(0xb212, STAP, S, Z, la2, 0, new, m1_16a, stap, 0) /* STORE CPU ID */ - C(0xb202, STIDP, S, Z, la2, 0, new, 0, stidp, 0) + C(0xb202, STIDP, S, Z, la2, 0, new, m1_64a, stidp, 0) /* STORE CPU TIMER */ - C(0xb209, STPT, S, Z, la2, 0, new, m1_64, stpt, 0) + C(0xb209, STPT, S, Z, la2, 0, new, m1_64a, stpt, 0) /* STORE FACILITY LIST */ C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0) /* STORE PREFIX */ - C(0xb211, STPX, S, Z, la2, 0, new, m1_32, stpx, 0) + C(0xb211, STPX, S, Z, la2, 0, new, m1_32a, stpx, 0) /* STORE SYSTEM INFORMATION */ C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0) /* STORE THEN AND SYSTEM MASK */ diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 427b795a78..d5291b246e 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -693,6 +693,11 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uintptr_t ra = GETPC(); int i; + if (a2 & 0x3) { + /* we either came here by lam or lamy, which have different lengths */ + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + } + for (i = r1;; i = (i + 1) % 16) { env->aregs[i] = cpu_ldl_data_ra(env, a2, ra); a2 += 4; @@ -709,6 +714,10 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uintptr_t ra = GETPC(); int i; + if (a2 & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stl_data_ra(env, a2, env->aregs[i], ra); a2 += 4; @@ -1620,6 +1629,10 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t src = a2; uint32_t i; + if (src & 0x7) { + s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + } + for (i = r1;; i = (i + 1) % 16) { uint64_t val = cpu_ldq_data_ra(env, src, ra); if (env->cregs[i] != val && i >= 9 && i <= 11) { @@ -1650,6 +1663,10 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t src = a2; uint32_t i; + if (src & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { uint32_t val = cpu_ldl_data_ra(env, src, ra); if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) { @@ -1677,6 +1694,10 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t dest = a2; uint32_t i; + if (dest & 0x7) { + s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stq_data_ra(env, dest, env->cregs[i], ra); dest += sizeof(uint64_t); @@ -1693,6 +1714,10 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t dest = a2; uint32_t i; + if (dest & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stl_data_ra(env, dest, env->cregs[i], ra); dest += sizeof(uint32_t); diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 5aea3bbca6..7d39ab350d 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -4062,7 +4062,6 @@ static ExitStatus op_stidp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); - tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); return NO_EXIT; } @@ -5220,18 +5219,42 @@ static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_wout_m1_16 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_16a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUW | MO_ALIGN); +} +#define SPEC_wout_m1_16a 0 +#endif + static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_32 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_32a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUL | MO_ALIGN); +} +#define SPEC_wout_m1_32a 0 +#endif + static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_64 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_64a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); +} +#define SPEC_wout_m1_64a 0 +#endif + static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); @@ -5657,6 +5680,15 @@ static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_m2_32u 0 +#ifndef CONFIG_USER_ONLY +static void in2_m2_32ua(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld_tl(o->in2, o->in2, get_mem_index(s), MO_TEUL | MO_ALIGN); +} +#define SPEC_in2_m2_32ua 0 +#endif + static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); @@ -5664,6 +5696,15 @@ static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_m2_64 0 +#ifndef CONFIG_USER_ONLY +static void in2_m2_64a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEQ | MO_ALIGN); +} +#define SPEC_in2_m2_64a 0 +#endif + static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); From 9d0306dfdfb7e7fd8d5fbe45973566d1a8ea592d Mon Sep 17 00:00:00 2001 From: Viktor Mihajlovski Date: Fri, 16 Feb 2018 17:08:37 +0100 Subject: [PATCH 19/27] qmp: expose s390-specific CPU info Presently s390x is the only architecture not exposing specific CPU information via QMP query-cpus. Upstream discussion has shown that it could make sense to report the architecture specific CPU state, e.g. to detect that a CPU has been stopped. With this change the output of query-cpus will look like this on s390: [ {"arch": "s390", "current": true, "props": {"core-id": 0}, "cpu-state": "operating", "CPU": 0, "qom_path": "/machine/unattached/device[0]", "halted": false, "thread_id": 63115}, {"arch": "s390", "current": false, "props": {"core-id": 1}, "cpu-state": "stopped", "CPU": 1, "qom_path": "/machine/unattached/device[1]", "halted": true, "thread_id": 63116} ] This change doesn't add the s390-specific data to HMP 'info cpus'. A follow-on patch will remove all architecture specific information from there. Signed-off-by: Viktor Mihajlovski Reviewed-by: David Hildenbrand Reviewed-by: Christian Borntraeger Reviewed-by: Eric Blake Message-Id: <1518797321-28356-2-git-send-email-mihajlov@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck Signed-off-by: Cornelia Huck --- cpus.c | 6 ++++++ hw/intc/s390_flic.c | 4 ++-- hw/s390x/s390-virtio-ccw.c | 2 +- qapi-schema.json | 28 +++++++++++++++++++++++++++- target/s390x/cpu.c | 24 ++++++++++++------------ target/s390x/cpu.h | 7 ++----- target/s390x/kvm.c | 8 ++++---- target/s390x/sigp.c | 38 +++++++++++++++++++------------------- 8 files changed, 73 insertions(+), 44 deletions(-) diff --git a/cpus.c b/cpus.c index f298b659f4..6006931d3a 100644 --- a/cpus.c +++ b/cpus.c @@ -2100,6 +2100,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_TRICORE) TriCoreCPU *tricore_cpu = TRICORE_CPU(cpu); CPUTriCoreState *env = &tricore_cpu->env; +#elif defined(TARGET_S390X) + S390CPU *s390_cpu = S390_CPU(cpu); + CPUS390XState *env = &s390_cpu->env; #endif cpu_synchronize_state(cpu); @@ -2127,6 +2130,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_TRICORE) info->value->arch = CPU_INFO_ARCH_TRICORE; info->value->u.tricore.PC = env->PC; +#elif defined(TARGET_S390X) + info->value->arch = CPU_INFO_ARCH_S390; + info->value->u.s390.cpu_state = env->cpu_state; #else info->value->arch = CPU_INFO_ARCH_OTHER; #endif diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index a85a149c6d..5f8168f0f0 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -192,8 +192,8 @@ static void qemu_s390_flic_notify(uint32_t type) cs->interrupt_request |= CPU_INTERRUPT_HARD; /* ignore CPUs that are not sleeping */ - if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING && - s390_cpu_get_state(cpu) != CPU_STATE_LOAD) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING && + s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD) { continue; } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4abbe89847..4d0c3deba6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -368,7 +368,7 @@ static void s390_machine_reset(void) /* all cpus are stopped - configure and start the ipl cpu only */ s390_ipl_prepare_cpu(ipl_cpu); - s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, ipl_cpu); } static void s390_machine_device_plug(HotplugHandler *hotplug_dev, diff --git a/qapi-schema.json b/qapi-schema.json index 0262b9f20b..94d560ed13 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -410,10 +410,12 @@ # An enumeration of cpu types that enable additional information during # @query-cpus. # +# @s390: since 2.12 +# # Since: 2.6 ## { 'enum': 'CpuInfoArch', - 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] } + 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] } ## # @CpuInfo: @@ -452,6 +454,7 @@ 'ppc': 'CpuInfoPPC', 'mips': 'CpuInfoMIPS', 'tricore': 'CpuInfoTricore', + 's390': 'CpuInfoS390', 'other': 'CpuInfoOther' } } ## @@ -521,6 +524,29 @@ ## { 'struct': 'CpuInfoOther', 'data': { } } +## +# @CpuS390State: +# +# An enumeration of cpu states that can be assumed by a virtual +# S390 CPU +# +# Since: 2.12 +## +{ 'enum': 'CpuS390State', + 'prefix': 'S390_CPU_STATE', + 'data': [ 'uninitialized', 'stopped', 'check-stop', 'operating', 'load' ] } + +## +# @CpuInfoS390: +# +# Additional information about a virtual S390 CPU +# +# @cpu-state: the virtual CPU's state +# +# Since: 2.12 +## +{ 'struct': 'CpuInfoS390', 'data': { 'cpu-state': 'CpuS390State' } } + ## # @query-cpus: # diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 3e1be56536..f02ed19c70 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -61,8 +61,8 @@ static bool s390_cpu_has_work(CPUState *cs) S390CPU *cpu = S390_CPU(cs); /* STOPPED cpus can never wake up */ - if (s390_cpu_get_state(cpu) != CPU_STATE_LOAD && - s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && + s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { return false; } @@ -80,7 +80,7 @@ static void s390_cpu_load_normal(CPUState *s) S390CPU *cpu = S390_CPU(s); cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR; cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64; - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); } #endif @@ -95,7 +95,7 @@ static void s390_cpu_reset(CPUState *s) env->bpbc = false; scc->parent_reset(s); cpu->env.sigp_order = 0; - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } /* S390CPUClass::initial_reset() */ @@ -138,7 +138,7 @@ static void s390_cpu_full_reset(CPUState *s) scc->parent_reset(s); cpu->env.sigp_order = 0; - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); memset(env, 0, offsetof(CPUS390XState, end_reset_fields)); @@ -292,7 +292,7 @@ static void s390_cpu_initfn(Object *obj) env->tod_basetime = 0; env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); #endif } @@ -320,8 +320,8 @@ static unsigned s390_count_running_cpus(void) CPU_FOREACH(cpu) { uint8_t state = S390_CPU(cpu)->env.cpu_state; - if (state == CPU_STATE_OPERATING || - state == CPU_STATE_LOAD) { + if (state == S390_CPU_STATE_OPERATING || + state == S390_CPU_STATE_LOAD) { if (!disabled_wait(cpu)) { nr_running++; } @@ -360,13 +360,13 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); switch (cpu_state) { - case CPU_STATE_STOPPED: - case CPU_STATE_CHECK_STOP: + case S390_CPU_STATE_STOPPED: + case S390_CPU_STATE_CHECK_STOP: /* halt the cpu for common infrastructure */ s390_cpu_halt(cpu); break; - case CPU_STATE_OPERATING: - case CPU_STATE_LOAD: + case S390_CPU_STATE_OPERATING: + case S390_CPU_STATE_LOAD: /* * Starting a CPU with a PSW WAIT bit set: * KVM: handles this internally and triggers another WAIT exit. diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index d4641663ef..802324b3e2 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -141,12 +141,9 @@ struct CPUS390XState { * architectures, there is a difference between a halt and a stop on s390. * If all cpus are either stopped (including check stop) or in the disabled * wait state, the vm can be shut down. + * The acceptable cpu_state values are defined in the CpuInfoS390State + * enum. */ -#define CPU_STATE_UNINITIALIZED 0x00 -#define CPU_STATE_STOPPED 0x01 -#define CPU_STATE_CHECK_STOP 0x02 -#define CPU_STATE_OPERATING 0x03 -#define CPU_STATE_LOAD 0x04 uint8_t cpu_state; /* currently processed sigp order */ diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 83e4eaf45d..8ef509ece4 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1880,16 +1880,16 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) } switch (cpu_state) { - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: mp_state.mp_state = KVM_MP_STATE_STOPPED; break; - case CPU_STATE_CHECK_STOP: + case S390_CPU_STATE_CHECK_STOP: mp_state.mp_state = KVM_MP_STATE_CHECK_STOP; break; - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: mp_state.mp_state = KVM_MP_STATE_OPERATING; break; - case CPU_STATE_LOAD: + case S390_CPU_STATE_LOAD: mp_state.mp_state = KVM_MP_STATE_LOAD; break; default: diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index ac3f8e7dc2..5a7a9c4cfb 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -46,13 +46,13 @@ static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si) } /* sensing without locks is racy, but it's the same for real hw */ - if (state != CPU_STATE_STOPPED && !ext_call) { + if (state != S390_CPU_STATE_STOPPED && !ext_call) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } else { if (ext_call) { status |= SIGP_STAT_EXT_CALL_PENDING; } - if (state == CPU_STATE_STOPPED) { + if (state == S390_CPU_STATE_STOPPED) { status |= SIGP_STAT_STOPPED; } set_sigp_status(si, status); @@ -94,12 +94,12 @@ static void sigp_start(CPUState *cs, run_on_cpu_data arg) S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg.host_ptr; - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -108,14 +108,14 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg) S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg.host_ptr; - if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } /* disabled wait - sleeping in user space */ if (cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } else { /* execute the stop function */ cpu->env.sigp_order = SIGP_STOP; @@ -130,17 +130,17 @@ static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; /* disabled wait - sleeping in user space */ - if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) { + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; cpu_inject_stop(cpu); /* store will be performed in do_stop_interrup() */ break; - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: /* already stopped, just store the status */ cpu_synchronize_state(cs); s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); @@ -156,7 +156,7 @@ static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) uint32_t address = si->param & 0x7ffffe00u; /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -186,7 +186,7 @@ static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) } /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -229,17 +229,17 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: /* the restart irq has to be delivered prior to any other pending irq */ cpu_synchronize_state(cs); /* * Set OPERATING (and unhalting) before loading the restart PSW. * load_psw() will then properly halt the CPU again if necessary (TCG). */ - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); do_restart_interrupt(&cpu->env); break; - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: cpu_inject_restart(cpu); break; } @@ -285,7 +285,7 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) } /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -318,7 +318,7 @@ static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */ s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */ - if (s390_cpu_get_state(dst_cpu) != CPU_STATE_STOPPED || + if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED || (psw_mask & psw_int_mask) != psw_int_mask || (idle && psw_addr != 0) || (!idle && (asn == p_asn || asn == s_asn))) { @@ -435,7 +435,7 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param, if (cur_cpu == cpu) { continue; } - if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) { all_stopped = false; } } @@ -492,7 +492,7 @@ void do_stop_interrupt(CPUS390XState *env) { S390CPU *cpu = s390_env_get_cpu(env); - if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { + if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { From ce74ee3dea6273a7a6aebc157f39cffd3e388097 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Fri, 16 Feb 2018 17:08:38 +0100 Subject: [PATCH 20/27] qmp: add query-cpus-fast The query-cpus command has an extremely serious side effect: it always interrupts all running vCPUs so that they can run ioctl calls. This can cause a huge performance degradation for some workloads. And most of the information retrieved by the ioctl calls are not even used by query-cpus. This commit introduces a replacement for query-cpus called query-cpus-fast, which has the following features: o Never interrupt vCPUs threads. query-cpus-fast only returns vCPU information maintained by QEMU itself, which should be sufficient for most management software needs o Drop "halted" field as it can not be retrieved in a fast way on most architectures o Drop irrelevant fields such as "current", "pc" and "arch" o Rename some fields for better clarification & proper naming standard Signed-off-by: Luiz Capitulino Signed-off-by: Viktor Mihajlovski Message-Id: <1518797321-28356-3-git-send-email-mihajlov@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck Reviewed-by: Eric Blake Signed-off-by: Cornelia Huck --- cpus.c | 38 ++++++++++++++++++++++++++ qapi-schema.json | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/cpus.c b/cpus.c index 6006931d3a..6df6660ccf 100644 --- a/cpus.c +++ b/cpus.c @@ -2156,6 +2156,44 @@ CpuInfoList *qmp_query_cpus(Error **errp) return head; } +/* + * fast means: we NEVER interrupt vCPU threads to retrieve + * information from KVM. + */ +CpuInfoFastList *qmp_query_cpus_fast(Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + CpuInfoFastList *head = NULL, *cur_item = NULL; + CPUState *cpu; + + CPU_FOREACH(cpu) { + CpuInfoFastList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + + info->value->cpu_index = cpu->cpu_index; + info->value->qom_path = object_get_canonical_path(OBJECT(cpu)); + info->value->thread_id = cpu->thread_id; + + info->value->has_props = !!mc->cpu_index_to_instance_props; + if (info->value->has_props) { + CpuInstanceProperties *props; + props = g_malloc0(sizeof(*props)); + *props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index); + info->value->props = props; + } + + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + return head; +} + void qmp_memsave(int64_t addr, int64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { diff --git a/qapi-schema.json b/qapi-schema.json index 94d560ed13..815f072876 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -552,6 +552,12 @@ # # Returns a list of information about each virtual CPU. # +# This command causes vCPU threads to exit to userspace, which causes +# a small interruption to guest CPU execution. This will have a negative +# impact on realtime guests and other latency sensitive guest workloads. +# It is recommended to use @query-cpus-fast instead of this command to +# avoid the vCPU interruption. +# # Returns: a list of @CpuInfo for each virtual CPU # # Since: 0.14.0 @@ -584,6 +590,70 @@ ## { 'command': 'query-cpus', 'returns': ['CpuInfo'] } +## +# @CpuInfoFast: +# +# Information about a virtual CPU +# +# @cpu-index: index of the virtual CPU +# +# @qom-path: path to the CPU object in the QOM tree +# +# @thread-id: ID of the underlying host thread +# +# @props: properties describing to which node/socket/core/thread +# virtual CPU belongs to, provided if supported by board +# +# Since: 2.12 +# +## +{ 'struct': 'CpuInfoFast', + 'data': {'cpu-index': 'int', 'qom-path': 'str', + 'thread-id': 'int', '*props': 'CpuInstanceProperties' } } + +## +# @query-cpus-fast: +# +# Returns information about all virtual CPUs. This command does not +# incur a performance penalty and should be used in production +# instead of query-cpus. +# +# Returns: list of @CpuInfoFast +# +# Notes: The CPU architecture name is not returned by query-cpus-fast. +# Use query-target to retrieve that information. +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-cpus-fast" } +# <- { "return": [ +# { +# "thread-id": 25627, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 0 +# }, +# "qom-path": "/machine/unattached/device[0]", +# "cpu-index": 0 +# }, +# { +# "thread-id": 25628, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 1 +# }, +# "qom-path": "/machine/unattached/device[2]", +# "cpu-index": 1 +# } +# ] +# } +## +{ 'command': 'query-cpus-fast', 'returns': [ 'CpuInfoFast' ] } + ## # @IOThreadInfo: # From ca230ff33f89bf7102cbfbc2328716da6750aaed Mon Sep 17 00:00:00 2001 From: Viktor Mihajlovski Date: Fri, 16 Feb 2018 17:08:39 +0100 Subject: [PATCH 21/27] qmp: add architecture specific cpu data for query-cpus-fast The s390 CPU state can be retrieved without interrupting the VM execution. Extendend the CpuInfoFast union with architecture specific data and an implementation for s390. Return data looks like this: [ {"thread-id":64301,"props":{"core-id":0}, "arch":"s390","cpu-state":"operating", "qom-path":"/machine/unattached/device[0]","cpu-index":0}, {"thread-id":64302,"props":{"core-id":1}, "arch":"s390","cpu-state":"operating", "qom-path":"/machine/unattached/device[1]","cpu-index":1} ] Signed-off-by: Viktor Mihajlovski Reviewed-by: Cornelia Huck Reviewed-by: Eric Blake Message-Id: <1518797321-28356-4-git-send-email-mihajlov@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck --- cpus.c | 10 ++++++++++ qapi-schema.json | 25 ++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/cpus.c b/cpus.c index 6df6660ccf..af678264f6 100644 --- a/cpus.c +++ b/cpus.c @@ -2166,6 +2166,10 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, *cur_item = NULL; CPUState *cpu; +#if defined(TARGET_S390X) + S390CPU *s390_cpu; + CPUS390XState *env; +#endif CPU_FOREACH(cpu) { CpuInfoFastList *info = g_malloc0(sizeof(*info)); @@ -2183,6 +2187,12 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) info->value->props = props; } +#if defined(TARGET_S390X) + s390_cpu = S390_CPU(cpu); + env = &s390_cpu->env; + info->value->arch = CPU_INFO_ARCH_S390; + info->value->u.s390.cpu_state = env->cpu_state; +#endif if (!cur_item) { head = cur_item = info; } else { diff --git a/qapi-schema.json b/qapi-schema.json index 815f072876..e6ca63f718 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -408,7 +408,7 @@ # @CpuInfoArch: # # An enumeration of cpu types that enable additional information during -# @query-cpus. +# @query-cpus and @query-cpus-fast. # # @s390: since 2.12 # @@ -604,12 +604,24 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # +# @arch: architecture of the cpu, which determines which additional fields +# will be listed +# # Since: 2.12 # ## -{ 'struct': 'CpuInfoFast', - 'data': {'cpu-index': 'int', 'qom-path': 'str', - 'thread-id': 'int', '*props': 'CpuInstanceProperties' } } +{ 'union': 'CpuInfoFast', + 'base': {'cpu-index': 'int', 'qom-path': 'str', + 'thread-id': 'int', '*props': 'CpuInstanceProperties', + 'arch': 'CpuInfoArch' }, + 'discriminator': 'arch', + 'data': { 'x86': 'CpuInfoOther', + 'sparc': 'CpuInfoOther', + 'ppc': 'CpuInfoOther', + 'mips': 'CpuInfoOther', + 'tricore': 'CpuInfoOther', + 's390': 'CpuInfoS390', + 'other': 'CpuInfoOther' } } ## # @query-cpus-fast: @@ -620,9 +632,6 @@ # # Returns: list of @CpuInfoFast # -# Notes: The CPU architecture name is not returned by query-cpus-fast. -# Use query-target to retrieve that information. -# # Since: 2.12 # # Example: @@ -637,6 +646,7 @@ # "socket-id": 0 # }, # "qom-path": "/machine/unattached/device[0]", +# "arch":"x86", # "cpu-index": 0 # }, # { @@ -647,6 +657,7 @@ # "socket-id": 1 # }, # "qom-path": "/machine/unattached/device[2]", +# "arch":"x86", # "cpu-index": 1 # } # ] From ff9a915655326878e1be650df644b7de6739f0fa Mon Sep 17 00:00:00 2001 From: Viktor Mihajlovski Date: Fri, 16 Feb 2018 17:08:40 +0100 Subject: [PATCH 22/27] qemu-doc: deprecate query-cpus Start the deprecation period for QAPI query-cpus (replaced by query-cpus-fast) beginning with 2.12.0. Signed-off-by: Viktor Mihajlovski Reviewed-by: Eric Blake Message-Id: <1518797321-28356-5-git-send-email-mihajlov@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck Signed-off-by: Cornelia Huck --- qapi-schema.json | 4 ++++ qemu-doc.texi | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/qapi-schema.json b/qapi-schema.json index e6ca63f718..cd98a94388 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -587,6 +587,10 @@ # ] # } # +# Notes: This interface is deprecated (since 2.12.0), and it is strongly +# recommended that you avoid using it. Use @query-cpus-fast to +# obtain information about virtual CPUs. +# ## { 'command': 'query-cpus', 'returns': ['CpuInfo'] } diff --git a/qemu-doc.texi b/qemu-doc.texi index 8e3556976b..b3c2763624 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2762,6 +2762,10 @@ by the ``convert -l snapshot_param'' argument instead. "autoload" parameter is now ignored. All bitmaps are automatically loaded from qcow2 images. +@subsection query-cpus (since 2.12.0) + +The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. + @section System emulator human monitor commands @subsection host_net_add (since 2.10.0) From 137b5cb6ab565cb3781d5337591e155932b4230e Mon Sep 17 00:00:00 2001 From: Viktor Mihajlovski Date: Fri, 16 Feb 2018 17:08:41 +0100 Subject: [PATCH 23/27] hmp: change hmp_info_cpus to use query-cpus-fast Changing the implementation of hmp_info_cpus() to call qmp_query_cpus_fast() instead of qmp_query_cpus. This has the following consequences: o No further code change required for qmp_query_cpus deprecation o HMP profits from the less disruptive cpu information retrieval o HMP 'info cpus' won't display architecture specific data anymore, which should be tolerable in the light of the deprecation of query-cpus. In order to allow 'info cpus' to be executed completely on the fast path, monitor_get_cpu_index() has been adapted to not synchronize the cpu state. Signed-off-by: Viktor Mihajlovski Message-Id: <1518797321-28356-6-git-send-email-mihajlov@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck Reviewed-by: Eric Blake Signed-off-by: Cornelia Huck --- hmp.c | 41 +++++++---------------------------------- monitor.c | 13 ++++++++++--- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/hmp.c b/hmp.c index 7870d6a300..ae86bfbade 100644 --- a/hmp.c +++ b/hmp.c @@ -360,50 +360,23 @@ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict) void hmp_info_cpus(Monitor *mon, const QDict *qdict) { - CpuInfoList *cpu_list, *cpu; + CpuInfoFastList *cpu_list, *cpu; - cpu_list = qmp_query_cpus(NULL); + cpu_list = qmp_query_cpus_fast(NULL); for (cpu = cpu_list; cpu; cpu = cpu->next) { int active = ' '; - if (cpu->value->CPU == monitor_get_cpu_index()) { + if (cpu->value->cpu_index == monitor_get_cpu_index()) { active = '*'; } - monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU); - - switch (cpu->value->arch) { - case CPU_INFO_ARCH_X86: - monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc); - break; - case CPU_INFO_ARCH_PPC: - monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip); - break; - case CPU_INFO_ARCH_SPARC: - monitor_printf(mon, " pc=0x%016" PRIx64, - cpu->value->u.q_sparc.pc); - monitor_printf(mon, " npc=0x%016" PRIx64, - cpu->value->u.q_sparc.npc); - break; - case CPU_INFO_ARCH_MIPS: - monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC); - break; - case CPU_INFO_ARCH_TRICORE: - monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC); - break; - default: - break; - } - - if (cpu->value->halted) { - monitor_printf(mon, " (halted)"); - } - - monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id); + monitor_printf(mon, "%c CPU #%" PRId64 ":", active, + cpu->value->cpu_index); + monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id); } - qapi_free_CpuInfoList(cpu_list); + qapi_free_CpuInfoFastList(cpu_list); } static void print_block_info(Monitor *mon, BlockInfo *info, diff --git a/monitor.c b/monitor.c index 373bb8d1c3..308a3d9b78 100644 --- a/monitor.c +++ b/monitor.c @@ -1055,7 +1055,7 @@ int monitor_set_cpu(int cpu_index) return 0; } -CPUState *mon_get_cpu(void) +static CPUState *mon_get_cpu_sync(bool synchronize) { CPUState *cpu; @@ -1074,10 +1074,17 @@ CPUState *mon_get_cpu(void) monitor_set_cpu(first_cpu->cpu_index); cpu = first_cpu; } - cpu_synchronize_state(cpu); + if (synchronize) { + cpu_synchronize_state(cpu); + } return cpu; } +CPUState *mon_get_cpu(void) +{ + return mon_get_cpu_sync(true); +} + CPUArchState *mon_get_cpu_env(void) { CPUState *cs = mon_get_cpu(); @@ -1087,7 +1094,7 @@ CPUArchState *mon_get_cpu_env(void) int monitor_get_cpu_index(void) { - CPUState *cs = mon_get_cpu(); + CPUState *cs = mon_get_cpu_sync(false); return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; } From a5a2b80d9538f5ca7b91ac201f1466ef58cf833f Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Wed, 21 Feb 2018 17:56:28 +0100 Subject: [PATCH 24/27] s390x/cpumodel: document S390FeatDef.bit not applicable The 'bit' field of the 'S390FeatDef' structure is not applicable to all its instances. Currently this field is not applicable, and remains unused, iff the feature is of type S390_FEAT_TYPE_MISC. Having the value 0 specified for multiple such feature definitions was a little confusing, as it's a perfectly legit bit value, and as the value of the bit field is usually ought to be unique for each feature of a given feature type. Let us introduce a specialized macro for defining features of type S390_FEAT_TYPE_MISC so, that one does not have to specify neither bit nor type (as the latter is implied). Signed-off-by: Halil Pasic Message-Id: <20180221165628.78946-1-pasic@linux.vnet.ibm.com> Reviewed-by: David Hildenbrand Acked-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- target/s390x/cpu_features.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index a5619f2893..3b9e2745e9 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -23,6 +23,10 @@ .desc = _desc, \ } +/* S390FeatDef.bit is not applicable as there is no feature block. */ +#define FEAT_INIT_MISC(_name, _desc) \ + FEAT_INIT(_name, S390_FEAT_TYPE_MISC, 0, _desc) + /* indexed by feature number for easy lookup */ static const S390FeatDef s390_features[] = { FEAT_INIT("esan3", S390_FEAT_TYPE_STFL, 0, "Instructions marked as n3"), @@ -123,8 +127,8 @@ static const S390FeatDef s390_features[] = { FEAT_INIT("ib", S390_FEAT_TYPE_SCLP_CPU, 42, "SIE: Intervention bypass facility"), FEAT_INIT("cei", S390_FEAT_TYPE_SCLP_CPU, 43, "SIE: Conditional-external-interception facility"), - FEAT_INIT("dateh2", S390_FEAT_TYPE_MISC, 0, "DAT-enhancement facility 2"), - FEAT_INIT("cmm", S390_FEAT_TYPE_MISC, 0, "Collaborative-memory-management facility"), + FEAT_INIT_MISC("dateh2", "DAT-enhancement facility 2"), + FEAT_INIT_MISC("cmm", "Collaborative-memory-management facility"), FEAT_INIT("plo-cl", S390_FEAT_TYPE_PLO, 0, "PLO Compare and load (32 bit in general registers)"), FEAT_INIT("plo-clg", S390_FEAT_TYPE_PLO, 1, "PLO Compare and load (64 bit in parameter list)"), From 82fab5c5b90e468f3e9d54cd614c89d9416d18cd Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 19 Feb 2018 18:42:31 +0100 Subject: [PATCH 25/27] s390x/sclp: remove memory hotplug support From an architecture point of view, nothing can be mapped into the address space on s390x. All there is is memory. Therefore there is also not really an interface to communicate such information to the guest. All we can do is specify the maximum ram address and guests can probe in that range if memory is available and usable (TPROT). Also memory hotplug is strange. The guest can decide at some point in time to add / remove memory in some range. While the hypervisor can deny to online an increment, all increments have to be predefined and there is no way of telling the guest about a newly "hotplugged" increment. So if we specify right now e.g. -m 2G,slots=2,maxmem=20G An ordinary fedora guest will happily online (hotplug) all memory, resulting in a guest consuming 20G. So it really behaves rather like -m 22G There is no way to hotplug memory from the outside like on other architectures. This is of course bad for upper management layers. As the guest can create/delete memory regions while it is running, of course migration support is not available and tricky to implement. With virtualization, it is different. We might want to map something into guest address space (e.g. fake DAX devices) and not detect it automatically as memory. So we really want to use the maxmem and slots parameter just like on all other architectures. Such devices will have to expose the applicable memory range themselves. To finally be able to provide memory hotplug to guests, we will need a new paravirtualized interface to do that (e.g. something into the direction of virtio-mem). This implies, that maxmem cannot be used for s390x memory hotplug anymore and has to go. This simplifies the code quite a bit. As migration support is not working, this change cannot really break migration as guests without slots and maxmem don't see the SCLP features. Also, the ram size calculation does not change. Signed-off-by: David Hildenbrand Message-Id: <20180219174231.10874-1-david@redhat.com> Reviewed-by: Christian Borntraeger Acked-by: Matthew Rosato [CH: tweaked patch description, as discussed on list] Signed-off-by: Cornelia Huck --- hw/s390x/sclp.c | 310 +--------------------------------------- include/hw/s390x/sclp.h | 25 ---- target/s390x/cpu.h | 1 - 3 files changed, 5 insertions(+), 331 deletions(-) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 276972b59f..047d577313 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -15,9 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/memory.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" @@ -57,10 +55,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) { ReadInfo *read_info = (ReadInfo *) sccb; MachineState *machine = MACHINE(qdev_get_machine()); - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); int cpu_count; int rnsize, rnmax; - int slots = MIN(machine->ram_slots, s390_get_memslot_count()); IplParameterBlock *ipib = s390_ipl_get_iplb(); /* CPU information */ @@ -80,36 +76,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | SCLP_HAS_IOA_RECONFIG); - /* Memory Hotplug is only supported for the ccw machine type */ - if (mhd) { - mhd->standby_subregion_size = MEM_SECTION_SIZE; - /* Deduct the memory slot already used for core */ - if (slots > 0) { - while ((mhd->standby_subregion_size * (slots - 1) - < mhd->standby_mem_size)) { - mhd->standby_subregion_size = mhd->standby_subregion_size << 1; - } - } - /* - * Initialize mapping of guest standby memory sections indicating which - * are and are not online. Assume all standby memory begins offline. - */ - if (mhd->standby_state_map == 0) { - if (mhd->standby_mem_size % mhd->standby_subregion_size) { - mhd->standby_state_map = g_malloc0((mhd->standby_mem_size / - mhd->standby_subregion_size + 1) * - (mhd->standby_subregion_size / - MEM_SECTION_SIZE)); - } else { - mhd->standby_state_map = g_malloc0(mhd->standby_mem_size / - MEM_SECTION_SIZE); - } - } - mhd->padded_ram_size = ram_size + mhd->pad_size; - mhd->rzm = 1 << mhd->increment_size; - - read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); - } read_info->mha_pow = s390_get_mha_pow(); read_info->hmfai = cpu_to_be32(s390_get_hmfai()); @@ -121,7 +87,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } - rnmax = machine->maxram_size >> sclp->increment_size; + /* we don't support standby memory, maxram_size is never exposed */ + rnmax = machine->ram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); } else { @@ -139,195 +106,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb) -{ - int i, assigned; - int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID; - ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if ((ram_size >> mhd->increment_size) >= 0x10000) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); - return; - } - - /* Return information regarding core memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - assigned = ram_size >> mhd->increment_size; - storage_info->assigned = cpu_to_be16(assigned); - - for (i = 0; i < assigned; i++) { - storage_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); -} - -static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb) -{ - ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); - return; - } - - /* Return information regarding standby memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - storage_info->standby = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); -} - -static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb, - uint16_t element) -{ - int i, assigned, subincrement_id; - AttachStorageElement *attach_info = (AttachStorageElement *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if (element != 1) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - assigned = mhd->standby_mem_size >> mhd->increment_size; - attach_info->assigned = cpu_to_be16(assigned); - subincrement_id = ((ram_size >> mhd->increment_size) << 16) - + SCLP_STARTING_SUBINCREMENT_ID; - for (i = 0; i < assigned; i++) { - attach_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -static void assign_storage(SCLPDevice *sclp, SCCB *sccb) -{ - MemoryRegion *mr = NULL; - uint64_t this_subregion_size; - AssignStorage *assign_info = (AssignStorage *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - ram_addr_t assign_addr; - MemoryRegion *sysmem = get_system_memory(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - assign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm; - - if ((assign_addr % MEM_SECTION_SIZE == 0) && - (assign_addr >= mhd->padded_ram_size)) { - /* Re-use existing memory region if found */ - mr = memory_region_find(sysmem, assign_addr, 1).mr; - memory_region_unref(mr); - if (!mr) { - - MemoryRegion *standby_ram = g_new(MemoryRegion, 1); - - /* offset to align to standby_subregion_size for allocation */ - ram_addr_t offset = assign_addr - - (assign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size; - - /* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) + NULL */ - char id[16]; - snprintf(id, 16, "standby.ram%d", - (int)((offset - mhd->padded_ram_size) / - mhd->standby_subregion_size) + 1); - - /* Allocate a subregion of the calculated standby_subregion_size */ - if (offset + mhd->standby_subregion_size > - mhd->padded_ram_size + mhd->standby_mem_size) { - this_subregion_size = mhd->padded_ram_size + - mhd->standby_mem_size - offset; - } else { - this_subregion_size = mhd->standby_subregion_size; - } - - memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, - &error_fatal); - /* This is a hack to make memory hotunplug work again. Once we have - * subdevices, we have to unparent them when unassigning memory, - * instead of doing it via the ref count of the MemoryRegion. */ - object_ref(OBJECT(standby_ram)); - object_unparent(OBJECT(standby_ram)); - memory_region_add_subregion(sysmem, offset, standby_ram); - } - /* The specified subregion is no longer in standby */ - mhd->standby_state_map[(assign_addr - mhd->padded_ram_size) - / MEM_SECTION_SIZE] = 1; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -static void unassign_storage(SCLPDevice *sclp, SCCB *sccb) -{ - MemoryRegion *mr = NULL; - AssignStorage *assign_info = (AssignStorage *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - ram_addr_t unassign_addr; - MemoryRegion *sysmem = get_system_memory(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - unassign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm; - - /* if the addr is a multiple of 256 MB */ - if ((unassign_addr % MEM_SECTION_SIZE == 0) && - (unassign_addr >= mhd->padded_ram_size)) { - mhd->standby_state_map[(unassign_addr - - mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0; - - /* find the specified memory region and destroy it */ - mr = memory_region_find(sysmem, unassign_addr, 1).mr; - memory_region_unref(mr); - if (mr) { - int i; - int is_removable = 1; - ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size - - (unassign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size); - /* Mark all affected subregions as 'standby' once again */ - for (i = 0; - i < (mhd->standby_subregion_size / MEM_SECTION_SIZE); - i++) { - - if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) { - is_removable = 0; - break; - } - } - if (is_removable) { - memory_region_del_subregion(sysmem, mr); - object_unref(OBJECT(mr)); - } - } - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - /* Provide information about the CPU */ static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) { @@ -390,22 +168,6 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) case SCLP_CMDW_READ_CPU_INFO: sclp_c->read_cpu_info(sclp, sccb); break; - case SCLP_READ_STORAGE_ELEMENT_INFO: - if (code & 0xff00) { - sclp_c->read_storage_element1_info(sclp, sccb); - } else { - sclp_c->read_storage_element0_info(sclp, sccb); - } - break; - case SCLP_ATTACH_STORAGE_ELEMENT: - sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8); - break; - case SCLP_ASSIGN_STORAGE: - sclp_c->assign_storage(sclp, sccb); - break; - case SCLP_UNASSIGN_STORAGE: - sclp_c->unassign_storage(sclp, sccb); - break; case SCLP_CMDW_CONFIGURE_IOA: sclp_configure_io_adapter(sclp, sccb, true); break; @@ -540,9 +302,6 @@ static void sclp_memory_init(SCLPDevice *sclp) { MachineState *machine = MACHINE(qdev_get_machine()); ram_addr_t initial_mem = machine->ram_size; - ram_addr_t max_mem = machine->maxram_size; - ram_addr_t standby_mem = max_mem - initial_mem; - ram_addr_t pad_mem = 0; int increment_size = 20; /* The storage increment size is a multiple of 1M and is a power of 2. @@ -552,34 +311,14 @@ static void sclp_memory_init(SCLPDevice *sclp) while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { increment_size++; } - if (machine->ram_slots) { - while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - } sclp->increment_size = increment_size; - /* The core and standby memory areas need to be aligned with - * the increment size. In effect, this can cause the - * user-specified memory size to be rounded down to align - * with the nearest increment boundary. */ + /* The core memory area needs to be aligned with the increment size. + * In effect, this can cause the user-specified memory size to be rounded + * down to align with the nearest increment boundary. */ initial_mem = initial_mem >> increment_size << increment_size; - standby_mem = standby_mem >> increment_size << increment_size; - /* If the size of ram is not on a MEM_SECTION_SIZE boundary, - calculate the pad size necessary to force this boundary. */ - if (machine->ram_slots && standby_mem) { - sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); - - if (initial_mem % MEM_SECTION_SIZE) { - pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE; - } - mhd->increment_size = increment_size; - mhd->pad_size = pad_mem; - mhd->standby_mem_size = standby_mem; - } machine->ram_size = initial_mem; - machine->maxram_size = initial_mem + pad_mem + standby_mem; /* let's propagate the changed ram size into the global variable. */ ram_size = initial_mem; } @@ -613,11 +352,6 @@ static void sclp_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; sc->read_SCP_info = read_SCP_info; - sc->read_storage_element0_info = read_storage_element0_info; - sc->read_storage_element1_info = read_storage_element1_info; - sc->attach_storage_element = attach_storage_element; - sc->assign_storage = assign_storage; - sc->unassign_storage = unassign_storage; sc->read_cpu_info = sclp_read_cpu_info; sc->execute = sclp_execute; sc->service_interrupt = service_interrupt; @@ -632,42 +366,8 @@ static TypeInfo sclp_info = { .class_size = sizeof(SCLPDeviceClass), }; -sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void) -{ - DeviceState *dev; - dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV); - object_property_add_child(qdev_get_machine(), - TYPE_SCLP_MEMORY_HOTPLUG_DEV, - OBJECT(dev), NULL); - qdev_init_nofail(dev); - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void) -{ - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -static void sclp_memory_hotplug_dev_class_init(ObjectClass *klass, - void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static TypeInfo sclp_memory_hotplug_dev_info = { - .name = TYPE_SCLP_MEMORY_HOTPLUG_DEV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(sclpMemoryHotplugDev), - .class_init = sclp_memory_hotplug_dev_class_init, -}; - static void register_types(void) { - type_register_static(&sclp_memory_hotplug_dev_info); type_register_static(&sclp_info); } type_init(register_types); diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 847ff32f85..476cbf78f2 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -202,12 +202,6 @@ typedef struct SCLPDeviceClass { /* private */ DeviceClass parent_class; void (*read_SCP_info)(SCLPDevice *sclp, SCCB *sccb); - void (*read_storage_element0_info)(SCLPDevice *sclp, SCCB *sccb); - void (*read_storage_element1_info)(SCLPDevice *sclp, SCCB *sccb); - void (*attach_storage_element)(SCLPDevice *sclp, SCCB *sccb, - uint16_t element); - void (*assign_storage)(SCLPDevice *sclp, SCCB *sccb); - void (*unassign_storage)(SCLPDevice *sclp, SCCB *sccb); void (*read_cpu_info)(SCLPDevice *sclp, SCCB *sccb); /* public */ @@ -215,23 +209,6 @@ typedef struct SCLPDeviceClass { void (*service_interrupt)(SCLPDevice *sclp, uint32_t sccb); } SCLPDeviceClass; -typedef struct sclpMemoryHotplugDev sclpMemoryHotplugDev; - -#define TYPE_SCLP_MEMORY_HOTPLUG_DEV "sclp-memory-hotplug-dev" -#define SCLP_MEMORY_HOTPLUG_DEV(obj) \ - OBJECT_CHECK(sclpMemoryHotplugDev, (obj), TYPE_SCLP_MEMORY_HOTPLUG_DEV) - -struct sclpMemoryHotplugDev { - SysBusDevice parent; - ram_addr_t standby_mem_size; - ram_addr_t padded_ram_size; - ram_addr_t pad_size; - ram_addr_t standby_subregion_size; - ram_addr_t rzm; - int increment_size; - char *standby_state_map; -}; - static inline int sccb_data_len(SCCB *sccb) { return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); @@ -239,8 +216,6 @@ static inline int sccb_data_len(SCCB *sccb) void s390_sclp_init(void); -sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void); -sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void); void sclp_service_interrupt(uint32_t sccb); void raise_irq_cpu_hotplug(void); int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 802324b3e2..5bd6de7e8e 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -622,7 +622,6 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096); #define SIGP_ORDER_MASK 0x000000ff /* from s390-virtio-ccw */ -#define MEM_SECTION_SIZE 0x10000000UL #define MAX_AVAIL_SLOTS 32 /* machine check interruption code */ From 3e65a3c283ae36c1f27141f71d2bb040737b1390 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 23 Feb 2018 11:13:45 +0100 Subject: [PATCH 26/27] s390x: remove s390_get_memslot_count Not needed anymore after removal of the memory hotplug code. Acked-by: Christian Borntraeger Reviewed-by: David Hildenbrand Signed-off-by: Cornelia Huck --- include/hw/s390x/sclp.h | 1 - target/s390x/cpu.c | 9 --------- target/s390x/cpu.h | 4 ---- target/s390x/kvm-stub.c | 5 ----- target/s390x/kvm.c | 5 ----- target/s390x/kvm_s390x.h | 1 - 6 files changed, 25 deletions(-) diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 476cbf78f2..f9db243484 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -35,7 +35,6 @@ #define SCLP_FC_ASSIGN_ATTACH_READ_STOR 0xE00000000000ULL #define SCLP_STARTING_SUBINCREMENT_ID 0x10001 #define SCLP_INCREMENT_UNIT 0x10000 -#define MAX_AVAIL_SLOTS 32 #define MAX_STORAGE_INCREMENTS 1020 /* CPU hotplug SCLP codes */ diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index f02ed19c70..627002b225 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -437,15 +437,6 @@ void s390_cmma_reset(void) } } -int s390_get_memslot_count(void) -{ - if (kvm_enabled()) { - return kvm_s390_get_memslot_count(); - } else { - return MAX_AVAIL_SLOTS; - } -} - int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, int vq, bool assign) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 5bd6de7e8e..c5ef930876 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -621,9 +621,6 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096); /* SIGP order code mask corresponding to bit positions 56-63 */ #define SIGP_ORDER_MASK 0x000000ff -/* from s390-virtio-ccw */ -#define MAX_AVAIL_SLOTS 32 - /* machine check interruption code */ /* subclasses */ @@ -695,7 +692,6 @@ int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low); int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low); void s390_crypto_reset(void); bool s390_get_squash_mcss(void); -int s390_get_memslot_count(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c index 8cdcf83845..29b10542cc 100644 --- a/target/s390x/kvm-stub.c +++ b/target/s390x/kvm-stub.c @@ -84,11 +84,6 @@ void kvm_s390_cmma_reset(void) { } -int kvm_s390_get_memslot_count(void) -{ - return MAX_AVAIL_SLOTS; -} - void kvm_s390_reset_vcpu(S390CPU *cpu) { } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 8ef509ece4..656aaea2cd 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1854,11 +1854,6 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); } -int kvm_s390_get_memslot_count(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_NR_MEMSLOTS); -} - int kvm_s390_get_ri(void) { return cap_ri; diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index 7a3b862eea..c383bf4ee9 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -30,7 +30,6 @@ int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); void kvm_s390_enable_css_support(S390CPU *cpu); int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); -int kvm_s390_get_memslot_count(void); int kvm_s390_cmma_active(void); void kvm_s390_cmma_reset(void); void kvm_s390_reset_vcpu(S390CPU *cpu); From be8b49de2491145a7aad3201803327b6f568cbd3 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 1 Mar 2018 13:08:26 +0100 Subject: [PATCH 27/27] s390x/tcg: fix loading 31bit PSWs with the highest bit set Let's also put the 31-bit hack in front of the REAL MMU, otherwise right now we get errors when loading a PSW where the highest bit is set (e.g. via s390-netboot.img). The highest bit is not masked away, therefore we inject addressing exceptions into the guest. The proper fix will later be to do all address wrapping before accessing the MMU - so we won't get any "wrong" entries in there (which makes flushing also easier). But that will require more work (wrapping in load_psw, wrapping when incrementing the PC, wrapping every memory access). This fixes the tests/pxe-test test. Signed-off-by: David Hildenbrand Message-Id: <20180301120826.6847-1-david@redhat.com> Signed-off-by: Cornelia Huck --- target/s390x/excp_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index 411051edc3..dfee221111 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -107,6 +107,10 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, return 1; } } else if (mmu_idx == MMU_REAL_IDX) { + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + vaddr &= 0x7fffffff; + } if (mmu_translate_real(env, vaddr, rw, &raddr, &prot)) { return 1; }