diff options
Diffstat (limited to 'qemu/pc-bios/s390-ccw')
-rw-r--r-- | qemu/pc-bios/s390-ccw/Makefile | 28 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/bootmap.c | 746 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/bootmap.h | 541 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/cio.h | 342 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/main.c | 111 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/s390-ccw.h | 192 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/sclp-ascii.c | 82 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/sclp.h | 107 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/scsi.h | 184 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/start.S | 65 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/virtio-scsi.c | 342 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/virtio-scsi.h | 72 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/virtio.c | 608 | ||||
-rw-r--r-- | qemu/pc-bios/s390-ccw/virtio.h | 292 |
14 files changed, 0 insertions, 3712 deletions
diff --git a/qemu/pc-bios/s390-ccw/Makefile b/qemu/pc-bios/s390-ccw/Makefile deleted file mode 100644 index 4208cb429..000000000 --- a/qemu/pc-bios/s390-ccw/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -all: build-all -# Dummy command so that make thinks it has done something - @true - -include ../../config-host.mak -include $(SRC_PATH)/rules.mak - -$(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) - -.PHONY : all clean build-all - -OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o virtio-scsi.o -CFLAGS += -fPIE -fno-stack-protector -ffreestanding -march=z900 -CFLAGS += -fno-delete-null-pointer-checks -msoft-float -LDFLAGS += -Wl,-pie -nostdlib - -build-all: s390-ccw.img - -s390-ccw.elf: $(OBJECTS) - $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS)," Building $(TARGET_DIR)$@") - -s390-ccw.img: s390-ccw.elf - $(call quiet-command,strip --strip-unneeded $< -o $@," Stripping $(TARGET_DIR)$@") - -$(OBJECTS): Makefile - -clean: - rm -f *.o *.d *.img *.elf *~ diff --git a/qemu/pc-bios/s390-ccw/bootmap.c b/qemu/pc-bios/s390-ccw/bootmap.c deleted file mode 100644 index 611102e3e..000000000 --- a/qemu/pc-bios/s390-ccw/bootmap.c +++ /dev/null @@ -1,746 +0,0 @@ -/* - * QEMU S390 bootmap interpreter - * - * Copyright (c) 2009 Alexander Graf <agraf@suse.de> - * - * 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 "s390-ccw.h" -#include "bootmap.h" -#include "virtio.h" - -#ifdef DEBUG -/* #define DEBUG_FALLBACK */ -#endif - -#ifdef DEBUG_FALLBACK -#define dputs(txt) \ - do { sclp_print("zipl: " txt); } while (0) -#else -#define dputs(fmt, ...) \ - do { } while (0) -#endif - -/* Scratch space */ -static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE))); - -typedef struct ResetInfo { - uint32_t ipl_mask; - uint32_t ipl_addr; - uint32_t ipl_continue; -} ResetInfo; - -static ResetInfo save; - -static void jump_to_IPL_2(void) -{ - ResetInfo *current = 0; - - void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; - *current = save; - ipl(); /* should not return */ -} - -static void jump_to_IPL_code(uint64_t address) -{ - /* store the subsystem information _after_ the bootmap was loaded */ - write_subsystem_identification(); - /* - * The IPL PSW is at address 0. We also must not overwrite the - * content of non-BIOS memory after we loaded the guest, so we - * save the original content and restore it in jump_to_IPL_2. - */ - ResetInfo *current = 0; - - save = *current; - current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; - current->ipl_continue = address & 0x7fffffff; - - debug_print_int("set IPL addr to", current->ipl_continue); - - /* Ensure the guest output starts fresh */ - sclp_print("\n"); - - /* - * HACK ALERT. - * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 - * can then use r15 as its stack pointer. - */ - asm volatile("lghi 1,1\n\t" - "diag 1,1,0x308\n\t" - : : : "1", "memory"); - panic("\n! IPL returns !\n"); -} - -/*********************************************************************** - * IPL an ECKD DASD (CDL or LDL/CMS format) - */ - -static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */ -static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); - -static inline void verify_boot_info(BootInfo *bip) -{ - IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); - IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); - IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); - IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); - IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch"); - IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size), - "Bad block size in zIPL section of the 1st record."); -} - -static block_number_t eckd_block_num(BootMapPointer *p) -{ - 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 block_number_t block = sectors * heads * cylinder - + sectors * head - + p->eckd.sector - - 1; /* block nr starts with zero */ - return block; -} - -static bool eckd_valid_address(BootMapPointer *p) -{ - const uint64_t head = p->eckd.head & 0x000f; - - if (head >= virtio_get_heads() - || p->eckd.sector > virtio_get_sectors() - || p->eckd.sector <= 0) { - return false; - } - - if (!virtio_guessed_disk_nature() && - eckd_block_num(p) >= virtio_get_blocks()) { - return false; - } - - return true; -} - -static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) -{ - block_number_t block_nr; - int j, rc; - BootMapPointer *bprs = (void *)_bprs; - bool more_data; - - memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(blk, bprs, "BPRS read failed"); - - do { - more_data = false; - for (j = 0;; j++) { - block_nr = eckd_block_num((void *)&(bprs[j].xeckd)); - if (is_null_block_number(block_nr)) { /* end of chunk */ - break; - } - - /* we need the updated blockno for the next indirect entry - * in the chain, but don't want to advance address - */ - if (j == (max_bprs_entries - 1)) { - break; - } - - IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size), - "bad chunk block size"); - IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr"); - - if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]), - sizeof(EckdBlockPtr))) { - /* This is a "continue" pointer. - * This ptr should be the last one in the current - * script section. - * I.e. the next ptr must point to the unused memory area - */ - memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(block_nr, bprs, "BPRS continuation read failed"); - more_data = true; - break; - } - - /* Load (count+1) blocks of code at (block_nr) - * to memory (address). - */ - rc = virtio_read_many(block_nr, (void *)(*address), - bprs[j].xeckd.bptr.count+1); - IPL_assert(rc == 0, "code chunk read failed"); - - *address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size(); - } - } while (more_data); - return block_nr; -} - -static void run_eckd_boot_script(block_number_t mbr_block_nr) -{ - int i; - block_number_t block_nr; - uint64_t address; - ScsiMbr *scsi_mbr = (void *)sec; - BootMapScript *bms = (void *)sec; - - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(mbr_block_nr, sec, "Cannot read MBR"); - - block_nr = eckd_block_num((void *)&(scsi_mbr->blockptr)); - - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(block_nr, sec, "Cannot read Boot Map Script"); - - 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)); - - do { - block_nr = load_eckd_segments(block_nr, &address); - } while (block_nr != -1); - } - - IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC, - "Unknown script entry type"); - jump_to_IPL_code(bms->entry[i].address.load_address); /* no return */ -} - -static void ipl_eckd_cdl(void) -{ - XEckdMbr *mbr; - Ipl2 *ipl2 = (void *)sec; - IplVolumeLabel *vlbl = (void *)sec; - block_number_t block_nr; - - /* we have just read the block #0 and recognized it as "IPL1" */ - sclp_print("CDL\n"); - - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(1, ipl2, "Cannot read IPL2 record at block 1"); - - mbr = &ipl2->u.x.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."); - 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)); - - 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), - "Invalid magic of volume label block"); - IPL_assert(magic_match(vlbl->f.key, VOL1_MAGIC), - "Invalid magic of volser block"); - print_volser(vlbl->f.volser); - - run_eckd_boot_script(block_nr); - /* no return */ -} - -static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) -{ - LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */ - char msg[4] = { '?', '.', '\n', '\0' }; - - sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL"); - sclp_print(" version "); - switch (vlbl->LDL_version) { - case LDL1_VERSION: - msg[0] = '1'; - break; - case LDL2_VERSION: - msg[0] = '2'; - break; - default: - msg[0] = vlbl->LDL_version; - msg[0] &= 0x0f; /* convert EBCDIC */ - msg[0] |= 0x30; /* to ASCII (digit) */ - msg[1] = '?'; - break; - } - sclp_print(msg); - print_volser(vlbl->volser); -} - -static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) -{ - block_number_t block_nr; - BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */ - - if (mode != ECKD_LDL_UNLABELED) { - print_eckd_ldl_msg(mode); - } - - /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */ - - 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)) { - return; /* not applicable layout */ - } - sclp_print("unlabeled LDL.\n"); - } - verify_boot_info(bip); - - block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr)); - run_eckd_boot_script(block_nr); - /* no return */ -} - -static void print_eckd_msg(void) -{ - char msg[] = "Using ECKD scheme (block size *****), "; - char *p = &msg[34], *q = &msg[30]; - int n = virtio_get_block_size(); - - /* Fill in the block size and show up the message */ - if (n > 0 && n <= 99999) { - while (n) { - *p-- = '0' + (n % 10); - n /= 10; - } - while (p >= q) { - *p-- = ' '; - } - } - sclp_print(msg); -} - -static void ipl_eckd(void) -{ - ScsiMbr *mbr = (void *)sec; - LDL_VTOC *vlbl = (void *)sec; - - print_eckd_msg(); - - /* Grab the MBR again */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0 on DASD"); - - if (magic_match(mbr->magic, IPL1_MAGIC)) { - ipl_eckd_cdl(); /* no return */ - } - - /* LDL/CMS? */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); - - if (magic_match(vlbl->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ - } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ - } - - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ - /* - * Ok, it is not a LDL by any means. - * It still might be a CDL with zero record keys for IPL1 and IPL2 - */ - ipl_eckd_cdl(); -} - -/*********************************************************************** - * IPL a SCSI disk - */ - -static void zipl_load_segment(ComponentEntry *entry) -{ - const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr)); - ScsiBlockPtr *bprs = (void *)sec; - const int bprs_size = sizeof(sec); - block_number_t blockno; - uint64_t address; - int i; - char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ"; - char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */ - - blockno = entry->data.blockno; - address = entry->load_address; - - debug_print_int("loading segment at block", blockno); - debug_print_int("addr", address); - - do { - memset(bprs, FREE_SPACE_FILLER, bprs_size); - fill_hex_val(blk_no, &blockno, sizeof(blockno)); - read_block(blockno, bprs, err_msg); - - for (i = 0;; i++) { - uint64_t *cur_desc = (void *)&bprs[i]; - - blockno = bprs[i].blockno; - if (!blockno) { - break; - } - - /* we need the updated blockno for the next indirect entry in the - chain, but don't want to advance address */ - if (i == (max_entries - 1)) { - break; - } - - if (bprs[i].blockct == 0 && unused_space(&bprs[i + 1], - sizeof(ScsiBlockPtr))) { - /* This is a "continue" pointer. - * This ptr is the last one in the current script section. - * I.e. the next ptr must point to the unused memory area. - * The blockno is not zero, so the upper loop must continue - * reading next section of BPRS. - */ - break; - } - address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, - (void *)address); - IPL_assert(address != -1, "zIPL load segment failed"); - } - } while (blockno); -} - -/* Run a zipl program */ -static void zipl_run(ScsiBlockPtr *pte) -{ - ComponentHeader *header; - ComponentEntry *entry; - uint8_t tmp_sec[MAX_SECTOR_SIZE]; - - read_block(pte->blockno, tmp_sec, "Cannot read header"); - header = (ComponentHeader *)tmp_sec; - - IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); - IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); - - dputs("start loading images\n"); - - /* Load image(s) into RAM */ - entry = (ComponentEntry *)(&header[1]); - while (entry->component_type == ZIPL_COMP_ENTRY_LOAD) { - zipl_load_segment(entry); - - entry++; - - IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE), - "Wrong entry value"); - } - - IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry"); - - /* should not return */ - jump_to_IPL_code(entry->load_address); -} - -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; - - /* Grab the MBR */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0"); - - if (!magic_match(mbr->magic, ZIPL_MAGIC)) { - return; - } - - sclp_print("Using SCSI scheme.\n"); - 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.blockno); - IPL_assert(mbr->blockptr.blockno, "No Program Table"); - - /* Parse the program table */ - read_block(mbr->blockptr.blockno, sec, - "Error reading Program Table"); - - IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - - 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) { - break; - } - - program_table_entries++; - } - - debug_print_int("program table entries", program_table_entries); - - IPL_assert(program_table_entries != 0, "Empty Program Table"); - - /* Run the default entry */ - - prog_table_entry = (ScsiBlockPtr *)(sec + pte_len); - - zipl_run(prog_table_entry); /* no return */ -} - -/*********************************************************************** - * IPL El Torito ISO9660 image or DVD - */ - -static bool is_iso_bc_entry_compatible(IsoBcSection *s) -{ - uint8_t *magic_sec = (uint8_t *)(sec + ISO_SECTOR_SIZE); - - if (s->unused || !s->sector_count) { - return false; - } - read_iso_sector(bswap32(s->load_rba), magic_sec, - "Failed to read image sector 0"); - - /* Checking bytes 8 - 32 for S390 Linux magic */ - return !_memcmp(magic_sec + 8, linux_s390_magic, 24); -} - -/* Location of the current sector of the directory */ -static uint32_t sec_loc[ISO9660_MAX_DIR_DEPTH]; -/* Offset in the current sector of the directory */ -static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH]; -/* Remained directory space in bytes */ -static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH]; - -static inline uint32_t iso_get_file_size(uint32_t load_rba) -{ - IsoVolDesc *vd = (IsoVolDesc *)sec; - IsoDirHdr *cur_record = &vd->vd.primary.rootdir; - uint8_t *temp = sec + ISO_SECTOR_SIZE; - int level = 0; - - read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec, - "Failed to read ISO primary descriptor"); - sec_loc[0] = iso_733_to_u32(cur_record->ext_loc); - dir_rem[0] = 0; - sec_offset[0] = 0; - - while (level >= 0) { - IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE, - "Directory tree structure violation"); - - cur_record = (IsoDirHdr *)(temp + sec_offset[level]); - - if (sec_offset[level] == 0) { - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); - if (dir_rem[level] == 0) { - /* Skip self and parent records */ - dir_rem[level] = iso_733_to_u32(cur_record->data_len) - - cur_record->dr_len; - sec_offset[level] += cur_record->dr_len; - - cur_record = (IsoDirHdr *)(temp + sec_offset[level]); - dir_rem[level] -= cur_record->dr_len; - sec_offset[level] += cur_record->dr_len; - continue; - } - } - - if (!cur_record->dr_len || sec_offset[level] == ISO_SECTOR_SIZE) { - /* Zero-padding and/or the end of current sector */ - dir_rem[level] -= ISO_SECTOR_SIZE - sec_offset[level]; - sec_offset[level] = 0; - sec_loc[level]++; - } else { - /* The directory record is valid */ - if (load_rba == iso_733_to_u32(cur_record->ext_loc)) { - return iso_733_to_u32(cur_record->data_len); - } - - dir_rem[level] -= cur_record->dr_len; - sec_offset[level] += cur_record->dr_len; - - if (cur_record->file_flags & 0x2) { - /* Subdirectory */ - if (level == ISO9660_MAX_DIR_DEPTH - 1) { - sclp_print("ISO-9660 directory depth limit exceeded\n"); - } else { - level++; - sec_loc[level] = iso_733_to_u32(cur_record->ext_loc); - sec_offset[level] = 0; - dir_rem[level] = 0; - continue; - } - } - } - - if (dir_rem[level] == 0) { - /* Nothing remaining */ - level--; - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); - } - } - - return 0; -} - -static void load_iso_bc_entry(IsoBcSection *load) -{ - IsoBcSection s = *load; - /* - * According to spec, extent for each file - * is padded and ISO_SECTOR_SIZE bytes aligned - */ - uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT; - uint32_t real_size = iso_get_file_size(bswap32(s.load_rba)); - - if (real_size) { - /* Round up blocks to load */ - blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE; - sclp_print("ISO boot image size verified\n"); - } else { - sclp_print("ISO boot image size could not be verified\n"); - } - - read_iso_boot_image(bswap32(s.load_rba), - (void *)((uint64_t)bswap16(s.load_segment)), - blks_to_load); - - /* Trying to get PSW at zero address */ - if (*((uint64_t *)0) & IPL_PSW_MASK) { - jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); - } - - /* Try default linux start address */ - jump_to_IPL_code(KERN_IMAGE_START); -} - -static uint32_t find_iso_bc(void) -{ - IsoVolDesc *vd = (IsoVolDesc *)sec; - uint32_t block_num = ISO_PRIMARY_VD_SECTOR; - - if (virtio_read_many(block_num++, sec, 1)) { - /* If primary vd cannot be read, there is no boot catalog */ - return 0; - } - - while (is_iso_vd_valid(vd) && vd->type != VOL_DESC_TERMINATOR) { - if (vd->type == VOL_DESC_TYPE_BOOT) { - IsoVdElTorito *et = &vd->vd.boot; - - if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) { - return bswap32(et->bc_offset); - } - } - read_iso_sector(block_num++, sec, - "Failed to read ISO volume descriptor"); - } - - return 0; -} - -static IsoBcSection *find_iso_bc_entry(void) -{ - IsoBcEntry *e = (IsoBcEntry *)sec; - uint32_t offset = find_iso_bc(); - int i; - - if (!offset) { - return NULL; - } - - read_iso_sector(offset, sec, "Failed to read El Torito boot catalog"); - - if (!is_iso_bc_valid(e)) { - /* The validation entry is mandatory */ - panic("No valid boot catalog found!\n"); - return NULL; - } - - /* - * Each entry has 32 bytes size, so one sector cannot contain > 64 entries. - * We consider only boot catalogs with no more than 64 entries. - */ - for (i = 1; i < ISO_BC_ENTRY_PER_SECTOR; i++) { - if (e[i].id == ISO_BC_BOOTABLE_SECTION) { - if (is_iso_bc_entry_compatible(&e[i].body.sect)) { - return &e[i].body.sect; - } - } - } - - panic("No suitable boot entry found on ISO-9660 media!\n"); - - return NULL; -} - -static void ipl_iso_el_torito(void) -{ - IsoBcSection *s = find_iso_bc_entry(); - - if (s) { - load_iso_bc_entry(s); - /* no return */ - } -} - -/*********************************************************************** - * Bus specific IPL sequences - */ - -static void zipl_load_vblk(void) -{ - if (virtio_guessed_disk_nature()) { - virtio_assume_iso9660(); - } - ipl_iso_el_torito(); - - if (virtio_guessed_disk_nature()) { - sclp_print("Using guessed DASD geometry.\n"); - virtio_assume_eckd(); - } - ipl_eckd(); -} - -static void zipl_load_vscsi(void) -{ - if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { - /* Is it an ISO image in non-CD drive? */ - ipl_iso_el_torito(); - } - - sclp_print("Using guessed DASD geometry.\n"); - virtio_assume_eckd(); - ipl_eckd(); -} - -/*********************************************************************** - * IPL starts here - */ - -void zipl_load(void) -{ - if (virtio_get_device()->is_cdrom) { - ipl_iso_el_torito(); - panic("\n! Cannot IPL this ISO image !\n"); - } - - ipl_scsi(); - - switch (virtio_get_device_type()) { - case VIRTIO_ID_BLOCK: - zipl_load_vblk(); - break; - case VIRTIO_ID_SCSI: - zipl_load_vscsi(); - break; - default: - panic("\n! Unknown IPL device type !\n"); - } - - panic("\n* this can never happen *\n"); -} diff --git a/qemu/pc-bios/s390-ccw/bootmap.h b/qemu/pc-bios/s390-ccw/bootmap.h deleted file mode 100644 index bea168714..000000000 --- a/qemu/pc-bios/s390-ccw/bootmap.h +++ /dev/null @@ -1,541 +0,0 @@ -/* - * QEMU S390 bootmap interpreter -- declarations - * - * Copyright 2014 IBM Corp. - * Author(s): Eugene (jno) Dvurechenski <jno@linux.vnet.ibm.com> - * - * 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. - */ -#ifndef _PC_BIOS_S390_CCW_BOOTMAP_H -#define _PC_BIOS_S390_CCW_BOOTMAP_H - -#include "s390-ccw.h" -#include "virtio.h" - -typedef uint64_t block_number_t; -#define NULL_BLOCK_NR 0xffffffffffffffffULL - -#define FREE_SPACE_FILLER '\xAA' - -typedef struct ScsiBlockPtr { - uint64_t blockno; - uint16_t size; - uint16_t blockct; - uint8_t reserved[4]; -} __attribute__ ((packed)) ScsiBlockPtr; - -typedef struct FbaBlockPtr { - uint32_t blockno; - uint16_t size; - uint16_t blockct; -} __attribute__ ((packed)) FbaBlockPtr; - -typedef struct EckdBlockPtr { - uint16_t cylinder; /* cylinder/head/sector is an address of the block */ - uint16_t head; - uint8_t sector; - uint16_t size; - uint8_t count; /* (size_in_blocks-1); - * it's 0 for TablePtr, ScriptPtr, and SectionPtr */ -} __attribute__ ((packed)) EckdBlockPtr; - -typedef struct ExtEckdBlockPtr { - EckdBlockPtr bptr; - uint8_t reserved[8]; -} __attribute__ ((packed)) ExtEckdBlockPtr; - -typedef union BootMapPointer { - ScsiBlockPtr scsi; - FbaBlockPtr fba; - EckdBlockPtr eckd; - ExtEckdBlockPtr xeckd; -} __attribute__ ((packed)) BootMapPointer; - -typedef struct ComponentEntry { - ScsiBlockPtr data; - uint8_t pad[7]; - uint8_t component_type; - uint64_t load_address; -} __attribute((packed)) ComponentEntry; - -typedef struct ComponentHeader { - uint8_t magic[4]; /* == "zIPL" */ - uint8_t type; /* == ZIPL_COMP_HEADER_* */ - uint8_t reserved[27]; -} __attribute((packed)) ComponentHeader; - -typedef struct ScsiMbr { - uint8_t magic[4]; - uint32_t version_id; - uint8_t reserved[8]; - ScsiBlockPtr blockptr; -} __attribute__ ((packed)) ScsiMbr; - -#define ZIPL_MAGIC "zIPL" -#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 */ -#define LNX1_MAGIC "\xd3\xd5\xe7\xf1" /* == "LNX1" in EBCDIC */ -#define CMS1_MAGIC "\xc3\xd4\xe2\xf1" /* == "CMS1" in EBCDIC */ - -#define LDL1_VERSION '\x40' /* == ' ' in EBCDIC */ -#define LDL2_VERSION '\xf2' /* == '2' in EBCDIC */ - -#define ZIPL_COMP_HEADER_IPL 0x00 -#define ZIPL_COMP_HEADER_DUMP 0x01 - -#define ZIPL_COMP_ENTRY_LOAD 0x02 -#define ZIPL_COMP_ENTRY_EXEC 0x01 - -typedef struct XEckdMbr { - uint8_t magic[4]; /* == "xIPL" */ - uint8_t version; - uint8_t bp_type; - uint8_t dev_type; /* == DEV_TYPE_* */ -#define DEV_TYPE_ECKD 0x00 -#define DEV_TYPE_FBA 0x01 - uint8_t flags; - BootMapPointer blockptr; - uint8_t reserved[8]; -} __attribute__ ((packed)) XEckdMbr; /* see also BootInfo */ - -typedef struct BootMapScriptEntry { - BootMapPointer blkptr; - uint8_t pad[7]; - uint8_t type; /* == BOOT_SCRIPT_* */ -#define BOOT_SCRIPT_EXEC 0x01 -#define BOOT_SCRIPT_LOAD 0x02 - union { - uint64_t load_address; - uint64_t load_psw; - } address; -} __attribute__ ((packed)) BootMapScriptEntry; - -typedef struct BootMapScriptHeader { - uint32_t magic; - uint8_t type; -#define BOOT_SCRIPT_HDR_IPL 0x00 - uint8_t reserved[27]; -} __attribute__ ((packed)) BootMapScriptHeader; - -typedef struct BootMapScript { - BootMapScriptHeader header; - BootMapScriptEntry entry[0]; -} __attribute__ ((packed)) BootMapScript; - -/* - * These aren't real VTOCs, but referred to this way in some docs. - * They are "volume labels" actually. - * - * Some structures looks similar to described above, but left - * separate as there is no indication that they are the same. - * So, the value definitions are left separate too. - */ -typedef struct LDL_VTOC { /* @ rec.3 cyl.0 trk.0 for ECKD */ - char magic[4]; /* "LNX1", EBCDIC */ - char volser[6]; /* volser, EBCDIC */ - uint8_t reserved[69]; /* reserved, 0x40 */ - uint8_t LDL_version; /* 0x40 or 0xF2 */ - uint64_t formatted_blocks; /* if LDL_version >= 0xF2 */ -} __attribute__ ((packed)) LDL_VTOC; - -typedef struct format_date { - uint8_t YY; - uint8_t MM; - uint8_t DD; - uint8_t hh; - uint8_t mm; - uint8_t ss; -} __attribute__ ((packed)) format_date_t; - -typedef struct CMS_VTOC { /* @ rec.3 cyl.0 trk.0 for ECKD */ - /* @ blk.1 (zero based) for FBA */ - char magic[4]; /* 'CMS1', EBCDIC */ - char volser[6]; /* volser, EBCDIC */ - uint16_t version; /* = 0 */ - uint32_t block_size; /* = 512, 1024, 2048, or 4096 */ - uint32_t disk_origin; /* = 4 or 5 */ - uint32_t blocks; /* Number of usable cyls/blocks */ - uint32_t formatted; /* Max number of fmtd cyls/blks */ - uint32_t CMS_blocks; /* disk size in CMS blocks */ - uint32_t CMS_used; /* Number of CMS blocks in use */ - uint32_t FST_size; /* = 64, bytes */ - uint32_t FST_per_CMS_blk; /* */ - format_date_t format_date; /* YYMMDDhhmmss as 6 bytes */ - uint8_t reserved1[2]; /* = 0 */ - uint32_t offset; /* disk offset when reserved */ - uint32_t next_hole; /* block nr */ - uint32_t HBLK_hole_offset; /* >> HBLK data of next hole */ - uint32_t alloc_map_usr_off; /* >> user part of Alloc map */ - uint8_t reserved2[4]; /* = 0 */ - char shared_seg_name[8]; /* */ -} __attribute__ ((packed)) CMS_VTOC; - -/* from zipl/include/boot.h */ -typedef struct BootInfoBpIpl { - union { - ExtEckdBlockPtr eckd; - ScsiBlockPtr linr; - } bm_ptr; - uint8_t unused[16]; -} __attribute__ ((packed)) BootInfoBpIpl; - -typedef struct EckdDumpParam { - uint32_t start_blk; - uint32_t end_blk; - uint16_t blocksize; - uint8_t num_heads; - uint8_t bpt; - char reserved[4]; -} __attribute((packed, may_alias)) EckdDumpParam; - -typedef struct FbaDumpParam { - uint64_t start_blk; - uint64_t blockct; -} __attribute((packed)) FbaDumpParam; - -typedef struct BootInfoBpDump { - union { - EckdDumpParam eckd; - FbaDumpParam fba; - } param; - uint8_t unused[16]; -} __attribute__ ((packed)) BootInfoBpDump; - -typedef struct BootInfo { /* @ 0x70, record #0 */ - unsigned char magic[4]; /* = 'zIPL', ASCII */ - uint8_t version; /* = 1 */ -#define BOOT_INFO_VERSION 1 - uint8_t bp_type; /* = 0 */ -#define BOOT_INFO_BP_TYPE_IPL 0x00 -#define BOOT_INFO_BP_TYPE_DUMP 0x01 - uint8_t dev_type; /* = 0 */ -#define BOOT_INFO_DEV_TYPE_ECKD 0x00 -#define BOOT_INFO_DEV_TYPE_FBA 0x01 - uint8_t flags; /* = 1 */ -#ifdef __s390x__ -#define BOOT_INFO_FLAGS_ARCH 0x01 -#else -#define BOOT_INFO_FLAGS_ARCH 0x00 -#endif - union { - BootInfoBpDump dump; - BootInfoBpIpl ipl; - } bp; -} __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */ - -typedef struct Ipl1 { - unsigned char key[4]; /* == "IPL1" */ - unsigned char data[24]; -} __attribute__((packed)) Ipl1; - -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 IplVolumeLabel { - unsigned char key[4]; /* == "VOL1" */ - union { - unsigned char data[80]; - struct { - unsigned char key[4]; /* == "VOL1" */ - unsigned char volser[6]; - unsigned char reserved[6]; - } f; - }; -} __attribute__((packed)) IplVolumeLabel; - -typedef enum { - ECKD_NO_IPL, - ECKD_CMS, - ECKD_LDL, - ECKD_LDL_UNLABELED, -} ECKD_IPL_mode_t; - -/* utility code below */ - -static const unsigned char ebc2asc[256] = - /* 0123456789abcdef0123456789abcdef */ - "................................" /* 1F */ - "................................" /* 3F */ - " ...........<(+|&.........!$*);." /* 5F first.chr.here.is.real.space */ - "-/.........,%_>?.........`:#@'=\""/* 7F */ - ".abcdefghi.......jklmnopqr......" /* 9F */ - "..stuvwxyz......................" /* BF */ - ".ABCDEFGHI.......JKLMNOPQR......" /* DF */ - "..STUVWXYZ......0123456789......";/* FF */ - -static inline void ebcdic_to_ascii(const char *src, - char *dst, - unsigned int size) -{ - unsigned int i; - for (i = 0; i < size; i++) { - unsigned c = src[i]; - dst[i] = ebc2asc[c]; - } -} - -static inline void print_volser(const void *volser) -{ - char ascii[8]; - - ebcdic_to_ascii((char *)volser, ascii, 6); - ascii[6] = '\0'; - sclp_print("VOLSER=["); - sclp_print(ascii); - sclp_print("]\n"); -} - -static inline bool unused_space(const void *p, size_t size) -{ - size_t i; - const unsigned char *m = p; - - for (i = 0; i < size; i++) { - if (m[i] != FREE_SPACE_FILLER) { - return false; - } - } - return true; -} - -static inline bool is_null_block_number(block_number_t x) -{ - return x == NULL_BLOCK_NR; -} - -static inline void read_block(block_number_t blockno, - void *buffer, - const char *errmsg) -{ - IPL_assert(virtio_read(blockno, buffer) == 0, errmsg); -} - -static inline bool block_size_ok(uint32_t block_size) -{ - return block_size == virtio_get_block_size(); -} - -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; -} - -/* from include/qemu/bswap.h */ - -/* El Torito is always little-endian */ -static inline uint16_t bswap16(uint16_t x) -{ - return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8); -} - -static inline uint32_t bswap32(uint32_t x) -{ - return ((x & 0x000000ffU) << 24) | ((x & 0x0000ff00U) << 8) | - ((x & 0x00ff0000U) >> 8) | ((x & 0xff000000U) >> 24); -} - -static inline uint64_t bswap64(uint64_t x) -{ - return ((x & 0x00000000000000ffULL) << 56) | - ((x & 0x000000000000ff00ULL) << 40) | - ((x & 0x0000000000ff0000ULL) << 24) | - ((x & 0x00000000ff000000ULL) << 8) | - ((x & 0x000000ff00000000ULL) >> 8) | - ((x & 0x0000ff0000000000ULL) >> 24) | - ((x & 0x00ff000000000000ULL) >> 40) | - ((x & 0xff00000000000000ULL) >> 56); -} - -static inline uint32_t iso_733_to_u32(uint64_t x) -{ - return (uint32_t)x; -} - -#define ISO_SECTOR_SIZE 2048 -/* El Torito specifies boot image size in 512 byte blocks */ -#define ET_SECTOR_SHIFT 2 -#define KERN_IMAGE_START 0x010000UL -#define PSW_MASK_64 0x0000000100000000ULL -#define PSW_MASK_32 0x0000000080000000ULL -#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) - -#define ISO_PRIMARY_VD_SECTOR 16 - -static inline void read_iso_sector(uint32_t block_offset, void *buf, - const char *errmsg) -{ - IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg); -} - -static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr, - uint32_t blks_to_load) -{ - IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0, - "Failed to read boot image!"); -} - -const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -#define ISO9660_MAX_DIR_DEPTH 8 - -typedef struct IsoDirHdr { - uint8_t dr_len; - uint8_t ear_len; - uint64_t ext_loc; - uint64_t data_len; - uint8_t recording_datetime[7]; - uint8_t file_flags; - uint8_t file_unit_size; - uint8_t gap_size; - uint32_t vol_seqnum; - uint8_t fileid_len; -} __attribute__((packed)) IsoDirHdr; - -typedef struct IsoVdElTorito { - uint8_t el_torito[32]; /* must contain el_torito_magic value */ - uint8_t unused0[32]; - uint32_t bc_offset; - uint8_t unused1[1974]; -} __attribute__((packed)) IsoVdElTorito; - -typedef struct IsoVdPrimary { - uint8_t unused1; - uint8_t sys_id[32]; - uint8_t vol_id[32]; - uint8_t unused2[8]; - uint64_t vol_space_size; - uint8_t unused3[32]; - uint32_t vol_set_size; - uint32_t vol_seqnum; - uint32_t log_block_size; - uint64_t path_table_size; - uint32_t l_path_table; - uint32_t opt_l_path_table; - uint32_t m_path_table; - uint32_t opt_m_path_table; - IsoDirHdr rootdir; - uint8_t root_null; - uint8_t reserved2[1858]; -} __attribute__((packed)) IsoVdPrimary; - -typedef struct IsoVolDesc { - uint8_t type; - uint8_t ident[5]; - uint8_t version; - union { - IsoVdElTorito boot; - IsoVdPrimary primary; - } vd; -} __attribute__((packed)) IsoVolDesc; - -const uint8_t vol_desc_magic[] = "CD001"; -#define VOL_DESC_TYPE_BOOT 0 -#define VOL_DESC_TYPE_PRIMARY 1 -#define VOL_DESC_TYPE_SUPPLEMENT 2 -#define VOL_DESC_TYPE_PARTITION 3 -#define VOL_DESC_TERMINATOR 255 - -static inline bool is_iso_vd_valid(IsoVolDesc *vd) -{ - return !_memcmp(&vd->ident[0], vol_desc_magic, 5) && - vd->version == 0x1 && - vd->type <= VOL_DESC_TYPE_PARTITION; -} - -typedef struct IsoBcValid { - uint8_t platform_id; - uint16_t reserved; - uint8_t id[24]; - uint16_t checksum; - uint8_t key[2]; -} __attribute__((packed)) IsoBcValid; - -typedef struct IsoBcSection { - uint8_t boot_type; - uint16_t load_segment; - uint8_t sys_type; - uint8_t unused; - uint16_t sector_count; - uint32_t load_rba; - uint8_t selection[20]; -} __attribute__((packed)) IsoBcSection; - -typedef struct IsoBcHdr { - uint8_t platform_id; - uint16_t sect_num; - uint8_t id[28]; -} __attribute__((packed)) IsoBcHdr; - -/* - * Match two CCWs located after PSW and eight filler bytes. - * From libmagic and arch/s390/kernel/head.S. - */ -const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00" - "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40" - "\x40\x40\x40\x40"; - -typedef struct IsoBcEntry { - uint8_t id; - union { - IsoBcValid valid; /* id == 0x01 */ - IsoBcSection sect; /* id == 0x88 || id == 0x0 */ - IsoBcHdr hdr; /* id == 0x90 || id == 0x91 */ - } body; -} __attribute__((packed)) IsoBcEntry; - -#define ISO_BC_ENTRY_PER_SECTOR (ISO_SECTOR_SIZE / sizeof(IsoBcEntry)) -#define ISO_BC_HDR_VALIDATION 0x01 -#define ISO_BC_BOOTABLE_SECTION 0x88 -#define ISO_BC_MAGIC_55 0x55 -#define ISO_BC_MAGIC_AA 0xaa -#define ISO_BC_PLATFORM_X86 0x0 -#define ISO_BC_PLATFORM_PPC 0x1 -#define ISO_BC_PLATFORM_MAC 0x2 - -static inline bool is_iso_bc_valid(IsoBcEntry *e) -{ - IsoBcValid *v = &e->body.valid; - - if (e->id != ISO_BC_HDR_VALIDATION) { - return false; - } - - if (v->platform_id != ISO_BC_PLATFORM_X86 && - v->platform_id != ISO_BC_PLATFORM_PPC && - v->platform_id != ISO_BC_PLATFORM_MAC) { - return false; - } - - return v->key[0] == ISO_BC_MAGIC_55 && - v->key[1] == ISO_BC_MAGIC_AA && - v->reserved == 0x0; -} - -#endif /* _PC_BIOS_S390_CCW_BOOTMAP_H */ diff --git a/qemu/pc-bios/s390-ccw/cio.h b/qemu/pc-bios/s390-ccw/cio.h deleted file mode 100644 index f5b4549ea..000000000 --- a/qemu/pc-bios/s390-ccw/cio.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Channel IO definitions - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * Inspired by various s390 headers in Linux 3.9. - * - * 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. - */ - -#ifndef CIO_H -#define CIO_H - -/* - * path management control word - */ -struct pmcw { - __u32 intparm; /* interruption parameter */ - __u32 qf : 1; /* qdio facility */ - __u32 w : 1; - __u32 isc : 3; /* interruption sublass */ - __u32 res5 : 3; /* reserved zeros */ - __u32 ena : 1; /* enabled */ - __u32 lm : 2; /* limit mode */ - __u32 mme : 2; /* measurement-mode enable */ - __u32 mp : 1; /* multipath mode */ - __u32 tf : 1; /* timing facility */ - __u32 dnv : 1; /* device number valid */ - __u32 dev : 16; /* device number */ - __u8 lpm; /* logical path mask */ - __u8 pnom; /* path not operational mask */ - __u8 lpum; /* last path used mask */ - __u8 pim; /* path installed mask */ - __u16 mbi; /* measurement-block index */ - __u8 pom; /* path operational mask */ - __u8 pam; /* path available mask */ - __u8 chpid[8]; /* CHPID 0-7 (if available) */ - __u32 unused1 : 8; /* reserved zeros */ - __u32 st : 3; /* subchannel type */ - __u32 unused2 : 18; /* reserved zeros */ - __u32 mbfc : 1; /* measurement block format control */ - __u32 xmwme : 1; /* extended measurement word mode enable */ - __u32 csense : 1; /* concurrent sense; can be enabled ...*/ - /* ... per MSCH, however, if facility */ - /* ... is not installed, this results */ - /* ... in an operand exception. */ -} __attribute__ ((packed)); - -/* Target SCHIB configuration. */ -struct schib_config { - __u64 mba; - __u32 intparm; - __u16 mbi; - __u32 isc:3; - __u32 ena:1; - __u32 mme:2; - __u32 mp:1; - __u32 csense:1; - __u32 mbfc:1; -} __attribute__ ((packed)); - -struct scsw { - __u16 flags; - __u16 ctrl; - __u32 cpa; - __u8 dstat; - __u8 cstat; - __u16 count; -} __attribute__ ((packed)); - -#define SCSW_FCTL_CLEAR_FUNC 0x1000 -#define SCSW_FCTL_HALT_FUNC 0x2000 -#define SCSW_FCTL_START_FUNC 0x4000 - -/* - * subchannel information block - */ -struct schib { - struct pmcw pmcw; /* path management control word */ - struct scsw scsw; /* subchannel status word */ - __u64 mba; /* measurement block address */ - __u8 mda[4]; /* model dependent area */ -} __attribute__ ((packed,aligned(4))); - -struct subchannel_id { - __u32 cssid : 8; - __u32 : 4; - __u32 m : 1; - __u32 ssid : 2; - __u32 one : 1; - __u32 sch_no : 16; -} __attribute__ ((packed, aligned(4))); - -struct chsc_header { - __u16 length; - __u16 code; -} __attribute__((packed)); - -struct chsc_area_sda { - struct chsc_header request; - __u8 reserved1:4; - __u8 format:4; - __u8 reserved2; - __u16 operation_code; - __u32 reserved3; - __u32 reserved4; - __u32 operation_data_area[252]; - struct chsc_header response; - __u32 reserved5:4; - __u32 format2:4; - __u32 reserved6:24; -} __attribute__((packed)); - -/* - * TPI info structure - */ -struct tpi_info { - struct subchannel_id schid; - __u32 intparm; /* interruption parameter */ - __u32 adapter_IO : 1; - __u32 reserved2 : 1; - __u32 isc : 3; - __u32 reserved3 : 12; - __u32 int_type : 3; - __u32 reserved4 : 12; -} __attribute__ ((packed)); - -/* channel command word (type 1) */ -struct ccw1 { - __u8 cmd_code; - __u8 flags; - __u16 count; - __u32 cda; -} __attribute__ ((packed)); - -#define CCW_FLAG_DC 0x80 -#define CCW_FLAG_CC 0x40 -#define CCW_FLAG_SLI 0x20 -#define CCW_FLAG_SKIP 0x10 -#define CCW_FLAG_PCI 0x08 -#define CCW_FLAG_IDA 0x04 -#define CCW_FLAG_SUSPEND 0x02 - -#define CCW_CMD_NOOP 0x03 -#define CCW_CMD_BASIC_SENSE 0x04 -#define CCW_CMD_TIC 0x08 -#define CCW_CMD_SENSE_ID 0xe4 - -#define CCW_CMD_SET_VQ 0x13 -#define CCW_CMD_VDEV_RESET 0x33 -#define CCW_CMD_READ_FEAT 0x12 -#define CCW_CMD_WRITE_FEAT 0x11 -#define CCW_CMD_READ_CONF 0x22 -#define CCW_CMD_WRITE_CONF 0x21 -#define CCW_CMD_WRITE_STATUS 0x31 -#define CCW_CMD_SET_IND 0x43 -#define CCW_CMD_SET_CONF_IND 0x53 -#define CCW_CMD_READ_VQ_CONF 0x32 - -/* - * Command-mode operation request block - */ -struct cmd_orb { - __u32 intparm; /* interruption parameter */ - __u32 key:4; /* flags, like key, suspend control, etc. */ - __u32 spnd:1; /* suspend control */ - __u32 res1:1; /* reserved */ - __u32 mod:1; /* modification control */ - __u32 sync:1; /* synchronize control */ - __u32 fmt:1; /* format control */ - __u32 pfch:1; /* prefetch control */ - __u32 isic:1; /* initial-status-interruption control */ - __u32 alcc:1; /* address-limit-checking control */ - __u32 ssic:1; /* suppress-suspended-interr. control */ - __u32 res2:1; /* reserved */ - __u32 c64:1; /* IDAW/QDIO 64 bit control */ - __u32 i2k:1; /* IDAW 2/4kB block size control */ - __u32 lpm:8; /* logical path mask */ - __u32 ils:1; /* incorrect length */ - __u32 zero:6; /* reserved zeros */ - __u32 orbx:1; /* ORB extension control */ - __u32 cpa; /* channel program address */ -} __attribute__ ((packed, aligned(4))); - -struct ciw { - __u8 type; - __u8 command; - __u16 count; -}; - -/* - * sense-id response buffer layout - */ -struct senseid { - /* common part */ - __u8 reserved; /* always 0x'FF' */ - __u16 cu_type; /* control unit type */ - __u8 cu_model; /* control unit model */ - __u16 dev_type; /* device type */ - __u8 dev_model; /* device model */ - __u8 unused; /* padding byte */ - /* extended part */ - struct ciw ciw[62]; -} __attribute__ ((packed, aligned(4))); - -/* interruption response block */ -struct irb { - struct scsw scsw; - __u32 esw[5]; - __u32 ecw[8]; - __u32 emw[8]; -} __attribute__ ((packed, aligned(4))); - -/* - * Some S390 specific IO instructions as inline - */ - -static inline int stsch_err(struct subchannel_id schid, struct schib *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode = -EIO; - - asm volatile( - " stsch 0(%3)\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - : "+d" (ccode), "=m" (*addr) - : "d" (reg1), "a" (addr) - : "cc"); - return ccode; -} - -static inline int msch(struct subchannel_id schid, struct schib *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " msch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (reg1), "a" (addr), "m" (*addr) - : "cc"); - return ccode; -} - -static inline int msch_err(struct subchannel_id schid, struct schib *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode = -EIO; - - asm volatile( - " msch 0(%2)\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - : "+d" (ccode) - : "d" (reg1), "a" (addr), "m" (*addr) - : "cc"); - return ccode; -} - -static inline int tsch(struct subchannel_id schid, struct irb *addr) -{ - register struct subchannel_id reg1 asm ("1") = schid; - int ccode; - - asm volatile( - " tsch 0(%3)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode), "=m" (*addr) - : "d" (reg1), "a" (addr) - : "cc"); - return ccode; -} - -static inline int ssch(struct subchannel_id schid, struct cmd_orb *addr) -{ - register struct subchannel_id reg1 asm("1") = schid; - int ccode = -EIO; - - asm volatile( - " ssch 0(%2)\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - : "+d" (ccode) - : "d" (reg1), "a" (addr), "m" (*addr) - : "cc", "memory"); - return ccode; -} - -static inline int csch(struct subchannel_id schid) -{ - register struct subchannel_id reg1 asm("1") = schid; - int ccode; - - asm volatile( - " csch\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "d" (reg1) - : "cc"); - return ccode; -} - -static inline int tpi(struct tpi_info *addr) -{ - int ccode; - - asm volatile( - " tpi 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode), "=m" (*addr) - : "a" (addr) - : "cc"); - return ccode; -} - -static inline int chsc(void *chsc_area) -{ - typedef struct { char _[4096]; } addr_type; - int cc; - - asm volatile( - " .insn rre,0xb25f0000,%2,0\n" - " ipm %0\n" - " srl %0,28\n" - : "=d" (cc), "=m" (*(addr_type *) chsc_area) - : "d" (chsc_area), "m" (*(addr_type *) chsc_area) - : "cc"); - return cc; -} - -#endif /* CIO_H */ diff --git a/qemu/pc-bios/s390-ccw/main.c b/qemu/pc-bios/s390-ccw/main.c deleted file mode 100644 index 1c9e0791a..000000000 --- a/qemu/pc-bios/s390-ccw/main.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * S390 virtio-ccw loading program - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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 "s390-ccw.h" -#include "virtio.h" - -char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -uint64_t boot_value; -static SubChannelId blk_schid = { .one = 1 }; - -/* - * Priniciples of Operations (SA22-7832-09) chapter 17 requires that - * a subsystem-identification is at 184-187 and bytes 188-191 are zero - * after list-directed-IPL and ccw-IPL. - */ -void write_subsystem_identification(void) -{ - SubChannelId *schid = (SubChannelId *) 184; - uint32_t *zeroes = (uint32_t *) 188; - - *schid = blk_schid; - *zeroes = 0; -} - - -void panic(const char *string) -{ - sclp_print(string); - disabled_wait(); - while (1) { } -} - -static bool find_dev(Schib *schib, int dev_no) -{ - int i, r; - - for (i = 0; i < 0x10000; i++) { - blk_schid.sch_no = i; - r = stsch_err(blk_schid, schib); - if ((r == 3) || (r == -EIO)) { - break; - } - if (!schib->pmcw.dnv) { - continue; - } - if (!virtio_is_supported(blk_schid)) { - continue; - } - if ((dev_no < 0) || (schib->pmcw.dev == dev_no)) { - return true; - } - } - - return false; -} - -static void virtio_setup(uint64_t dev_info) -{ - Schib schib; - int ssid; - bool found = false; - uint16_t dev_no; - - /* - * We unconditionally enable mss support. In every sane configuration, - * this will succeed; and even if it doesn't, stsch_err() can deal - * with the consequences. - */ - enable_mss_facility(); - - if (dev_info != -1) { - dev_no = dev_info & 0xffff; - debug_print_int("device no. ", dev_no); - blk_schid.ssid = (dev_info >> 16) & 0x3; - debug_print_int("ssid ", blk_schid.ssid); - found = find_dev(&schib, dev_no); - } else { - for (ssid = 0; ssid < 0x3; ssid++) { - blk_schid.ssid = ssid; - found = find_dev(&schib, -1); - if (found) { - break; - } - } - } - - IPL_assert(found, "No virtio device found"); - - virtio_setup_device(blk_schid); - - IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); -} - -int main(void) -{ - sclp_setup(); - debug_print_int("boot reg[7] ", boot_value); - virtio_setup(boot_value); - - zipl_load(); /* no return */ - - panic("Failed to load OS from hard disk\n"); - return 0; /* make compiler happy */ -} diff --git a/qemu/pc-bios/s390-ccw/s390-ccw.h b/qemu/pc-bios/s390-ccw/s390-ccw.h deleted file mode 100644 index 616d96738..000000000 --- a/qemu/pc-bios/s390-ccw/s390-ccw.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * S390 CCW boot loader - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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. - */ - -#ifndef S390_CCW_H -#define S390_CCW_H - -/* #define DEBUG */ - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef unsigned long ulong; -typedef long size_t; -typedef int bool; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; -typedef unsigned char __u8; -typedef unsigned short __u16; -typedef unsigned int __u32; -typedef unsigned long long __u64; - -#define true 1 -#define false 0 -#define PAGE_SIZE 4096 - -#ifndef EIO -#define EIO 1 -#endif -#ifndef EBUSY -#define EBUSY 2 -#endif -#ifndef NULL -#define NULL 0 -#endif - -#include "cio.h" - -typedef struct irb Irb; -typedef struct ccw1 Ccw1; -typedef struct cmd_orb CmdOrb; -typedef struct schib Schib; -typedef struct chsc_area_sda ChscAreaSda; -typedef struct senseid SenseId; -typedef struct subchannel_id SubChannelId; - -/* start.s */ -void disabled_wait(void); -void consume_sclp_int(void); - -/* main.c */ -void panic(const char *string); -void write_subsystem_identification(void); -extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -extern uint64_t boot_value; - -/* sclp-ascii.c */ -void sclp_print(const char *string); -void sclp_setup(void); - -/* virtio.c */ -unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, - ulong subchan_id, void *load_addr); -bool virtio_is_supported(SubChannelId schid); -void virtio_setup_device(SubChannelId schid); -int virtio_read(ulong sector, void *load_addr); -int enable_mss_facility(void); -ulong get_second(void); - -/* bootmap.c */ -void zipl_load(void); - -static inline void *memset(void *s, int c, size_t n) -{ - int i; - unsigned char *p = s; - - for (i = 0; i < n; i++) { - p[i] = c; - } - - return s; -} - -static inline void fill_hex(char *out, unsigned char val) -{ - const char hex[] = "0123456789abcdef"; - - out[0] = hex[(val >> 4) & 0xf]; - out[1] = hex[val & 0xf]; -} - -static inline void fill_hex_val(char *out, void *ptr, unsigned size) -{ - unsigned char *value = ptr; - unsigned int i; - - for (i = 0; i < size; i++) { - fill_hex(&out[i*2], value[i]); - } -} - -static inline void print_int(const char *desc, u64 addr) -{ - char out[] = ": 0xffffffffffffffff\n"; - - fill_hex_val(&out[4], &addr, sizeof(addr)); - - sclp_print(desc); - sclp_print(out); -} - -static inline void debug_print_int(const char *desc, u64 addr) -{ -#ifdef DEBUG - print_int(desc, addr); -#endif -} - -static inline void debug_print_addr(const char *desc, void *p) -{ -#ifdef DEBUG - debug_print_int(desc, (unsigned int)(unsigned long)p); -#endif -} - -/*********************************************** - * Hypercall functions * - ***********************************************/ - -#define KVM_S390_VIRTIO_NOTIFY 0 -#define KVM_S390_VIRTIO_RESET 1 -#define KVM_S390_VIRTIO_SET_STATUS 2 -#define KVM_S390_VIRTIO_CCW_NOTIFY 3 - -static inline void yield(void) -{ - asm volatile ("diag 0,0,0x44" - : : - : "memory", "cc"); -} - -#define MAX_SECTOR_SIZE 4096 - -static inline void sleep(unsigned int seconds) -{ - ulong target = get_second() + seconds; - - while (get_second() < target) { - yield(); - } -} - -static inline void *memcpy(void *s1, const void *s2, size_t n) -{ - uint8_t *p1 = s1; - const uint8_t *p2 = s2; - - while (n--) { - p1[n] = p2[n]; - } - return s1; -} - -static inline void IPL_assert(bool term, const char *message) -{ - if (!term) { - sclp_print("\n! "); - sclp_print(message); - panic(" !\n"); /* no return */ - } -} - -static inline void IPL_check(bool term, const char *message) -{ - if (!term) { - sclp_print("\n! WARNING: "); - sclp_print(message); - sclp_print(" !\n"); - } -} - -#endif /* S390_CCW_H */ diff --git a/qemu/pc-bios/s390-ccw/sclp-ascii.c b/qemu/pc-bios/s390-ccw/sclp-ascii.c deleted file mode 100644 index dc1c3e4f4..000000000 --- a/qemu/pc-bios/s390-ccw/sclp-ascii.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SCLP ASCII access driver - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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 "s390-ccw.h" -#include "sclp.h" - -static char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); - -/* Perform service call. Return 0 on success, non-zero otherwise. */ -static int sclp_service_call(unsigned int command, void *sccb) -{ - int cc; - - asm volatile( - " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - " ipm %0\n" - " srl %0,28" - : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) - : "cc", "memory"); - consume_sclp_int(); - if (cc == 3) - return -EIO; - if (cc == 2) - return -EBUSY; - return 0; -} - -static void sclp_set_write_mask(void) -{ - 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; - - sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb); -} - -void sclp_setup(void) -{ - sclp_set_write_mask(); -} - -static int _strlen(const char *str) -{ - int i; - for (i = 0; *str; i++) - str++; - return i; -} - -static void _memcpy(char *dest, const char *src, int len) -{ - int i; - for (i = 0; i < len; i++) - dest[i] = src[i]; -} - -void sclp_print(const char *str) -{ - int len = _strlen(str); - WriteEventData *sccb = (void *)_sccb; - - sccb->h.length = sizeof(WriteEventData) + len; - sccb->h.function_code = SCLP_FC_NORMAL_WRITE; - sccb->ebh.length = sizeof(EventBufferHeader) + len; - sccb->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; - sccb->ebh.flags = 0; - _memcpy(sccb->data, str, len); - - sclp_service_call(SCLP_CMD_WRITE_EVENT_DATA, sccb); -} diff --git a/qemu/pc-bios/s390-ccw/sclp.h b/qemu/pc-bios/s390-ccw/sclp.h deleted file mode 100644 index 3cbfb7893..000000000 --- a/qemu/pc-bios/s390-ccw/sclp.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SCLP ASCII access driver - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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. - */ - -#ifndef SCLP_H -#define SCLP_H - -/* SCLP command codes */ -#define SCLP_CMDW_READ_SCP_INFO 0x00020001 -#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 -#define SCLP_CMD_READ_EVENT_DATA 0x00770005 -#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 -#define SCLP_CMD_READ_EVENT_DATA 0x00770005 -#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 -#define SCLP_CMD_WRITE_EVENT_MASK 0x00780005 - -/* SCLP response codes */ -#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010 -#define SCLP_RC_NORMAL_COMPLETION 0x0020 -#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0 -#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340 -#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 -#define SCLP_RC_INVALID_FUNCTION 0x40f0 -#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 -#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0 -#define SCLP_RC_INCONSISTENT_LENGTHS 0x72f0 -#define SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR 0x73f0 -#define SCLP_RC_INVALID_MASK_LENGTH 0x74f0 - -/* Service Call Control Block (SCCB) and its elements */ - -#define SCCB_SIZE 4096 - -#define SCLP_VARIABLE_LENGTH_RESPONSE 0x80 -#define SCLP_EVENT_BUFFER_ACCEPTED 0x80 - -#define SCLP_FC_NORMAL_WRITE 0 - -typedef struct SCCBHeader { - uint16_t length; - uint8_t function_code; - uint8_t control_mask[3]; - uint16_t response_code; -} __attribute__((packed)) SCCBHeader; - -#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader)) - -typedef struct ReadInfo { - SCCBHeader h; - uint16_t rnmax; - uint8_t rnsize; -} __attribute__((packed)) ReadInfo; - -typedef struct SCCB { - SCCBHeader h; - char data[SCCB_DATA_LEN]; - } __attribute__((packed)) SCCB; - -/* SCLP event types */ -#define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a -#define SCLP_EVENT_SIGNAL_QUIESCE 0x1d - -/* SCLP event masks */ -#define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 -#define SCLP_EVENT_MASK_MSG_ASCII 0x00000040 - -#define SCLP_UNCONDITIONAL_READ 0x00 -#define SCLP_SELECTIVE_READ 0x01 - -typedef struct WriteEventMask { - SCCBHeader h; - uint16_t _reserved; - uint16_t mask_length; - uint32_t cp_receive_mask; - uint32_t cp_send_mask; - uint32_t send_mask; - uint32_t receive_mask; -} __attribute__((packed)) WriteEventMask; - -typedef struct EventBufferHeader { - uint16_t length; - uint8_t type; - uint8_t flags; - uint16_t _reserved; -} __attribute__((packed)) EventBufferHeader; - -typedef struct WriteEventData { - SCCBHeader h; - EventBufferHeader ebh; - char data[0]; -} __attribute__((packed)) WriteEventData; - -typedef struct ReadEventData { - SCCBHeader h; - EventBufferHeader ebh; - uint32_t mask; -} __attribute__((packed)) ReadEventData; - -#define __pa(x) (x) - -#endif /* SCLP_H */ diff --git a/qemu/pc-bios/s390-ccw/scsi.h b/qemu/pc-bios/s390-ccw/scsi.h deleted file mode 100644 index fc830f7e5..000000000 --- a/qemu/pc-bios/s390-ccw/scsi.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * SCSI definitions for s390 machine loader for qemu - * - * Copyright 2015 IBM Corp. - * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> - * - * 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. - */ - -#ifndef SCSI_H -#define SCSI_H - -#include "s390-ccw.h" - -#define SCSI_DEFAULT_CDB_SIZE 32 -#define SCSI_DEFAULT_SENSE_SIZE 96 - -#define CDB_STATUS_GOOD 0 -#define CDB_STATUS_CHECK_CONDITION 0x02U -#define CDB_STATUS_VALID(status) (((status) & ~0x3eU) == 0) - -#define SCSI_SENSE_CODE_MASK 0x7fU -#define SCSI_SENSE_KEY_MASK 0x0fU -#define SCSI_SENSE_KEY_NO_SENSE 0 -#define SCSI_SENSE_KEY_UNIT_ATTENTION 6 - -union ScsiLun { - uint64_t v64; /* numeric shortcut */ - uint8_t v8[8]; /* generic 8 bytes representation */ - uint16_t v16[4]; /* 4-level big-endian LUN as specified by SAM-2 */ -}; -typedef union ScsiLun ScsiLun; - -struct ScsiSense70 { - uint8_t b0; /* b0 & 7f = resp code (0x70 or 0x71) */ - uint8_t b1, b2; /* b2 & 0f = sense key */ - uint8_t u1[1 * 4 + 1 + 1 * 4]; /* b7 = N - 7 */ - uint8_t additional_sense_code; /* b12 */ - uint8_t additional_sense_code_qualifier; /* b13 */ - uint8_t u2[1 + 3 + 0]; /* up to N (<=252) bytes */ -} __attribute__((packed)); -typedef struct ScsiSense70 ScsiSense70; - -/* don't confuse with virtio-scsi response/status fields! */ - -static inline uint8_t scsi_sense_response(const void *p) -{ - return ((const ScsiSense70 *)p)->b0 & SCSI_SENSE_CODE_MASK; -} - -static inline uint8_t scsi_sense_key(const void *p) -{ - return ((const ScsiSense70 *)p)->b2 & SCSI_SENSE_KEY_MASK; -} - -#define SCSI_INQ_RDT_CDROM 0x05 - -struct ScsiInquiryStd { - uint8_t peripheral_qdt; /* b0, use (b0 & 0x1f) to get SCSI_INQ_RDT */ - uint8_t b1; /* Removable Media Bit = b1 & 0x80 */ - uint8_t spc_version; /* b2 */ - uint8_t b3; /* b3 & 0x0f == resp_data_fmt == 2, must! */ - uint8_t u1[1 + 1 + 1 + 1 + 8]; /* b4..b15 unused, b4 = (N - 1) */ - char prod_id[16]; /* "QEMU CD-ROM" is here */ - uint8_t u2[4 /* b32..b35 unused, mandatory */ - + 8 + 12 + 1 + 1 + 8 * 2 + 22 /* b36..95 unused, optional*/ - + 0]; /* b96..bN unused, vendor specific */ - /* byte N */ -} __attribute__((packed)); -typedef struct ScsiInquiryStd ScsiInquiryStd; - -struct ScsiCdbInquiry { - uint8_t command; /* b0, == 0x12 */ - uint8_t b1; /* b1, |= 0x01 (evpd) */ - uint8_t b2; /* b2; if evpd==1 */ - uint16_t alloc_len; /* b3, b4 */ - uint8_t control; /* b5 */ -} __attribute__((packed)); -typedef struct ScsiCdbInquiry ScsiCdbInquiry; - -struct ScsiCdbRead10 { - uint8_t command; /* =0x28 */ - uint8_t b1; - uint32_t lba; - uint8_t b6; - uint16_t xfer_length; - uint8_t control; -} __attribute__((packed)); -typedef struct ScsiCdbRead10 ScsiCdbRead10; - -struct ScsiCdbTestUnitReady { - uint8_t command; /* =0x00 */ - uint8_t b1_b4[4]; - uint8_t control; -} __attribute__((packed)); -typedef struct ScsiCdbTestUnitReady ScsiCdbTestUnitReady; - -struct ScsiCdbReportLuns { - uint8_t command; /* =0xa0 */ - uint8_t b1; - uint8_t select_report; /* =0x02, "all" */ - uint8_t b3_b5[3]; - uint32_t alloc_len; - uint8_t b10; - uint8_t control; -} __attribute__((packed)); -typedef struct ScsiCdbReportLuns ScsiCdbReportLuns; - -struct ScsiLunReport { - uint32_t lun_list_len; - uint32_t b4_b7; - ScsiLun lun[1]; /* space for at least 1 lun must be allocated */ -} __attribute__((packed)); -typedef struct ScsiLunReport ScsiLunReport; - -struct ScsiCdbReadCapacity16 { - uint8_t command; /* =0x9e = "service action in 16" */ - uint8_t service_action; /* 5 bits, =0x10 = "read capacity 16" */ - uint64_t b2_b9; - uint32_t alloc_len; - uint8_t b14; - uint8_t control; -} __attribute__((packed)); -typedef struct ScsiCdbReadCapacity16 ScsiCdbReadCapacity16; - -struct ScsiReadCapacity16Data { - uint64_t ret_lba; /* get it, 0..7 */ - uint32_t lb_len; /* bytes, 8..11 */ - uint8_t u1[2 + 1 * 2 + 16]; /* b12..b31, unused */ -} __attribute__((packed)); -typedef struct ScsiReadCapacity16Data ScsiReadCapacity16Data; - -static inline ScsiLun make_lun(uint16_t channel, uint16_t target, uint32_t lun) -{ - ScsiLun r = { .v64 = 0 }; - - /* See QEMU code to choose the way to handle LUNs. - * - * So, a valid LUN must have (always channel #0): - * lun[0] == 1 - * lun[1] - target, any value - * lun[2] == 0 or (LUN, MSB, 0x40 set, 0x80 clear) - * lun[3] - LUN, LSB, any value - */ - r.v8[0] = 1; - r.v8[1] = target & 0xffU; - r.v8[2] = (lun >> 8) & 0x3fU; - if (r.v8[2]) { - r.v8[2] |= 0x40; - } - r.v8[3] = lun & 0xffU; - - return r; -} - -static inline const char *scsi_cdb_status_msg(uint8_t status) -{ - static char err_msg[] = "STATUS=XX"; - uint8_t v = status & 0x3eU; - - fill_hex_val(err_msg + 7, &v, 1); - return err_msg; -} - -static inline const char *scsi_cdb_asc_msg(const void *s) -{ - static char err_msg[] = "RSPN=XX KEY=XX CODE=XX QLFR=XX"; - const ScsiSense70 *p = s; - uint8_t sr = scsi_sense_response(s); - uint8_t sk = scsi_sense_key(s); - uint8_t ac = p->additional_sense_code; - uint8_t cq = p->additional_sense_code_qualifier; - - fill_hex_val(err_msg + 5, &sr, 1); - fill_hex_val(err_msg + 12, &sk, 1); - fill_hex_val(err_msg + 20, &ac, 1); - fill_hex_val(err_msg + 28, &cq, 1); - - return err_msg; -} - -#endif /* SCSI_H */ diff --git a/qemu/pc-bios/s390-ccw/start.S b/qemu/pc-bios/s390-ccw/start.S deleted file mode 100644 index b6dd8c2fb..000000000 --- a/qemu/pc-bios/s390-ccw/start.S +++ /dev/null @@ -1,65 +0,0 @@ -/* - * First stage boot loader for virtio devices. The compiled output goes - * into the pc-bios directory of qemu. - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * Copyright 2013 IBM Corp. - * - * 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. - */ - - .globl _start -_start: - -larl %r15, stack + 0x8000 /* Set up stack */ -larl %r6, boot_value -stg %r7, 0(%r6) /* save the boot_value before any function calls */ -j main /* And call C */ - -/* - * void disabled_wait(void) - * - * stops the current guest cpu. - */ - .globl disabled_wait -disabled_wait: - larl %r1,disabled_wait_psw - lpswe 0(%r1) - - -/* - * void consume_sclp_int(void) - * - * eats one sclp interrupt - */ - .globl consume_sclp_int -consume_sclp_int: - /* enable service interrupts in cr0 */ - stctg 0,0,0(15) - oi 6(15), 0x2 - lctlg 0,0,0(15) - /* prepare external call handler */ - larl %r1, external_new_code - stg %r1, 0x1b8 - larl %r1, external_new_mask - mvc 0x1b0(8),0(%r1) - /* load enabled wait PSW */ - larl %r1, enabled_wait_psw - lpswe 0(%r1) - -external_new_code: - /* disable service interrupts in cr0 */ - stctg 0,0,0(15) - ni 6(15), 0xfd - lctlg 0,0,0(15) - br 14 - - .align 8 -disabled_wait_psw: - .quad 0x0002000180000000,0x0000000000000000 -enabled_wait_psw: - .quad 0x0302000180000000,0x0000000000000000 -external_new_mask: - .quad 0x0000000180000000 diff --git a/qemu/pc-bios/s390-ccw/virtio-scsi.c b/qemu/pc-bios/s390-ccw/virtio-scsi.c deleted file mode 100644 index 3bb48e917..000000000 --- a/qemu/pc-bios/s390-ccw/virtio-scsi.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Virtio-SCSI implementation for s390 machine loader for qemu - * - * Copyright 2015 IBM Corp. - * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> - * - * 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 "s390-ccw.h" -#include "virtio.h" -#include "scsi.h" -#include "virtio-scsi.h" - -static ScsiDevice default_scsi_device; -static VirtioScsiCmdReq req; -static VirtioScsiCmdResp resp; - -static uint8_t scsi_inquiry_std_response[256]; - -static inline void vs_assert(bool term, const char **msgs) -{ - if (!term) { - int i = 0; - - sclp_print("\n! "); - while (msgs[i]) { - sclp_print(msgs[i++]); - } - panic(" !\n"); - } -} - -static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, - const char *title) -{ - const char *mr[] = { - title, ": response ", virtio_scsi_response_msg(resp), 0 - }; - const char *ms[] = { - title, - CDB_STATUS_VALID(resp->status) ? ": " : ": invalid ", - scsi_cdb_status_msg(resp->status), - resp->status == CDB_STATUS_CHECK_CONDITION ? " " : 0, - resp->sense_len ? scsi_cdb_asc_msg(resp->sense) - : "no sense data", - scsi_sense_response(resp->sense) == 0x70 ? ", sure" : "?", - 0 - }; - - vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); - vs_assert(resp->status == CDB_STATUS_GOOD, ms); -} - -static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, - void *data, uint32_t data_size) -{ - const ScsiDevice *sdev = vdev->scsi_device; - - memset(&req, 0, sizeof(req)); - req.lun = make_lun(sdev->channel, sdev->target, sdev->lun); - memcpy(&req.cdb, cdb, cdb_size); - - memset(&resp, 0, sizeof(resp)); - resp.status = 0xff; /* set invalid */ - resp.response = 0xff; /* */ - - if (data && data_size) { - memset(data, 0, data_size); - } -} - -static inline void vs_io_assert(bool term, const char *msg) -{ - if (!term) { - virtio_scsi_verify_response(&resp, msg); - } -} - -static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, - const void *cdb, int cdb_size, - void *data, uint32_t data_size) -{ - prepare_request(vdev, cdb, cdb_size, data, data_size); - vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); -} - -/* SCSI protocol implementation routines */ - -static bool scsi_inquiry(VDev *vdev, void *data, uint32_t data_size) -{ - ScsiCdbInquiry cdb = { - .command = 0x12, - .alloc_len = data_size < 65535 ? data_size : 65535, - }; - VirtioCmd inquiry[] = { - { &req, sizeof(req), VRING_DESC_F_NEXT }, - { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, - { data, data_size, VRING_DESC_F_WRITE }, - }; - - vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); - - return virtio_scsi_response_ok(&resp); -} - -static bool scsi_test_unit_ready(VDev *vdev) -{ - ScsiCdbTestUnitReady cdb = { - .command = 0x00, - }; - VirtioCmd test_unit_ready[] = { - { &req, sizeof(req), VRING_DESC_F_NEXT }, - { &resp, sizeof(resp), VRING_DESC_F_WRITE }, - }; - - prepare_request(vdev, &cdb, sizeof(cdb), 0, 0); - virtio_run(vdev, VR_REQUEST, test_unit_ready); /* ignore errors here */ - - return virtio_scsi_response_ok(&resp); -} - -static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) -{ - ScsiCdbReportLuns cdb = { - .command = 0xa0, - .select_report = 0x02, /* REPORT ALL */ - .alloc_len = data_size, - }; - VirtioCmd report_luns[] = { - { &req, sizeof(req), VRING_DESC_F_NEXT }, - { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, - { data, data_size, VRING_DESC_F_WRITE }, - }; - - vs_run("report luns", report_luns, - vdev, &cdb, sizeof(cdb), data, data_size); - - return virtio_scsi_response_ok(&resp); -} - -static bool scsi_read_10(VDev *vdev, - ulong sector, int sectors, void *data) -{ - int f = vdev->blk_factor; - unsigned int data_size = sectors * virtio_get_block_size() * f; - ScsiCdbRead10 cdb = { - .command = 0x28, - .lba = sector * f, - .xfer_length = sectors * f, - }; - VirtioCmd read_10[] = { - { &req, sizeof(req), VRING_DESC_F_NEXT }, - { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, - { data, data_size * f, VRING_DESC_F_WRITE }, - }; - - debug_print_int("read_10 sector", sector); - debug_print_int("read_10 sectors", sectors); - - vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); - - return virtio_scsi_response_ok(&resp); -} - -static bool scsi_read_capacity(VDev *vdev, - void *data, uint32_t data_size) -{ - ScsiCdbReadCapacity16 cdb = { - .command = 0x9e, /* SERVICE_ACTION_IN_16 */ - .service_action = 0x10, /* SA_READ_CAPACITY */ - .alloc_len = data_size, - }; - VirtioCmd read_capacity_16[] = { - { &req, sizeof(req), VRING_DESC_F_NEXT }, - { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, - { data, data_size, VRING_DESC_F_WRITE }, - }; - - vs_run("read capacity", read_capacity_16, - vdev, &cdb, sizeof(cdb), data, data_size); - - return virtio_scsi_response_ok(&resp); -} - -/* virtio-scsi routines */ - -static void virtio_scsi_locate_device(VDev *vdev) -{ - const uint16_t channel = 0; /* again, it's what QEMU does */ - uint16_t target; - static uint8_t data[16 + 8 * 63]; - ScsiLunReport *r = (void *) data; - ScsiDevice *sdev = vdev->scsi_device; - int i, luns; - - /* QEMU has hardcoded channel #0 in many places. - * If this hardcoded value is ever changed, we'll need to add code for - * vdev->config.scsi.max_channel != 0 here. - */ - debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); - debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); - debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); - - for (target = 0; target <= vdev->config.scsi.max_target; target++) { - sdev->channel = channel; - sdev->target = target; /* sdev->lun will be 0 here */ - if (!scsi_report_luns(vdev, data, sizeof(data))) { - if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { - continue; - } - print_int("target", target); - virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); - } - if (r->lun_list_len == 0) { - print_int("no LUNs for target", target); - continue; - } - luns = r->lun_list_len / 8; - debug_print_int("LUNs reported", luns); - if (luns == 1) { - /* There is no ",lun=#" arg for -device or ",lun=0" given. - * Hence, the only LUN reported. - * Usually, it's 0. - */ - sdev->lun = r->lun[0].v16[0]; /* it's returned this way */ - debug_print_int("Have to use LUN", sdev->lun); - return; /* we have to use this device */ - } - for (i = 0; i < luns; i++) { - if (r->lun[i].v64) { - /* Look for non-zero LUN - we have where to choose from */ - sdev->lun = r->lun[i].v16[0]; - debug_print_int("Will use LUN", sdev->lun); - return; /* we have found a device */ - } - } - } - panic("\n! Cannot locate virtio-scsi device !\n"); -} - -int virtio_scsi_read_many(VDev *vdev, - ulong sector, void *load_addr, int sec_num) -{ - if (!scsi_read_10(vdev, sector, sec_num, load_addr)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); - } - - return 0; -} - -static bool virtio_scsi_inquiry_response_is_cdrom(void *data) -{ - const ScsiInquiryStd *response = data; - const int resp_data_fmt = response->b3 & 0x0f; - int i; - - IPL_check(resp_data_fmt == 2, "Wrong INQUIRY response format"); - if (resp_data_fmt != 2) { - return false; /* cannot decode */ - } - - if ((response->peripheral_qdt & 0x1f) == SCSI_INQ_RDT_CDROM) { - return true; - } - - for (i = 0; i < sizeof(response->prod_id); i++) { - if (response->prod_id[i] != QEMU_CDROM_SIGNATURE[i]) { - return false; - } - } - return true; -} - -static void scsi_parse_capacity_report(void *data, - uint64_t *last_lba, uint32_t *lb_len) -{ - ScsiReadCapacity16Data *p = data; - - if (last_lba) { - *last_lba = p->ret_lba; - } - - if (lb_len) { - *lb_len = p->lb_len; - } -} - -void virtio_scsi_setup(VDev *vdev) -{ - int retry_test_unit_ready = 3; - uint8_t data[256]; - uint32_t data_size = sizeof(data); - - vdev->scsi_device = &default_scsi_device; - virtio_scsi_locate_device(vdev); - - /* We have to "ping" the device before it becomes readable */ - while (!scsi_test_unit_ready(vdev)) { - - if (!virtio_scsi_response_ok(&resp)) { - uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; - uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; - - IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); - - IPL_assert(retry_test_unit_ready && code == 0x70 && - sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, - "virtio-scsi:setup: cannot retry"); - - /* retry on CHECK_CONDITION/UNIT_ATTENTION as it - * may not designate a real error, but it may be - * a result of device reset, etc. - */ - retry_test_unit_ready--; - sleep(1); - continue; - } - - virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); - } - - /* read and cache SCSI INQUIRY response */ - if (!scsi_inquiry(vdev, scsi_inquiry_std_response, - sizeof(scsi_inquiry_std_response))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); - } - - if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { - sclp_print("SCSI CD-ROM detected.\n"); - vdev->is_cdrom = true; - vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; - } - - if (!scsi_read_capacity(vdev, data, data_size)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); - } - scsi_parse_capacity_report(data, &vdev->scsi_last_block, - (uint32_t *) &vdev->scsi_block_size); -} diff --git a/qemu/pc-bios/s390-ccw/virtio-scsi.h b/qemu/pc-bios/s390-ccw/virtio-scsi.h deleted file mode 100644 index f50b38b18..000000000 --- a/qemu/pc-bios/s390-ccw/virtio-scsi.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Virtio-SCSI definitions for s390 machine loader for qemu - * - * Copyright 2015 IBM Corp. - * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> - * - * 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. - */ - -#ifndef VIRTIO_SCSI_H -#define VIRTIO_SCSI_H - -#include "s390-ccw.h" -#include "virtio.h" -#include "scsi.h" - -#define VIRTIO_SCSI_CDB_SIZE SCSI_DEFAULT_CDB_SIZE -#define VIRTIO_SCSI_SENSE_SIZE SCSI_DEFAULT_SENSE_SIZE - -/* command-specific response values */ -#define VIRTIO_SCSI_S_OK 0x00 -#define VIRTIO_SCSI_S_BAD_TARGET 0x03 - -#define QEMU_CDROM_SIGNATURE "QEMU CD-ROM " - -enum virtio_scsi_vq_id { - VR_CONTROL = 0, - VR_EVENT = 1, - VR_REQUEST = 2, -}; - -struct VirtioScsiCmdReq { - ScsiLun lun; - uint64_t id; - uint8_t task_attr; /* = 0 = VIRTIO_SCSI_S_SIMPLE */ - uint8_t prio; - uint8_t crn; /* = 0 */ - uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; -} __attribute__((packed)); -typedef struct VirtioScsiCmdReq VirtioScsiCmdReq; - -struct VirtioScsiCmdResp { - uint32_t sense_len; - uint32_t residual; - uint16_t status_qualifier; - uint8_t status; /* first check for .response */ - uint8_t response; /* then for .status */ - uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; -} __attribute__((packed)); -typedef struct VirtioScsiCmdResp VirtioScsiCmdResp; - -static inline const char *virtio_scsi_response_msg(const VirtioScsiCmdResp *r) -{ - static char err_msg[] = "VS RESP=XX"; - uint8_t v = r->response; - - fill_hex_val(err_msg + 8, &v, 1); - return err_msg; -} - -static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) -{ - return r->response == VIRTIO_SCSI_S_OK && r->status == CDB_STATUS_GOOD; -} - -void virtio_scsi_setup(VDev *vdev); -int virtio_scsi_read_many(VDev *vdev, - ulong sector, void *load_addr, int sec_num); - -#endif /* VIRTIO_SCSI_H */ diff --git a/qemu/pc-bios/s390-ccw/virtio.c b/qemu/pc-bios/s390-ccw/virtio.c deleted file mode 100644 index 1d34e8c1a..000000000 --- a/qemu/pc-bios/s390-ccw/virtio.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Virtio driver bits - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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 "s390-ccw.h" -#include "virtio.h" -#include "virtio-scsi.h" - -#define VRING_WAIT_REPLY_TIMEOUT 3 - -static VRing block[VIRTIO_MAX_VQS]; -static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS] - __attribute__((__aligned__(PAGE_SIZE))); - -static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); - -static VDev vdev = { - .nr_vqs = 1, - .vrings = block, - .cmd_vr_idx = 0, - .ring_area = ring_area, - .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT, - .schid = { .one = 1 }, - .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE, - .blk_factor = 1, -}; - -VDev *virtio_get_device(void) -{ - return &vdev; -} - -VirtioDevType virtio_get_device_type(void) -{ - return vdev.senseid.cu_model; -} - -/* virtio spec v1.0 para 4.3.3.2 */ -static long kvm_hypercall(unsigned long nr, unsigned long param1, - unsigned long param2, unsigned long param3) -{ - register ulong r_nr asm("1") = nr; - register ulong r_param1 asm("2") = param1; - register ulong r_param2 asm("3") = param2; - register ulong r_param3 asm("4") = param3; - register long retval asm("2"); - - asm volatile ("diag 2,4,0x500" - : "=d" (retval) - : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3) - : "memory", "cc"); - - return retval; -} - -static long virtio_notify(SubChannelId schid, int vq_idx, long cookie) -{ - return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, - vq_idx, cookie); -} - -/*********************************************** - * Virtio functions * - ***********************************************/ - -static int drain_irqs(SubChannelId schid) -{ - Irb irb = {}; - int r = 0; - - while (1) { - /* FIXME: make use of TPI, for that enable subchannel and isc */ - if (tsch(schid, &irb)) { - /* Might want to differentiate error codes later on. */ - if (irb.scsw.cstat) { - r = -EIO; - } else if (irb.scsw.dstat != 0xc) { - r = -EIO; - } - return r; - } - } -} - -static int run_ccw(VDev *vdev, int cmd, void *ptr, int len) -{ - Ccw1 ccw = {}; - CmdOrb orb = {}; - Schib schib; - int r; - - /* start command processing */ - stsch_err(vdev->schid, &schib); - schib.scsw.ctrl = SCSW_FCTL_START_FUNC; - msch(vdev->schid, &schib); - - /* start subchannel command */ - orb.fmt = 1; - orb.cpa = (u32)(long)&ccw; - orb.lpm = 0x80; - - ccw.cmd_code = cmd; - ccw.cda = (long)ptr; - ccw.count = len; - - r = ssch(vdev->schid, &orb); - /* - * XXX Wait until device is done processing the CCW. For now we can - * assume that a simple tsch will have finished the CCW processing, - * but the architecture allows for asynchronous operation - */ - if (!r) { - r = drain_irqs(vdev->schid); - } - return r; -} - -static void vring_init(VRing *vr, VqInfo *info) -{ - void *p = (void *) info->queue; - - debug_print_addr("init p", p); - vr->id = info->index; - vr->num = info->num; - vr->desc = p; - vr->avail = p + info->num * sizeof(VRingDesc); - vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num] - + info->align - 1) & ~(info->align - 1)); - - /* Zero out all relevant field */ - vr->avail->flags = 0; - vr->avail->idx = 0; - - /* We're running with interrupts off anyways, so don't bother */ - vr->used->flags = VRING_USED_F_NO_NOTIFY; - vr->used->idx = 0; - vr->used_idx = 0; - vr->next_idx = 0; - vr->cookie = 0; - - debug_print_addr("init vr", vr); -} - -static bool vring_notify(VRing *vr) -{ - vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie); - return vr->cookie >= 0; -} - -static void vring_send_buf(VRing *vr, void *p, int len, int flags) -{ - /* For follow-up chains we need to keep the first entry point */ - if (!(flags & VRING_HIDDEN_IS_CHAIN)) { - vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx; - } - - vr->desc[vr->next_idx].addr = (ulong)p; - vr->desc[vr->next_idx].len = len; - vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN; - vr->desc[vr->next_idx].next = vr->next_idx; - vr->desc[vr->next_idx].next++; - vr->next_idx++; - - /* Chains only have a single ID */ - if (!(flags & VRING_DESC_F_NEXT)) { - vr->avail->idx++; - } -} - -static u64 get_clock(void) -{ - u64 r; - - asm volatile("stck %0" : "=Q" (r) : : "cc"); - return r; -} - -ulong get_second(void) -{ - return (get_clock() >> 12) / 1000000; -} - -static int vr_poll(VRing *vr) -{ - if (vr->used->idx == vr->used_idx) { - vring_notify(vr); - yield(); - return 0; - } - - vr->used_idx = vr->used->idx; - vr->next_idx = 0; - vr->desc[0].len = 0; - vr->desc[0].flags = 0; - return 1; /* vr has been updated */ -} - -/* - * Wait for the host to reply. - * - * timeout is in seconds if > 0. - * - * Returns 0 on success, 1 on timeout. - */ -static int vring_wait_reply(void) -{ - ulong target_second = get_second() + vdev.wait_reply_timeout; - - /* Wait for any queue to be updated by the host */ - do { - int i, r = 0; - - for (i = 0; i < vdev.nr_vqs; i++) { - r += vr_poll(&vdev.vrings[i]); - } - yield(); - if (r) { - return 0; - } - } while (!vdev.wait_reply_timeout || (get_second() < target_second)); - - return 1; -} - -int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) -{ - VRing *vr = &vdev->vrings[vqid]; - int i = 0; - - do { - vring_send_buf(vr, cmd[i].data, cmd[i].size, - cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0)); - } while (cmd[i++].flags & VRING_DESC_F_NEXT); - - vring_wait_reply(); - if (drain_irqs(vr->schid)) { - return -1; - } - return 0; -} - -/*********************************************** - * Virtio block * - ***********************************************/ - -static int virtio_blk_read_many(VDev *vdev, - ulong sector, void *load_addr, int sec_num) -{ - VirtioBlkOuthdr out_hdr; - u8 status; - VRing *vr = &vdev->vrings[vdev->cmd_vr_idx]; - - /* Tell the host we want to read */ - out_hdr.type = VIRTIO_BLK_T_IN; - out_hdr.ioprio = 99; - out_hdr.sector = virtio_sector_adjust(sector); - - vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); - - /* This is where we want to receive data */ - vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num, - VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | - VRING_DESC_F_NEXT); - - /* status field */ - vring_send_buf(vr, &status, sizeof(u8), - VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN); - - /* Now we can tell the host to read */ - vring_wait_reply(); - - if (drain_irqs(vr->schid)) { - /* Well, whatever status is supposed to contain... */ - status = 1; - } - return status; -} - -int virtio_read_many(ulong sector, void *load_addr, int sec_num) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return virtio_blk_read_many(&vdev, sector, load_addr, sec_num); - case VIRTIO_ID_SCSI: - return virtio_scsi_read_many(&vdev, sector, load_addr, sec_num); - } - panic("\n! No readable IPL device !\n"); - return -1; -} - -unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, - ulong subchan_id, void *load_addr) -{ - u8 status; - int sec = rec_list1; - int sec_num = ((rec_list2 >> 32) & 0xffff) + 1; - int sec_len = rec_list2 >> 48; - ulong addr = (ulong)load_addr; - - if (sec_len != virtio_get_block_size()) { - return -1; - } - - sclp_print("."); - status = virtio_read_many(sec, (void *)addr, sec_num); - if (status) { - panic("I/O Error"); - } - addr += sec_num * virtio_get_block_size(); - - return addr; -} - -int virtio_read(ulong sector, void *load_addr) -{ - return virtio_read_many(sector, load_addr, 1); -} - -/* - * Other supported value pairs, if any, would need to be added here. - * Note: head count is always 15. - */ -static inline u8 virtio_eckd_sectors_for_block_size(int size) -{ - switch (size) { - case 512: - return 49; - case 1024: - return 33; - case 2048: - return 21; - case 4096: - return 12; - } - return 0; -} - -VirtioGDN virtio_guessed_disk_nature(void) -{ - return vdev.guessed_disk_nature; -} - -void virtio_assume_scsi(void) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; - vdev.config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; - vdev.config.blk.physical_block_exp = 0; - vdev.blk_factor = 1; - break; - case VIRTIO_ID_SCSI: - vdev.scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; - break; - } -} - -void virtio_assume_iso9660(void) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; - vdev.config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE; - vdev.config.blk.physical_block_exp = 0; - vdev.blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE; - break; - case VIRTIO_ID_SCSI: - vdev.scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; - break; - } -} - -void virtio_assume_eckd(void) -{ - vdev.guessed_disk_nature = VIRTIO_GDN_DASD; - vdev.blk_factor = 1; - vdev.config.blk.physical_block_exp = 0; - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev.config.blk.blk_size = 4096; - break; - case VIRTIO_ID_SCSI: - vdev.config.blk.blk_size = vdev.scsi_block_size; - break; - } - vdev.config.blk.geometry.heads = 15; - vdev.config.blk.geometry.sectors = - virtio_eckd_sectors_for_block_size(vdev.config.blk.blk_size); -} - -bool virtio_disk_is_scsi(void) -{ - if (vdev.guessed_disk_nature == VIRTIO_GDN_SCSI) { - return true; - } - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev.config.blk.geometry.heads == 255) - && (vdev.config.blk.geometry.sectors == 63) - && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); - case VIRTIO_ID_SCSI: - return true; - } - return false; -} - -bool virtio_disk_is_eckd(void) -{ - const int block_size = virtio_get_block_size(); - - if (vdev.guessed_disk_nature == VIRTIO_GDN_DASD) { - return true; - } - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return (vdev.config.blk.geometry.heads == 15) - && (vdev.config.blk.geometry.sectors == - virtio_eckd_sectors_for_block_size(block_size)); - case VIRTIO_ID_SCSI: - return false; - } - return false; -} - -bool virtio_ipl_disk_is_valid(void) -{ - return virtio_disk_is_scsi() || virtio_disk_is_eckd(); -} - -int virtio_get_block_size(void) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return vdev.config.blk.blk_size << vdev.config.blk.physical_block_exp; - case VIRTIO_ID_SCSI: - return vdev.scsi_block_size; - } - return 0; -} - -uint8_t virtio_get_heads(void) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return vdev.config.blk.geometry.heads; - case VIRTIO_ID_SCSI: - return vdev.guessed_disk_nature == VIRTIO_GDN_DASD - ? vdev.config.blk.geometry.heads : 255; - } - return 0; -} - -uint8_t virtio_get_sectors(void) -{ - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return vdev.config.blk.geometry.sectors; - case VIRTIO_ID_SCSI: - return vdev.guessed_disk_nature == VIRTIO_GDN_DASD - ? vdev.config.blk.geometry.sectors : 63; - } - return 0; -} - -uint64_t virtio_get_blocks(void) -{ - const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE; - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - return vdev.config.blk.capacity / factor; - case VIRTIO_ID_SCSI: - return vdev.scsi_last_block / factor; - } - return 0; -} - -static void virtio_setup_ccw(VDev *vdev) -{ - int i, cfg_size = 0; - unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; - - IPL_assert(virtio_is_supported(vdev->schid), "PE"); - /* device ID has been established now */ - - vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ - vdev->guessed_disk_nature = VIRTIO_GDN_NONE; - - run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0); - - switch (vdev->senseid.cu_model) { - case VIRTIO_ID_BLOCK: - vdev->nr_vqs = 1; - vdev->cmd_vr_idx = 0; - cfg_size = sizeof(vdev->config.blk); - break; - case VIRTIO_ID_SCSI: - vdev->nr_vqs = 3; - vdev->cmd_vr_idx = VR_REQUEST; - cfg_size = sizeof(vdev->config.scsi); - break; - default: - panic("Unsupported virtio device\n"); - } - IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0, - "Could not get block device configuration"); - - /* - * Skipping CCW_CMD_READ_FEAT. We're not doing anything fancy, and - * we'll just stop dead anyway if anything does not work like we - * expect it. - */ - - for (i = 0; i < vdev->nr_vqs; i++) { - VqInfo info = { - .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE), - .align = KVM_S390_VIRTIO_RING_ALIGN, - .index = i, - .num = 0, - }; - VqConfig config = { - .index = i, - .num = 0, - }; - - IPL_assert( - run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0, - "Could not get block device VQ configuration"); - info.num = config.num; - vring_init(&vdev->vrings[i], &info); - vdev->vrings[i].schid = vdev->schid; - IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0, - "Cannot set VQ info"); - } - IPL_assert( - run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0, - "Could not write status to host"); -} - -void virtio_setup_device(SubChannelId schid) -{ - vdev.schid = schid; - virtio_setup_ccw(&vdev); - - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - sclp_print("Using virtio-blk.\n"); - if (!virtio_ipl_disk_is_valid()) { - /* make sure all getters but blocksize return 0 for - * invalid IPL disk - */ - memset(&vdev.config.blk, 0, sizeof(vdev.config.blk)); - virtio_assume_scsi(); - } - break; - case VIRTIO_ID_SCSI: - IPL_assert(vdev.config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, - "Config: sense size mismatch"); - IPL_assert(vdev.config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, - "Config: CDB size mismatch"); - - sclp_print("Using virtio-scsi.\n"); - virtio_scsi_setup(&vdev); - break; - default: - panic("\n! No IPL device available !\n"); - } -} - -bool virtio_is_supported(SubChannelId schid) -{ - vdev.schid = schid; - memset(&vdev.senseid, 0, sizeof(vdev.senseid)); - /* run sense id command */ - if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) { - return false; - } - if (vdev.senseid.cu_type == 0x3832) { - switch (vdev.senseid.cu_model) { - case VIRTIO_ID_BLOCK: - case VIRTIO_ID_SCSI: - return true; - } - } - return false; -} - -int enable_mss_facility(void) -{ - int ret; - ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page; - - memset(sda_area, 0, PAGE_SIZE); - sda_area->request.length = 0x0400; - sda_area->request.code = 0x0031; - sda_area->operation_code = 0x2; - - ret = chsc(sda_area); - if ((ret == 0) && (sda_area->response.code == 0x0001)) { - return 0; - } - return -EIO; -} diff --git a/qemu/pc-bios/s390-ccw/virtio.h b/qemu/pc-bios/s390-ccw/virtio.h deleted file mode 100644 index 3c6e91510..000000000 --- a/qemu/pc-bios/s390-ccw/virtio.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Virtio driver bits - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * 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. - */ - -#ifndef VIRTIO_H -#define VIRTIO_H - -#include "s390-ccw.h" - -/* Status byte for guest to report progress, and synchronize features. */ -/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ -#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 -/* We have found a driver for the device. */ -#define VIRTIO_CONFIG_S_DRIVER 2 -/* Driver has used its parts of the config, and is happy */ -#define VIRTIO_CONFIG_S_DRIVER_OK 4 -/* We've given up on this device. */ -#define VIRTIO_CONFIG_S_FAILED 0x80 - -enum VirtioDevType { - VIRTIO_ID_NET = 1, - VIRTIO_ID_BLOCK = 2, - VIRTIO_ID_CONSOLE = 3, - VIRTIO_ID_BALLOON = 5, - VIRTIO_ID_SCSI = 8, -}; -typedef enum VirtioDevType VirtioDevType; - -struct VirtioDevHeader { - VirtioDevType type:8; - uint8_t num_vq; - uint8_t feature_len; - uint8_t config_len; - uint8_t status; - uint8_t vqconfig[]; -} __attribute__((packed)); -typedef struct VirtioDevHeader VirtioDevHeader; - -struct VirtioVqConfig { - uint64_t token; - uint64_t address; - uint16_t num; - uint8_t pad[6]; -} __attribute__((packed)); -typedef struct VirtioVqConfig VirtioVqConfig; - -struct VqInfo { - uint64_t queue; - uint32_t align; - uint16_t index; - uint16_t num; -} __attribute__((packed)); -typedef struct VqInfo VqInfo; - -struct VqConfig { - uint16_t index; - uint16_t num; -} __attribute__((packed)); -typedef struct VqConfig VqConfig; - -struct VirtioDev { - VirtioDevHeader *header; - VirtioVqConfig *vqconfig; - char *host_features; - char *guest_features; - char *config; -}; -typedef struct VirtioDev VirtioDev; - -#define VIRTIO_RING_SIZE (PAGE_SIZE * 8) -#define VIRTIO_MAX_VQS 3 -#define KVM_S390_VIRTIO_RING_ALIGN 4096 - -#define VRING_USED_F_NO_NOTIFY 1 - -/* This marks a buffer as continuing via the next field. */ -#define VRING_DESC_F_NEXT 1 -/* This marks a buffer as write-only (otherwise read-only). */ -#define VRING_DESC_F_WRITE 2 -/* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 - -/* Internal flag to mark follow-up segments as such */ -#define VRING_HIDDEN_IS_CHAIN 256 - -/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ -struct VRingDesc { - /* Address (guest-physical). */ - uint64_t addr; - /* Length. */ - uint32_t len; - /* The flags as indicated above. */ - uint16_t flags; - /* We chain unused descriptors via this, too */ - uint16_t next; -} __attribute__((packed)); -typedef struct VRingDesc VRingDesc; - -struct VRingAvail { - uint16_t flags; - uint16_t idx; - uint16_t ring[]; -} __attribute__((packed)); -typedef struct VRingAvail VRingAvail; - -/* uint32_t is used here for ids for padding reasons. */ -struct VRingUsedElem { - /* Index of start of used descriptor chain. */ - uint32_t id; - /* Total length of the descriptor chain which was used (written to) */ - uint32_t len; -} __attribute__((packed)); -typedef struct VRingUsedElem VRingUsedElem; - -struct VRingUsed { - uint16_t flags; - uint16_t idx; - VRingUsedElem ring[]; -} __attribute__((packed)); -typedef struct VRingUsed VRingUsed; - -struct VRing { - unsigned int num; - int next_idx; - int used_idx; - VRingDesc *desc; - VRingAvail *avail; - VRingUsed *used; - SubChannelId schid; - long cookie; - int id; -}; -typedef struct VRing VRing; - - -/*********************************************** - * Virtio block * - ***********************************************/ - -/* - * Command types - * - * Usage is a bit tricky as some bits are used as flags and some are not. - * - * Rules: - * VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or - * VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own - * and may not be combined with any of the other flags. - */ - -/* These two define direction. */ -#define VIRTIO_BLK_T_IN 0 -#define VIRTIO_BLK_T_OUT 1 - -/* This bit says it's a scsi command, not an actual read or write. */ -#define VIRTIO_BLK_T_SCSI_CMD 2 - -/* Cache flush command */ -#define VIRTIO_BLK_T_FLUSH 4 - -/* Barrier before this op. */ -#define VIRTIO_BLK_T_BARRIER 0x80000000 - -/* This is the first element of the read scatter-gather list. */ -struct VirtioBlkOuthdr { - /* VIRTIO_BLK_T* */ - uint32_t type; - /* io priority. */ - uint32_t ioprio; - /* Sector (ie. 512 byte offset) */ - uint64_t sector; -}; -typedef struct VirtioBlkOuthdr VirtioBlkOuthdr; - -struct VirtioBlkConfig { - uint64_t capacity; /* in 512-byte sectors */ - uint32_t size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ - uint32_t seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ - - struct VirtioBlkGeometry { - uint16_t cylinders; - uint8_t heads; - uint8_t sectors; - } geometry; /* (if VIRTIO_BLK_F_GEOMETRY) */ - - uint32_t blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ - - /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ - uint8_t physical_block_exp; /* exponent for physical blk per logical blk */ - uint8_t alignment_offset; /* alignment offset in logical blocks */ - uint16_t min_io_size; /* min I/O size without performance penalty - in logical blocks */ - uint32_t opt_io_size; /* optimal sustained I/O size in logical blks */ - - uint8_t wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ -} __attribute__((packed)); -typedef struct VirtioBlkConfig VirtioBlkConfig; - -enum guessed_disk_nature_type { - VIRTIO_GDN_NONE = 0, - VIRTIO_GDN_DASD = 1, - VIRTIO_GDN_CDROM = 2, - VIRTIO_GDN_SCSI = 3, -}; -typedef enum guessed_disk_nature_type VirtioGDN; - -VirtioGDN virtio_guessed_disk_nature(void); -void virtio_assume_scsi(void); -void virtio_assume_eckd(void); -void virtio_assume_iso9660(void); - -extern bool virtio_disk_is_scsi(void); -extern bool virtio_disk_is_eckd(void); -extern bool virtio_ipl_disk_is_valid(void); -extern int virtio_get_block_size(void); -extern uint8_t virtio_get_heads(void); -extern uint8_t virtio_get_sectors(void); -extern uint64_t virtio_get_blocks(void); -extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); - -#define VIRTIO_SECTOR_SIZE 512 -#define VIRTIO_ISO_BLOCK_SIZE 2048 -#define VIRTIO_SCSI_BLOCK_SIZE 512 - -static inline ulong virtio_sector_adjust(ulong sector) -{ - return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); -} - -struct VirtioScsiConfig { - uint32_t num_queues; - uint32_t seg_max; - uint32_t max_sectors; - uint32_t cmd_per_lun; - uint32_t event_info_size; - uint32_t sense_size; - uint32_t cdb_size; - uint16_t max_channel; - uint16_t max_target; - uint32_t max_lun; -} __attribute__((packed)); -typedef struct VirtioScsiConfig VirtioScsiConfig; - -struct ScsiDevice { - uint16_t channel; /* Always 0 in QEMU */ - uint16_t target; /* will be scanned over */ - uint32_t lun; /* will be reported */ -}; -typedef struct ScsiDevice ScsiDevice; - -struct VDev { - int nr_vqs; - VRing *vrings; - int cmd_vr_idx; - void *ring_area; - long wait_reply_timeout; - VirtioGDN guessed_disk_nature; - SubChannelId schid; - SenseId senseid; - union { - VirtioBlkConfig blk; - VirtioScsiConfig scsi; - } config; - ScsiDevice *scsi_device; - bool is_cdrom; - int scsi_block_size; - int blk_factor; - uint64_t scsi_last_block; - uint32_t scsi_dev_cyls; - uint8_t scsi_dev_heads; -}; -typedef struct VDev VDev; - -VDev *virtio_get_device(void); -VirtioDevType virtio_get_device_type(void); - -struct VirtioCmd { - void *data; - int size; - int flags; -}; -typedef struct VirtioCmd VirtioCmd; - -int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); - -#endif /* VIRTIO_H */ |