mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 15:23:53 -06:00
s390-bios: Support booting from real dasd device
Allows guest to boot from a vfio configured real dasd device. Signed-off-by: Jason J. Herne <jjherne@linux.ibm.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Message-Id: <1554388475-18329-16-git-send-email-jjherne@linux.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
parent
69333c36dc
commit
efa47d36da
7 changed files with 404 additions and 1 deletions
|
@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
|
|||
.PHONY : all clean build-all
|
||||
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \
|
||||
virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o
|
||||
virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o
|
||||
|
||||
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
||||
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
||||
|
|
235
pc-bios/s390-ccw/dasd-ipl.c
Normal file
235
pc-bios/s390-ccw/dasd-ipl.c
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* S390 IPL (boot) from a real DASD device via vfio framework.
|
||||
*
|
||||
* Copyright (c) 2019 Jason J. Herne <jjherne@us.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 "libc.h"
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "dasd-ipl.h"
|
||||
#include "helper.h"
|
||||
|
||||
static char prefix_page[PAGE_SIZE * 2]
|
||||
__attribute__((__aligned__(PAGE_SIZE * 2)));
|
||||
|
||||
static void enable_prefixing(void)
|
||||
{
|
||||
memcpy(&prefix_page, lowcore, 4096);
|
||||
set_prefix(ptr2u32(&prefix_page));
|
||||
}
|
||||
|
||||
static void disable_prefixing(void)
|
||||
{
|
||||
set_prefix(0);
|
||||
/* Copy io interrupt info back to low core */
|
||||
memcpy((void *)&lowcore->subchannel_id, prefix_page + 0xB8, 12);
|
||||
}
|
||||
|
||||
static bool is_read_tic_ccw_chain(Ccw0 *ccw)
|
||||
{
|
||||
Ccw0 *next_ccw = ccw + 1;
|
||||
|
||||
return ((ccw->cmd_code == CCW_CMD_DASD_READ ||
|
||||
ccw->cmd_code == CCW_CMD_DASD_READ_MT) &&
|
||||
ccw->chain && next_ccw->cmd_code == CCW_CMD_TIC);
|
||||
}
|
||||
|
||||
static bool dynamic_cp_fixup(uint32_t ccw_addr, uint32_t *next_cpa)
|
||||
{
|
||||
Ccw0 *cur_ccw = (Ccw0 *)(uint64_t)ccw_addr;
|
||||
Ccw0 *tic_ccw;
|
||||
|
||||
while (true) {
|
||||
/* Skip over inline TIC (it might not have the chain bit on) */
|
||||
if (cur_ccw->cmd_code == CCW_CMD_TIC &&
|
||||
cur_ccw->cda == ptr2u32(cur_ccw) - 8) {
|
||||
cur_ccw += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cur_ccw->chain) {
|
||||
break;
|
||||
}
|
||||
if (is_read_tic_ccw_chain(cur_ccw)) {
|
||||
/*
|
||||
* Breaking a chain of CCWs may alter the semantics or even the
|
||||
* validity of a channel program. The heuristic implemented below
|
||||
* seems to work well in practice for the channel programs
|
||||
* generated by zipl.
|
||||
*/
|
||||
tic_ccw = cur_ccw + 1;
|
||||
*next_cpa = tic_ccw->cda;
|
||||
cur_ccw->chain = 0;
|
||||
return true;
|
||||
}
|
||||
cur_ccw += 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
|
||||
uint32_t cpa)
|
||||
{
|
||||
bool has_next;
|
||||
uint32_t next_cpa = 0;
|
||||
int rc;
|
||||
|
||||
do {
|
||||
has_next = dynamic_cp_fixup(cpa, &next_cpa);
|
||||
|
||||
print_int("executing ccw chain at ", cpa);
|
||||
enable_prefixing();
|
||||
rc = do_cio(schid, cutype, cpa, CCW_FMT0);
|
||||
disable_prefixing();
|
||||
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
cpa = next_cpa;
|
||||
} while (has_next);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void make_readipl(void)
|
||||
{
|
||||
Ccw0 *ccwIplRead = (Ccw0 *)0x00;
|
||||
|
||||
/* Create Read IPL ccw at address 0 */
|
||||
ccwIplRead->cmd_code = CCW_CMD_READ_IPL;
|
||||
ccwIplRead->cda = 0x00; /* Read into address 0x00 in main memory */
|
||||
ccwIplRead->chain = 0; /* Chain flag */
|
||||
ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
|
||||
}
|
||||
|
||||
static void run_readipl(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
|
||||
panic("dasd-ipl: Failed to run Read IPL channel program\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The architecture states that IPL1 data should consist of a psw followed by
|
||||
* format-0 READ and TIC CCWs. Let's sanity check.
|
||||
*/
|
||||
static void check_ipl1(void)
|
||||
{
|
||||
Ccw0 *ccwread = (Ccw0 *)0x08;
|
||||
Ccw0 *ccwtic = (Ccw0 *)0x10;
|
||||
|
||||
if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
|
||||
ccwtic->cmd_code != CCW_CMD_TIC) {
|
||||
panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void check_ipl2(uint32_t ipl2_addr)
|
||||
{
|
||||
Ccw0 *ccw = u32toptr(ipl2_addr);
|
||||
|
||||
if (ipl2_addr == 0x00) {
|
||||
panic("IPL2 address invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
if (ccw->cmd_code == 0x00) {
|
||||
panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t read_ipl2_addr(void)
|
||||
{
|
||||
Ccw0 *ccwtic = (Ccw0 *)0x10;
|
||||
|
||||
return ccwtic->cda;
|
||||
}
|
||||
|
||||
static void ipl1_fixup(void)
|
||||
{
|
||||
Ccw0 *ccwSeek = (Ccw0 *) 0x08;
|
||||
Ccw0 *ccwSearchID = (Ccw0 *) 0x10;
|
||||
Ccw0 *ccwSearchTic = (Ccw0 *) 0x18;
|
||||
Ccw0 *ccwRead = (Ccw0 *) 0x20;
|
||||
CcwSeekData *seekData = (CcwSeekData *) 0x30;
|
||||
CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38;
|
||||
|
||||
/* move IPL1 CCWs to make room for CCWs needed to locate record 2 */
|
||||
memcpy(ccwRead, (void *)0x08, 16);
|
||||
|
||||
/* Disable chaining so we don't TIC to IPL2 channel program */
|
||||
ccwRead->chain = 0x00;
|
||||
|
||||
ccwSeek->cmd_code = CCW_CMD_DASD_SEEK;
|
||||
ccwSeek->cda = ptr2u32(seekData);
|
||||
ccwSeek->chain = 1;
|
||||
ccwSeek->count = sizeof(*seekData);
|
||||
seekData->reserved = 0x00;
|
||||
seekData->cyl = 0x00;
|
||||
seekData->head = 0x00;
|
||||
|
||||
ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ;
|
||||
ccwSearchID->cda = ptr2u32(searchData);
|
||||
ccwSearchID->chain = 1;
|
||||
ccwSearchID->count = sizeof(*searchData);
|
||||
searchData->cyl = 0;
|
||||
searchData->head = 0;
|
||||
searchData->record = 2;
|
||||
|
||||
/* Go back to Search CCW if correct record not yet found */
|
||||
ccwSearchTic->cmd_code = CCW_CMD_TIC;
|
||||
ccwSearchTic->cda = ptr2u32(ccwSearchID);
|
||||
}
|
||||
|
||||
static void run_ipl1(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
uint32_t startAddr = 0x08;
|
||||
|
||||
if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
|
||||
panic("dasd-ipl: Failed to run IPL1 channel program\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
|
||||
{
|
||||
if (run_dynamic_ccw_program(schid, cutype, addr)) {
|
||||
panic("dasd-ipl: Failed to run IPL2 channel program\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Limitations in vfio-ccw support complicate the IPL process. Details can
|
||||
* be found in docs/devel/s390-dasd-ipl.txt
|
||||
*/
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
PSWLegacy *pswl = (PSWLegacy *) 0x00;
|
||||
uint32_t ipl2_addr;
|
||||
|
||||
/* Construct Read IPL CCW and run it to read IPL1 from boot disk */
|
||||
make_readipl();
|
||||
run_readipl(schid, cutype);
|
||||
ipl2_addr = read_ipl2_addr();
|
||||
check_ipl1();
|
||||
|
||||
/*
|
||||
* Fixup IPL1 channel program to account for vfio-ccw limitations, then run
|
||||
* it to read IPL2 channel program from boot disk.
|
||||
*/
|
||||
ipl1_fixup();
|
||||
run_ipl1(schid, cutype);
|
||||
check_ipl2(ipl2_addr);
|
||||
|
||||
/*
|
||||
* Run IPL2 channel program to read operating system code from boot disk
|
||||
*/
|
||||
run_ipl2(schid, cutype, ipl2_addr);
|
||||
|
||||
/* Transfer control to the guest operating system */
|
||||
pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */
|
||||
pswl->addr |= PSW_MASK_BAMODE; /* ... */
|
||||
jump_to_low_kernel();
|
||||
}
|
16
pc-bios/s390-ccw/dasd-ipl.h
Normal file
16
pc-bios/s390-ccw/dasd-ipl.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* S390 IPL (boot) from a real DASD device via vfio framework.
|
||||
*
|
||||
* Copyright (c) 2019 Jason J. Herne <jjherne@us.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 DASD_IPL_H
|
||||
#define DASD_IPL_H
|
||||
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype);
|
||||
|
||||
#endif /* DASD_IPL_H */
|
|
@ -13,6 +13,7 @@
|
|||
#include "s390-ccw.h"
|
||||
#include "cio.h"
|
||||
#include "virtio.h"
|
||||
#include "dasd-ipl.h"
|
||||
|
||||
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static SubChannelId blk_schid = { .one = 1 };
|
||||
|
@ -209,6 +210,10 @@ int main(void)
|
|||
|
||||
cutype = cu_type(blk_schid);
|
||||
switch (cutype) {
|
||||
case CU_TYPE_DASD_3990:
|
||||
case CU_TYPE_DASD_2107:
|
||||
dasd_ipl(blk_schid, cutype); /* no return */
|
||||
break;
|
||||
case CU_TYPE_VIRTIO:
|
||||
virtio_setup();
|
||||
zipl_load(); /* no return */
|
||||
|
|
|
@ -87,4 +87,17 @@ typedef struct LowCore {
|
|||
|
||||
extern LowCore const *lowcore;
|
||||
|
||||
static inline void set_prefix(uint32_t address)
|
||||
{
|
||||
asm volatile("spx %0" : : "m" (address) : "memory");
|
||||
}
|
||||
|
||||
static inline uint32_t store_prefix(void)
|
||||
{
|
||||
uint32_t address;
|
||||
|
||||
asm volatile("stpx %0" : "=m" (address));
|
||||
return address;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue