- add query-cpus-fast and deprecate query-cpus, while adding s390 cpu
information - remove s390x memory hotplug implementation, which is not useable in this form - add boot menu support in the s390-ccw bios - expose s390x guest crash information - fixes and cleaups -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEw9DWbcNiT/aowBjO3s9rk8bwL68FAlqX+BcSHGNvaHVja0By ZWRoYXQuY29tAAoJEN7Pa5PG8C+vvMMP/0QihBv9AjhvuvD49Fmyr8+g7ARFO15V t2xU/hiiW3WSbKl2afeZ8TB8PhI8vv+IthJs6eUHLJ6+0iQt2MKWGU4voIGvx2Ip 0727GSrOKFN5Db0pYrgvhGuTVpwE5BUKNfBg7nC0JOAuf9piXO8F+tRcCQmfIkT2 oeMrJzzfIRbQGSORfS2igQPdn+sQMf4Lxv3ZxcDFIGuzG/UkoG+DrXeiVJYQMGdv NB/qmj6vUMo0QPZskLAMolfpw9HSllku2BTDOSPsvW5rUVJHDqcqnnWSLUkpuXOJ k1oyf2mDSM3DfSUDxTsB7QLUXmfdGl9noCGrqeILmciuG6fNCAZv7LWG//mUiIXU IkC4VjHoEFN0z3OvPUrL/ZRB4lBJICCSP3MVEuttn774zon+aKEJqpD6qThRkbjv ntMemq8Xw+qEYmB+kGKfsR/cc4RUVRkikX/eBUHun66kP5gjgmfRKWgYtne7G35b LA1BwX2R1FNAmzD1cPdLV8OeRXUfwqS9N+88/LZsY9pGUVSrqXz5YfNBGiQHJyJv pPoDQ0ZZJSjHUKfUIJB9F3za7vmY4iZp4X2xFp780mZbIH1xEE1OnZA4NrZepn5G tagBwQZWMztz9vjqkHV6LuPM4ycK0tAev58Zqi9caNErmOtmVkGiCGbpYhCz7Ckx IyaUFJZ3dxvO =chDT -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20180301-v2' into staging - add query-cpus-fast and deprecate query-cpus, while adding s390 cpu information - remove s390x memory hotplug implementation, which is not useable in this form - add boot menu support in the s390-ccw bios - expose s390x guest crash information - fixes and cleaups # gpg: Signature made Thu 01 Mar 2018 12:54:47 GMT # gpg: using RSA key DECF6B93C6F02FAF # gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>" # gpg: aka "Cornelia Huck <huckc@linux.vnet.ibm.com>" # gpg: aka "Cornelia Huck <cornelia.huck@de.ibm.com>" # gpg: aka "Cornelia Huck <cohuck@kernel.org>" # gpg: aka "Cornelia Huck <cohuck@redhat.com>" # Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0 18CE DECF 6B93 C6F0 2FAF * remotes/cohuck/tags/s390x-20180301-v2: (27 commits) s390x/tcg: fix loading 31bit PSWs with the highest bit set s390x: remove s390_get_memslot_count s390x/sclp: remove memory hotplug support s390x/cpumodel: document S390FeatDef.bit not applicable hmp: change hmp_info_cpus to use query-cpus-fast qemu-doc: deprecate query-cpus qmp: add architecture specific cpu data for query-cpus-fast qmp: add query-cpus-fast qmp: expose s390-specific CPU info s390x/tcg: add various alignment checks s390x/tcg: fix disabling/enabling DAT s390/stattrib: Make SaveVMHandlers data static s390x/cpu: expose the guest crash information pc-bios/s390: Rebuild the s390x firmware images with the boot menu changes s390-ccw: interactive boot menu for scsi s390-ccw: use zipl values when no boot menu options are present s390-ccw: set cp_receive mask only when needed and consume pending service irqs s390-ccw: read user input for boot index via the SCLP console s390-ccw: print zipl boot menu s390-ccw: read stage2 boot loader data to find menu ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0dc8ae5e8e
54
cpus.c
54
cpus.c
@ -2100,6 +2100,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
|||||||
#elif defined(TARGET_TRICORE)
|
#elif defined(TARGET_TRICORE)
|
||||||
TriCoreCPU *tricore_cpu = TRICORE_CPU(cpu);
|
TriCoreCPU *tricore_cpu = TRICORE_CPU(cpu);
|
||||||
CPUTriCoreState *env = &tricore_cpu->env;
|
CPUTriCoreState *env = &tricore_cpu->env;
|
||||||
|
#elif defined(TARGET_S390X)
|
||||||
|
S390CPU *s390_cpu = S390_CPU(cpu);
|
||||||
|
CPUS390XState *env = &s390_cpu->env;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cpu_synchronize_state(cpu);
|
cpu_synchronize_state(cpu);
|
||||||
@ -2127,6 +2130,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
|||||||
#elif defined(TARGET_TRICORE)
|
#elif defined(TARGET_TRICORE)
|
||||||
info->value->arch = CPU_INFO_ARCH_TRICORE;
|
info->value->arch = CPU_INFO_ARCH_TRICORE;
|
||||||
info->value->u.tricore.PC = env->PC;
|
info->value->u.tricore.PC = env->PC;
|
||||||
|
#elif defined(TARGET_S390X)
|
||||||
|
info->value->arch = CPU_INFO_ARCH_S390;
|
||||||
|
info->value->u.s390.cpu_state = env->cpu_state;
|
||||||
#else
|
#else
|
||||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||||
#endif
|
#endif
|
||||||
@ -2150,6 +2156,54 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fast means: we NEVER interrupt vCPU threads to retrieve
|
||||||
|
* information from KVM.
|
||||||
|
*/
|
||||||
|
CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *ms = MACHINE(qdev_get_machine());
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||||
|
CpuInfoFastList *head = NULL, *cur_item = NULL;
|
||||||
|
CPUState *cpu;
|
||||||
|
#if defined(TARGET_S390X)
|
||||||
|
S390CPU *s390_cpu;
|
||||||
|
CPUS390XState *env;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
CpuInfoFastList *info = g_malloc0(sizeof(*info));
|
||||||
|
info->value = g_malloc0(sizeof(*info->value));
|
||||||
|
|
||||||
|
info->value->cpu_index = cpu->cpu_index;
|
||||||
|
info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
|
||||||
|
info->value->thread_id = cpu->thread_id;
|
||||||
|
|
||||||
|
info->value->has_props = !!mc->cpu_index_to_instance_props;
|
||||||
|
if (info->value->has_props) {
|
||||||
|
CpuInstanceProperties *props;
|
||||||
|
props = g_malloc0(sizeof(*props));
|
||||||
|
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
|
||||||
|
info->value->props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(TARGET_S390X)
|
||||||
|
s390_cpu = S390_CPU(cpu);
|
||||||
|
env = &s390_cpu->env;
|
||||||
|
info->value->arch = CPU_INFO_ARCH_S390;
|
||||||
|
info->value->u.s390.cpu_state = env->cpu_state;
|
||||||
|
#endif
|
||||||
|
if (!cur_item) {
|
||||||
|
head = cur_item = info;
|
||||||
|
} else {
|
||||||
|
cur_item->next = info;
|
||||||
|
cur_item = info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
void qmp_memsave(int64_t addr, int64_t size, const char *filename,
|
void qmp_memsave(int64_t addr, int64_t size, const char *filename,
|
||||||
bool has_cpu, int64_t cpu_index, Error **errp)
|
bool has_cpu, int64_t cpu_index, Error **errp)
|
||||||
{
|
{
|
||||||
|
41
hmp.c
41
hmp.c
@ -360,50 +360,23 @@ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict)
|
|||||||
|
|
||||||
void hmp_info_cpus(Monitor *mon, const QDict *qdict)
|
void hmp_info_cpus(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
CpuInfoList *cpu_list, *cpu;
|
CpuInfoFastList *cpu_list, *cpu;
|
||||||
|
|
||||||
cpu_list = qmp_query_cpus(NULL);
|
cpu_list = qmp_query_cpus_fast(NULL);
|
||||||
|
|
||||||
for (cpu = cpu_list; cpu; cpu = cpu->next) {
|
for (cpu = cpu_list; cpu; cpu = cpu->next) {
|
||||||
int active = ' ';
|
int active = ' ';
|
||||||
|
|
||||||
if (cpu->value->CPU == monitor_get_cpu_index()) {
|
if (cpu->value->cpu_index == monitor_get_cpu_index()) {
|
||||||
active = '*';
|
active = '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
|
monitor_printf(mon, "%c CPU #%" PRId64 ":", active,
|
||||||
|
cpu->value->cpu_index);
|
||||||
switch (cpu->value->arch) {
|
monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id);
|
||||||
case CPU_INFO_ARCH_X86:
|
|
||||||
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc);
|
|
||||||
break;
|
|
||||||
case CPU_INFO_ARCH_PPC:
|
|
||||||
monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip);
|
|
||||||
break;
|
|
||||||
case CPU_INFO_ARCH_SPARC:
|
|
||||||
monitor_printf(mon, " pc=0x%016" PRIx64,
|
|
||||||
cpu->value->u.q_sparc.pc);
|
|
||||||
monitor_printf(mon, " npc=0x%016" PRIx64,
|
|
||||||
cpu->value->u.q_sparc.npc);
|
|
||||||
break;
|
|
||||||
case CPU_INFO_ARCH_MIPS:
|
|
||||||
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC);
|
|
||||||
break;
|
|
||||||
case CPU_INFO_ARCH_TRICORE:
|
|
||||||
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpu->value->halted) {
|
|
||||||
monitor_printf(mon, " (halted)");
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_CpuInfoList(cpu_list);
|
qapi_free_CpuInfoFastList(cpu_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_block_info(Monitor *mon, BlockInfo *info,
|
static void print_block_info(Monitor *mon, BlockInfo *info,
|
||||||
|
@ -192,8 +192,8 @@ static void qemu_s390_flic_notify(uint32_t type)
|
|||||||
cs->interrupt_request |= CPU_INTERRUPT_HARD;
|
cs->interrupt_request |= CPU_INTERRUPT_HARD;
|
||||||
|
|
||||||
/* ignore CPUs that are not sleeping */
|
/* ignore CPUs that are not sleeping */
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING &&
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING &&
|
||||||
s390_cpu_get_state(cpu) != CPU_STATE_LOAD) {
|
s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
#include "hw/s390x/ebcdic.h"
|
#include "hw/s390x/ebcdic.h"
|
||||||
#include "ipl.h"
|
#include "ipl.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/config-file.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/option.h"
|
||||||
|
|
||||||
#define KERN_IMAGE_START 0x010000UL
|
#define KERN_IMAGE_START 0x010000UL
|
||||||
#define KERN_PARM_AREA 0x010480UL
|
#define KERN_PARM_AREA 0x010480UL
|
||||||
@ -219,6 +222,61 @@ static Property s390_ipl_properties[] = {
|
|||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void s390_ipl_set_boot_menu(S390IPLState *ipl)
|
||||||
|
{
|
||||||
|
QemuOptsList *plist = qemu_find_opts("boot-opts");
|
||||||
|
QemuOpts *opts = QTAILQ_FIRST(&plist->head);
|
||||||
|
uint8_t *flags = &ipl->qipl.qipl_flags;
|
||||||
|
uint32_t *timeout = &ipl->qipl.boot_menu_timeout;
|
||||||
|
const char *tmp;
|
||||||
|
unsigned long splash_time = 0;
|
||||||
|
|
||||||
|
if (!get_boot_device(0)) {
|
||||||
|
if (boot_menu) {
|
||||||
|
error_report("boot menu requires a bootindex to be specified for "
|
||||||
|
"the IPL device.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ipl->iplb.pbt) {
|
||||||
|
case S390_IPL_TYPE_CCW:
|
||||||
|
/* In the absence of -boot menu, use zipl parameters */
|
||||||
|
if (!qemu_opt_get(opts, "menu")) {
|
||||||
|
*flags |= QIPL_FLAG_BM_OPTS_ZIPL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case S390_IPL_TYPE_QEMU_SCSI:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_report("boot menu is not supported for this device type.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boot_menu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*flags |= QIPL_FLAG_BM_OPTS_CMD;
|
||||||
|
|
||||||
|
tmp = qemu_opt_get(opts, "splash-time");
|
||||||
|
|
||||||
|
if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) {
|
||||||
|
error_report("splash-time is invalid, forcing it to 0.");
|
||||||
|
*timeout = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splash_time > 0xffffffff) {
|
||||||
|
error_report("splash-time is too large, forcing it to max value.");
|
||||||
|
*timeout = 0xffffffff;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*timeout = cpu_to_be32(splash_time);
|
||||||
|
}
|
||||||
|
|
||||||
static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||||||
{
|
{
|
||||||
DeviceState *dev_st;
|
DeviceState *dev_st;
|
||||||
@ -399,6 +457,21 @@ void s390_reipl_request(void)
|
|||||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void s390_ipl_prepare_qipl(S390CPU *cpu)
|
||||||
|
{
|
||||||
|
S390IPLState *ipl = get_ipl_device();
|
||||||
|
uint8_t *addr;
|
||||||
|
uint64_t len = 4096;
|
||||||
|
|
||||||
|
addr = cpu_physical_memory_map(cpu->env.psa, &len, 1);
|
||||||
|
if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) {
|
||||||
|
error_report("Cannot set QEMU IPL parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(addr + QIPL_ADDRESS, &ipl->qipl, sizeof(QemuIplParameters));
|
||||||
|
cpu_physical_memory_unmap(addr, len, 1, len);
|
||||||
|
}
|
||||||
|
|
||||||
void s390_ipl_prepare_cpu(S390CPU *cpu)
|
void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||||
{
|
{
|
||||||
S390IPLState *ipl = get_ipl_device();
|
S390IPLState *ipl = get_ipl_device();
|
||||||
@ -418,8 +491,10 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
|||||||
error_report_err(err);
|
error_report_err(err);
|
||||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||||
}
|
}
|
||||||
|
s390_ipl_set_boot_menu(ipl);
|
||||||
|
s390_ipl_prepare_qipl(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s390_ipl_reset(DeviceState *dev)
|
static void s390_ipl_reset(DeviceState *dev)
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
struct IplBlockCcw {
|
struct IplBlockCcw {
|
||||||
uint64_t netboot_start_addr;
|
uint8_t reserved0[85];
|
||||||
uint8_t reserved0[77];
|
|
||||||
uint8_t ssid;
|
uint8_t ssid;
|
||||||
uint16_t devno;
|
uint16_t devno;
|
||||||
uint8_t vm_flags;
|
uint8_t vm_flags;
|
||||||
@ -90,6 +89,33 @@ void s390_ipl_prepare_cpu(S390CPU *cpu);
|
|||||||
IplParameterBlock *s390_ipl_get_iplb(void);
|
IplParameterBlock *s390_ipl_get_iplb(void);
|
||||||
void s390_reipl_request(void);
|
void s390_reipl_request(void);
|
||||||
|
|
||||||
|
#define QIPL_ADDRESS 0xcc
|
||||||
|
|
||||||
|
/* Boot Menu flags */
|
||||||
|
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||||
|
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The QEMU IPL Parameters will be stored at absolute address
|
||||||
|
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||||
|
* double-word aligned.
|
||||||
|
* Placement of data fields in this area must account for
|
||||||
|
* their alignment needs. E.g., netboot_start_address must
|
||||||
|
* have an offset of 4 + n * 8 bytes within the struct in order
|
||||||
|
* to keep it double-word aligned.
|
||||||
|
* The total size of the struct must never exceed 28 bytes.
|
||||||
|
* This definition must be kept in sync with the defininition
|
||||||
|
* in pc-bios/s390-ccw/iplb.h.
|
||||||
|
*/
|
||||||
|
struct QemuIplParameters {
|
||||||
|
uint8_t qipl_flags;
|
||||||
|
uint8_t reserved1[3];
|
||||||
|
uint64_t netboot_start_addr;
|
||||||
|
uint32_t boot_menu_timeout;
|
||||||
|
uint8_t reserved2[12];
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct QemuIplParameters QemuIplParameters;
|
||||||
|
|
||||||
#define TYPE_S390_IPL "s390-ipl"
|
#define TYPE_S390_IPL "s390-ipl"
|
||||||
#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
|
#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
|
||||||
|
|
||||||
@ -105,6 +131,7 @@ struct S390IPLState {
|
|||||||
bool iplb_valid;
|
bool iplb_valid;
|
||||||
bool reipl_requested;
|
bool reipl_requested;
|
||||||
bool netboot;
|
bool netboot;
|
||||||
|
QemuIplParameters qipl;
|
||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
char *kernel;
|
char *kernel;
|
||||||
|
@ -365,22 +365,22 @@ static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value,
|
|||||||
s->migration_enabled = value;
|
s->migration_enabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SaveVMHandlers savevm_s390_stattrib_handlers = {
|
||||||
|
.save_setup = cmma_save_setup,
|
||||||
|
.save_live_iterate = cmma_save_iterate,
|
||||||
|
.save_live_complete_precopy = cmma_save_complete,
|
||||||
|
.save_live_pending = cmma_save_pending,
|
||||||
|
.save_cleanup = cmma_save_cleanup,
|
||||||
|
.load_state = cmma_load,
|
||||||
|
.is_active = cmma_active,
|
||||||
|
};
|
||||||
|
|
||||||
static void s390_stattrib_instance_init(Object *obj)
|
static void s390_stattrib_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
S390StAttribState *sas = S390_STATTRIB(obj);
|
S390StAttribState *sas = S390_STATTRIB(obj);
|
||||||
SaveVMHandlers *ops;
|
|
||||||
|
|
||||||
/* ops will always be freed by qemu when unregistering */
|
register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0,
|
||||||
ops = g_new0(SaveVMHandlers, 1);
|
&savevm_s390_stattrib_handlers, sas);
|
||||||
|
|
||||||
ops->save_setup = cmma_save_setup;
|
|
||||||
ops->save_live_iterate = cmma_save_iterate;
|
|
||||||
ops->save_live_complete_precopy = cmma_save_complete;
|
|
||||||
ops->save_live_pending = cmma_save_pending;
|
|
||||||
ops->save_cleanup = cmma_save_cleanup;
|
|
||||||
ops->load_state = cmma_load;
|
|
||||||
ops->is_active = cmma_active;
|
|
||||||
register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, ops, sas);
|
|
||||||
|
|
||||||
object_property_add_bool(obj, "migration-enabled",
|
object_property_add_bool(obj, "migration-enabled",
|
||||||
s390_stattrib_get_migration_enabled,
|
s390_stattrib_get_migration_enabled,
|
||||||
|
@ -368,7 +368,7 @@ static void s390_machine_reset(void)
|
|||||||
|
|
||||||
/* all cpus are stopped - configure and start the ipl cpu only */
|
/* all cpus are stopped - configure and start the ipl cpu only */
|
||||||
s390_ipl_prepare_cpu(ipl_cpu);
|
s390_ipl_prepare_cpu(ipl_cpu);
|
||||||
s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu);
|
s390_cpu_set_state(S390_CPU_STATE_OPERATING, ipl_cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
|
static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
|
||||||
|
310
hw/s390x/sclp.c
310
hw/s390x/sclp.c
@ -15,9 +15,7 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "exec/memory.h"
|
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "exec/address-spaces.h"
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/s390x/sclp.h"
|
#include "hw/s390x/sclp.h"
|
||||||
#include "hw/s390x/event-facility.h"
|
#include "hw/s390x/event-facility.h"
|
||||||
@ -57,10 +55,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
|||||||
{
|
{
|
||||||
ReadInfo *read_info = (ReadInfo *) sccb;
|
ReadInfo *read_info = (ReadInfo *) sccb;
|
||||||
MachineState *machine = MACHINE(qdev_get_machine());
|
MachineState *machine = MACHINE(qdev_get_machine());
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
int cpu_count;
|
int cpu_count;
|
||||||
int rnsize, rnmax;
|
int rnsize, rnmax;
|
||||||
int slots = MIN(machine->ram_slots, s390_get_memslot_count());
|
|
||||||
IplParameterBlock *ipib = s390_ipl_get_iplb();
|
IplParameterBlock *ipib = s390_ipl_get_iplb();
|
||||||
|
|
||||||
/* CPU information */
|
/* CPU information */
|
||||||
@ -80,36 +76,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
|||||||
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
|
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
|
||||||
SCLP_HAS_IOA_RECONFIG);
|
SCLP_HAS_IOA_RECONFIG);
|
||||||
|
|
||||||
/* Memory Hotplug is only supported for the ccw machine type */
|
|
||||||
if (mhd) {
|
|
||||||
mhd->standby_subregion_size = MEM_SECTION_SIZE;
|
|
||||||
/* Deduct the memory slot already used for core */
|
|
||||||
if (slots > 0) {
|
|
||||||
while ((mhd->standby_subregion_size * (slots - 1)
|
|
||||||
< mhd->standby_mem_size)) {
|
|
||||||
mhd->standby_subregion_size = mhd->standby_subregion_size << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Initialize mapping of guest standby memory sections indicating which
|
|
||||||
* are and are not online. Assume all standby memory begins offline.
|
|
||||||
*/
|
|
||||||
if (mhd->standby_state_map == 0) {
|
|
||||||
if (mhd->standby_mem_size % mhd->standby_subregion_size) {
|
|
||||||
mhd->standby_state_map = g_malloc0((mhd->standby_mem_size /
|
|
||||||
mhd->standby_subregion_size + 1) *
|
|
||||||
(mhd->standby_subregion_size /
|
|
||||||
MEM_SECTION_SIZE));
|
|
||||||
} else {
|
|
||||||
mhd->standby_state_map = g_malloc0(mhd->standby_mem_size /
|
|
||||||
MEM_SECTION_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mhd->padded_ram_size = ram_size + mhd->pad_size;
|
|
||||||
mhd->rzm = 1 << mhd->increment_size;
|
|
||||||
|
|
||||||
read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR);
|
|
||||||
}
|
|
||||||
read_info->mha_pow = s390_get_mha_pow();
|
read_info->mha_pow = s390_get_mha_pow();
|
||||||
read_info->hmfai = cpu_to_be32(s390_get_hmfai());
|
read_info->hmfai = cpu_to_be32(s390_get_hmfai());
|
||||||
|
|
||||||
@ -121,7 +87,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
|||||||
read_info->rnsize2 = cpu_to_be32(rnsize);
|
read_info->rnsize2 = cpu_to_be32(rnsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
rnmax = machine->maxram_size >> sclp->increment_size;
|
/* we don't support standby memory, maxram_size is never exposed */
|
||||||
|
rnmax = machine->ram_size >> sclp->increment_size;
|
||||||
if (rnmax < 0x10000) {
|
if (rnmax < 0x10000) {
|
||||||
read_info->rnmax = cpu_to_be16(rnmax);
|
read_info->rnmax = cpu_to_be16(rnmax);
|
||||||
} else {
|
} else {
|
||||||
@ -139,195 +106,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
|||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb)
|
|
||||||
{
|
|
||||||
int i, assigned;
|
|
||||||
int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID;
|
|
||||||
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
|
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
|
|
||||||
if (!mhd) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ram_size >> mhd->increment_size) >= 0x10000) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return information regarding core memory */
|
|
||||||
storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0);
|
|
||||||
assigned = ram_size >> mhd->increment_size;
|
|
||||||
storage_info->assigned = cpu_to_be16(assigned);
|
|
||||||
|
|
||||||
for (i = 0; i < assigned; i++) {
|
|
||||||
storage_info->entries[i] = cpu_to_be32(subincrement_id);
|
|
||||||
subincrement_id += SCLP_INCREMENT_UNIT;
|
|
||||||
}
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb)
|
|
||||||
{
|
|
||||||
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
|
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
|
|
||||||
if (!mhd) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return information regarding standby memory */
|
|
||||||
storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0);
|
|
||||||
storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >>
|
|
||||||
mhd->increment_size);
|
|
||||||
storage_info->standby = cpu_to_be16(mhd->standby_mem_size >>
|
|
||||||
mhd->increment_size);
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb,
|
|
||||||
uint16_t element)
|
|
||||||
{
|
|
||||||
int i, assigned, subincrement_id;
|
|
||||||
AttachStorageElement *attach_info = (AttachStorageElement *) sccb;
|
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
|
|
||||||
if (!mhd) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element != 1) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assigned = mhd->standby_mem_size >> mhd->increment_size;
|
|
||||||
attach_info->assigned = cpu_to_be16(assigned);
|
|
||||||
subincrement_id = ((ram_size >> mhd->increment_size) << 16)
|
|
||||||
+ SCLP_STARTING_SUBINCREMENT_ID;
|
|
||||||
for (i = 0; i < assigned; i++) {
|
|
||||||
attach_info->entries[i] = cpu_to_be32(subincrement_id);
|
|
||||||
subincrement_id += SCLP_INCREMENT_UNIT;
|
|
||||||
}
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void assign_storage(SCLPDevice *sclp, SCCB *sccb)
|
|
||||||
{
|
|
||||||
MemoryRegion *mr = NULL;
|
|
||||||
uint64_t this_subregion_size;
|
|
||||||
AssignStorage *assign_info = (AssignStorage *) sccb;
|
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
ram_addr_t assign_addr;
|
|
||||||
MemoryRegion *sysmem = get_system_memory();
|
|
||||||
|
|
||||||
if (!mhd) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
assign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm;
|
|
||||||
|
|
||||||
if ((assign_addr % MEM_SECTION_SIZE == 0) &&
|
|
||||||
(assign_addr >= mhd->padded_ram_size)) {
|
|
||||||
/* Re-use existing memory region if found */
|
|
||||||
mr = memory_region_find(sysmem, assign_addr, 1).mr;
|
|
||||||
memory_region_unref(mr);
|
|
||||||
if (!mr) {
|
|
||||||
|
|
||||||
MemoryRegion *standby_ram = g_new(MemoryRegion, 1);
|
|
||||||
|
|
||||||
/* offset to align to standby_subregion_size for allocation */
|
|
||||||
ram_addr_t offset = assign_addr -
|
|
||||||
(assign_addr - mhd->padded_ram_size)
|
|
||||||
% mhd->standby_subregion_size;
|
|
||||||
|
|
||||||
/* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) + NULL */
|
|
||||||
char id[16];
|
|
||||||
snprintf(id, 16, "standby.ram%d",
|
|
||||||
(int)((offset - mhd->padded_ram_size) /
|
|
||||||
mhd->standby_subregion_size) + 1);
|
|
||||||
|
|
||||||
/* Allocate a subregion of the calculated standby_subregion_size */
|
|
||||||
if (offset + mhd->standby_subregion_size >
|
|
||||||
mhd->padded_ram_size + mhd->standby_mem_size) {
|
|
||||||
this_subregion_size = mhd->padded_ram_size +
|
|
||||||
mhd->standby_mem_size - offset;
|
|
||||||
} else {
|
|
||||||
this_subregion_size = mhd->standby_subregion_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_region_init_ram(standby_ram, NULL, id, this_subregion_size,
|
|
||||||
&error_fatal);
|
|
||||||
/* This is a hack to make memory hotunplug work again. Once we have
|
|
||||||
* subdevices, we have to unparent them when unassigning memory,
|
|
||||||
* instead of doing it via the ref count of the MemoryRegion. */
|
|
||||||
object_ref(OBJECT(standby_ram));
|
|
||||||
object_unparent(OBJECT(standby_ram));
|
|
||||||
memory_region_add_subregion(sysmem, offset, standby_ram);
|
|
||||||
}
|
|
||||||
/* The specified subregion is no longer in standby */
|
|
||||||
mhd->standby_state_map[(assign_addr - mhd->padded_ram_size)
|
|
||||||
/ MEM_SECTION_SIZE] = 1;
|
|
||||||
}
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unassign_storage(SCLPDevice *sclp, SCCB *sccb)
|
|
||||||
{
|
|
||||||
MemoryRegion *mr = NULL;
|
|
||||||
AssignStorage *assign_info = (AssignStorage *) sccb;
|
|
||||||
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
|
|
||||||
ram_addr_t unassign_addr;
|
|
||||||
MemoryRegion *sysmem = get_system_memory();
|
|
||||||
|
|
||||||
if (!mhd) {
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unassign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm;
|
|
||||||
|
|
||||||
/* if the addr is a multiple of 256 MB */
|
|
||||||
if ((unassign_addr % MEM_SECTION_SIZE == 0) &&
|
|
||||||
(unassign_addr >= mhd->padded_ram_size)) {
|
|
||||||
mhd->standby_state_map[(unassign_addr -
|
|
||||||
mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0;
|
|
||||||
|
|
||||||
/* find the specified memory region and destroy it */
|
|
||||||
mr = memory_region_find(sysmem, unassign_addr, 1).mr;
|
|
||||||
memory_region_unref(mr);
|
|
||||||
if (mr) {
|
|
||||||
int i;
|
|
||||||
int is_removable = 1;
|
|
||||||
ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size -
|
|
||||||
(unassign_addr - mhd->padded_ram_size)
|
|
||||||
% mhd->standby_subregion_size);
|
|
||||||
/* Mark all affected subregions as 'standby' once again */
|
|
||||||
for (i = 0;
|
|
||||||
i < (mhd->standby_subregion_size / MEM_SECTION_SIZE);
|
|
||||||
i++) {
|
|
||||||
|
|
||||||
if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) {
|
|
||||||
is_removable = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_removable) {
|
|
||||||
memory_region_del_subregion(sysmem, mr);
|
|
||||||
object_unref(OBJECT(mr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Provide information about the CPU */
|
/* Provide information about the CPU */
|
||||||
static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
|
static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
|
||||||
{
|
{
|
||||||
@ -390,22 +168,6 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
|
|||||||
case SCLP_CMDW_READ_CPU_INFO:
|
case SCLP_CMDW_READ_CPU_INFO:
|
||||||
sclp_c->read_cpu_info(sclp, sccb);
|
sclp_c->read_cpu_info(sclp, sccb);
|
||||||
break;
|
break;
|
||||||
case SCLP_READ_STORAGE_ELEMENT_INFO:
|
|
||||||
if (code & 0xff00) {
|
|
||||||
sclp_c->read_storage_element1_info(sclp, sccb);
|
|
||||||
} else {
|
|
||||||
sclp_c->read_storage_element0_info(sclp, sccb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SCLP_ATTACH_STORAGE_ELEMENT:
|
|
||||||
sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8);
|
|
||||||
break;
|
|
||||||
case SCLP_ASSIGN_STORAGE:
|
|
||||||
sclp_c->assign_storage(sclp, sccb);
|
|
||||||
break;
|
|
||||||
case SCLP_UNASSIGN_STORAGE:
|
|
||||||
sclp_c->unassign_storage(sclp, sccb);
|
|
||||||
break;
|
|
||||||
case SCLP_CMDW_CONFIGURE_IOA:
|
case SCLP_CMDW_CONFIGURE_IOA:
|
||||||
sclp_configure_io_adapter(sclp, sccb, true);
|
sclp_configure_io_adapter(sclp, sccb, true);
|
||||||
break;
|
break;
|
||||||
@ -540,9 +302,6 @@ static void sclp_memory_init(SCLPDevice *sclp)
|
|||||||
{
|
{
|
||||||
MachineState *machine = MACHINE(qdev_get_machine());
|
MachineState *machine = MACHINE(qdev_get_machine());
|
||||||
ram_addr_t initial_mem = machine->ram_size;
|
ram_addr_t initial_mem = machine->ram_size;
|
||||||
ram_addr_t max_mem = machine->maxram_size;
|
|
||||||
ram_addr_t standby_mem = max_mem - initial_mem;
|
|
||||||
ram_addr_t pad_mem = 0;
|
|
||||||
int increment_size = 20;
|
int increment_size = 20;
|
||||||
|
|
||||||
/* The storage increment size is a multiple of 1M and is a power of 2.
|
/* The storage increment size is a multiple of 1M and is a power of 2.
|
||||||
@ -552,34 +311,14 @@ static void sclp_memory_init(SCLPDevice *sclp)
|
|||||||
while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
|
while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
|
||||||
increment_size++;
|
increment_size++;
|
||||||
}
|
}
|
||||||
if (machine->ram_slots) {
|
|
||||||
while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
|
|
||||||
increment_size++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sclp->increment_size = increment_size;
|
sclp->increment_size = increment_size;
|
||||||
|
|
||||||
/* The core and standby memory areas need to be aligned with
|
/* The core memory area needs to be aligned with the increment size.
|
||||||
* the increment size. In effect, this can cause the
|
* In effect, this can cause the user-specified memory size to be rounded
|
||||||
* user-specified memory size to be rounded down to align
|
* down to align with the nearest increment boundary. */
|
||||||
* with the nearest increment boundary. */
|
|
||||||
initial_mem = initial_mem >> increment_size << increment_size;
|
initial_mem = initial_mem >> increment_size << increment_size;
|
||||||
standby_mem = standby_mem >> increment_size << increment_size;
|
|
||||||
|
|
||||||
/* If the size of ram is not on a MEM_SECTION_SIZE boundary,
|
|
||||||
calculate the pad size necessary to force this boundary. */
|
|
||||||
if (machine->ram_slots && standby_mem) {
|
|
||||||
sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
|
|
||||||
|
|
||||||
if (initial_mem % MEM_SECTION_SIZE) {
|
|
||||||
pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE;
|
|
||||||
}
|
|
||||||
mhd->increment_size = increment_size;
|
|
||||||
mhd->pad_size = pad_mem;
|
|
||||||
mhd->standby_mem_size = standby_mem;
|
|
||||||
}
|
|
||||||
machine->ram_size = initial_mem;
|
machine->ram_size = initial_mem;
|
||||||
machine->maxram_size = initial_mem + pad_mem + standby_mem;
|
|
||||||
/* let's propagate the changed ram size into the global variable. */
|
/* let's propagate the changed ram size into the global variable. */
|
||||||
ram_size = initial_mem;
|
ram_size = initial_mem;
|
||||||
}
|
}
|
||||||
@ -613,11 +352,6 @@ static void sclp_class_init(ObjectClass *oc, void *data)
|
|||||||
dc->user_creatable = false;
|
dc->user_creatable = false;
|
||||||
|
|
||||||
sc->read_SCP_info = read_SCP_info;
|
sc->read_SCP_info = read_SCP_info;
|
||||||
sc->read_storage_element0_info = read_storage_element0_info;
|
|
||||||
sc->read_storage_element1_info = read_storage_element1_info;
|
|
||||||
sc->attach_storage_element = attach_storage_element;
|
|
||||||
sc->assign_storage = assign_storage;
|
|
||||||
sc->unassign_storage = unassign_storage;
|
|
||||||
sc->read_cpu_info = sclp_read_cpu_info;
|
sc->read_cpu_info = sclp_read_cpu_info;
|
||||||
sc->execute = sclp_execute;
|
sc->execute = sclp_execute;
|
||||||
sc->service_interrupt = service_interrupt;
|
sc->service_interrupt = service_interrupt;
|
||||||
@ -632,42 +366,8 @@ static TypeInfo sclp_info = {
|
|||||||
.class_size = sizeof(SCLPDeviceClass),
|
.class_size = sizeof(SCLPDeviceClass),
|
||||||
};
|
};
|
||||||
|
|
||||||
sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void)
|
|
||||||
{
|
|
||||||
DeviceState *dev;
|
|
||||||
dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV);
|
|
||||||
object_property_add_child(qdev_get_machine(),
|
|
||||||
TYPE_SCLP_MEMORY_HOTPLUG_DEV,
|
|
||||||
OBJECT(dev), NULL);
|
|
||||||
qdev_init_nofail(dev);
|
|
||||||
return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path(
|
|
||||||
TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void)
|
|
||||||
{
|
|
||||||
return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path(
|
|
||||||
TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sclp_memory_hotplug_dev_class_init(ObjectClass *klass,
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
static TypeInfo sclp_memory_hotplug_dev_info = {
|
|
||||||
.name = TYPE_SCLP_MEMORY_HOTPLUG_DEV,
|
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
|
||||||
.instance_size = sizeof(sclpMemoryHotplugDev),
|
|
||||||
.class_init = sclp_memory_hotplug_dev_class_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void register_types(void)
|
static void register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&sclp_memory_hotplug_dev_info);
|
|
||||||
type_register_static(&sclp_info);
|
type_register_static(&sclp_info);
|
||||||
}
|
}
|
||||||
type_init(register_types);
|
type_init(register_types);
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
#define SCLP_FC_ASSIGN_ATTACH_READ_STOR 0xE00000000000ULL
|
#define SCLP_FC_ASSIGN_ATTACH_READ_STOR 0xE00000000000ULL
|
||||||
#define SCLP_STARTING_SUBINCREMENT_ID 0x10001
|
#define SCLP_STARTING_SUBINCREMENT_ID 0x10001
|
||||||
#define SCLP_INCREMENT_UNIT 0x10000
|
#define SCLP_INCREMENT_UNIT 0x10000
|
||||||
#define MAX_AVAIL_SLOTS 32
|
|
||||||
#define MAX_STORAGE_INCREMENTS 1020
|
#define MAX_STORAGE_INCREMENTS 1020
|
||||||
|
|
||||||
/* CPU hotplug SCLP codes */
|
/* CPU hotplug SCLP codes */
|
||||||
@ -202,12 +201,6 @@ typedef struct SCLPDeviceClass {
|
|||||||
/* private */
|
/* private */
|
||||||
DeviceClass parent_class;
|
DeviceClass parent_class;
|
||||||
void (*read_SCP_info)(SCLPDevice *sclp, SCCB *sccb);
|
void (*read_SCP_info)(SCLPDevice *sclp, SCCB *sccb);
|
||||||
void (*read_storage_element0_info)(SCLPDevice *sclp, SCCB *sccb);
|
|
||||||
void (*read_storage_element1_info)(SCLPDevice *sclp, SCCB *sccb);
|
|
||||||
void (*attach_storage_element)(SCLPDevice *sclp, SCCB *sccb,
|
|
||||||
uint16_t element);
|
|
||||||
void (*assign_storage)(SCLPDevice *sclp, SCCB *sccb);
|
|
||||||
void (*unassign_storage)(SCLPDevice *sclp, SCCB *sccb);
|
|
||||||
void (*read_cpu_info)(SCLPDevice *sclp, SCCB *sccb);
|
void (*read_cpu_info)(SCLPDevice *sclp, SCCB *sccb);
|
||||||
|
|
||||||
/* public */
|
/* public */
|
||||||
@ -215,23 +208,6 @@ typedef struct SCLPDeviceClass {
|
|||||||
void (*service_interrupt)(SCLPDevice *sclp, uint32_t sccb);
|
void (*service_interrupt)(SCLPDevice *sclp, uint32_t sccb);
|
||||||
} SCLPDeviceClass;
|
} SCLPDeviceClass;
|
||||||
|
|
||||||
typedef struct sclpMemoryHotplugDev sclpMemoryHotplugDev;
|
|
||||||
|
|
||||||
#define TYPE_SCLP_MEMORY_HOTPLUG_DEV "sclp-memory-hotplug-dev"
|
|
||||||
#define SCLP_MEMORY_HOTPLUG_DEV(obj) \
|
|
||||||
OBJECT_CHECK(sclpMemoryHotplugDev, (obj), TYPE_SCLP_MEMORY_HOTPLUG_DEV)
|
|
||||||
|
|
||||||
struct sclpMemoryHotplugDev {
|
|
||||||
SysBusDevice parent;
|
|
||||||
ram_addr_t standby_mem_size;
|
|
||||||
ram_addr_t padded_ram_size;
|
|
||||||
ram_addr_t pad_size;
|
|
||||||
ram_addr_t standby_subregion_size;
|
|
||||||
ram_addr_t rzm;
|
|
||||||
int increment_size;
|
|
||||||
char *standby_state_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int sccb_data_len(SCCB *sccb)
|
static inline int sccb_data_len(SCCB *sccb)
|
||||||
{
|
{
|
||||||
return be16_to_cpu(sccb->h.length) - sizeof(sccb->h);
|
return be16_to_cpu(sccb->h.length) - sizeof(sccb->h);
|
||||||
@ -239,8 +215,6 @@ static inline int sccb_data_len(SCCB *sccb)
|
|||||||
|
|
||||||
|
|
||||||
void s390_sclp_init(void);
|
void s390_sclp_init(void);
|
||||||
sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void);
|
|
||||||
sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void);
|
|
||||||
void sclp_service_interrupt(uint32_t sccb);
|
void sclp_service_interrupt(uint32_t sccb);
|
||||||
void raise_irq_cpu_hotplug(void);
|
void raise_irq_cpu_hotplug(void);
|
||||||
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
|
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
|
||||||
|
13
monitor.c
13
monitor.c
@ -1055,7 +1055,7 @@ int monitor_set_cpu(int cpu_index)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPUState *mon_get_cpu(void)
|
static CPUState *mon_get_cpu_sync(bool synchronize)
|
||||||
{
|
{
|
||||||
CPUState *cpu;
|
CPUState *cpu;
|
||||||
|
|
||||||
@ -1074,10 +1074,17 @@ CPUState *mon_get_cpu(void)
|
|||||||
monitor_set_cpu(first_cpu->cpu_index);
|
monitor_set_cpu(first_cpu->cpu_index);
|
||||||
cpu = first_cpu;
|
cpu = first_cpu;
|
||||||
}
|
}
|
||||||
cpu_synchronize_state(cpu);
|
if (synchronize) {
|
||||||
|
cpu_synchronize_state(cpu);
|
||||||
|
}
|
||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPUState *mon_get_cpu(void)
|
||||||
|
{
|
||||||
|
return mon_get_cpu_sync(true);
|
||||||
|
}
|
||||||
|
|
||||||
CPUArchState *mon_get_cpu_env(void)
|
CPUArchState *mon_get_cpu_env(void)
|
||||||
{
|
{
|
||||||
CPUState *cs = mon_get_cpu();
|
CPUState *cs = mon_get_cpu();
|
||||||
@ -1087,7 +1094,7 @@ CPUArchState *mon_get_cpu_env(void)
|
|||||||
|
|
||||||
int monitor_get_cpu_index(void)
|
int monitor_get_cpu_index(void)
|
||||||
{
|
{
|
||||||
CPUState *cs = mon_get_cpu();
|
CPUState *cs = mon_get_cpu_sync(false);
|
||||||
|
|
||||||
return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX;
|
return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX;
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
|
|||||||
|
|
||||||
.PHONY : all clean build-all
|
.PHONY : all clean build-all
|
||||||
|
|
||||||
OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o
|
OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o
|
||||||
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
||||||
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
||||||
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
|
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
|
||||||
|
@ -83,6 +83,10 @@ static void jump_to_IPL_code(uint64_t address)
|
|||||||
|
|
||||||
static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */
|
static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */
|
||||||
static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
|
static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
|
||||||
|
static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
|
static void *s2_prev_blk = _s2;
|
||||||
|
static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
|
||||||
|
static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
|
||||||
|
|
||||||
static inline void verify_boot_info(BootInfo *bip)
|
static inline void verify_boot_info(BootInfo *bip)
|
||||||
{
|
{
|
||||||
@ -95,32 +99,32 @@ static inline void verify_boot_info(BootInfo *bip)
|
|||||||
"Bad block size in zIPL section of the 1st record.");
|
"Bad block size in zIPL section of the 1st record.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static block_number_t eckd_block_num(BootMapPointer *p)
|
static block_number_t eckd_block_num(EckdCHS *chs)
|
||||||
{
|
{
|
||||||
const uint64_t sectors = virtio_get_sectors();
|
const uint64_t sectors = virtio_get_sectors();
|
||||||
const uint64_t heads = virtio_get_heads();
|
const uint64_t heads = virtio_get_heads();
|
||||||
const uint64_t cylinder = p->eckd.cylinder
|
const uint64_t cylinder = chs->cylinder
|
||||||
+ ((p->eckd.head & 0xfff0) << 12);
|
+ ((chs->head & 0xfff0) << 12);
|
||||||
const uint64_t head = p->eckd.head & 0x000f;
|
const uint64_t head = chs->head & 0x000f;
|
||||||
const block_number_t block = sectors * heads * cylinder
|
const block_number_t block = sectors * heads * cylinder
|
||||||
+ sectors * head
|
+ sectors * head
|
||||||
+ p->eckd.sector
|
+ chs->sector
|
||||||
- 1; /* block nr starts with zero */
|
- 1; /* block nr starts with zero */
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool eckd_valid_address(BootMapPointer *p)
|
static bool eckd_valid_address(BootMapPointer *p)
|
||||||
{
|
{
|
||||||
const uint64_t head = p->eckd.head & 0x000f;
|
const uint64_t head = p->eckd.chs.head & 0x000f;
|
||||||
|
|
||||||
if (head >= virtio_get_heads()
|
if (head >= virtio_get_heads()
|
||||||
|| p->eckd.sector > virtio_get_sectors()
|
|| p->eckd.chs.sector > virtio_get_sectors()
|
||||||
|| p->eckd.sector <= 0) {
|
|| p->eckd.chs.sector <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!virtio_guessed_disk_nature() &&
|
if (!virtio_guessed_disk_nature() &&
|
||||||
eckd_block_num(p) >= virtio_get_blocks()) {
|
eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +144,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
|
|||||||
do {
|
do {
|
||||||
more_data = false;
|
more_data = false;
|
||||||
for (j = 0;; j++) {
|
for (j = 0;; j++) {
|
||||||
block_nr = eckd_block_num((void *)&(bprs[j].xeckd));
|
block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs);
|
||||||
if (is_null_block_number(block_nr)) { /* end of chunk */
|
if (is_null_block_number(block_nr)) { /* end of chunk */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -182,31 +186,105 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
|
|||||||
return block_nr;
|
return block_nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_eckd_boot_script(block_number_t mbr_block_nr)
|
static bool find_zipl_boot_menu_banner(int *offset)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Menu banner starts with "zIPL" */
|
||||||
|
for (i = 0; i < virtio_get_block_size() - 4; i++) {
|
||||||
|
if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) {
|
||||||
|
*offset = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||||
|
{
|
||||||
|
block_number_t cur_block_nr;
|
||||||
|
block_number_t prev_block_nr = 0;
|
||||||
|
block_number_t next_block_nr = 0;
|
||||||
|
EckdStage1b *s1b = (void *)sec;
|
||||||
|
int banner_offset;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Get Stage1b data */
|
||||||
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
|
read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
|
||||||
|
|
||||||
|
memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
|
||||||
|
|
||||||
|
/* Get Stage2 data */
|
||||||
|
for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) {
|
||||||
|
cur_block_nr = eckd_block_num(&s1b->seek[i].chs);
|
||||||
|
|
||||||
|
if (!cur_block_nr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
|
||||||
|
|
||||||
|
if (find_zipl_boot_menu_banner(&banner_offset)) {
|
||||||
|
/*
|
||||||
|
* Load the adjacent blocks to account for the
|
||||||
|
* possibility of menu data spanning multiple blocks.
|
||||||
|
*/
|
||||||
|
if (prev_block_nr) {
|
||||||
|
read_block(prev_block_nr, s2_prev_blk,
|
||||||
|
"Cannot read stage2 boot loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 < STAGE2_BLK_CNT_MAX) {
|
||||||
|
next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_block_nr) {
|
||||||
|
read_block(next_block_nr, s2_next_blk,
|
||||||
|
"Cannot read stage2 boot loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu_get_zipl_boot_index(s2_cur_blk + banner_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_block_nr = cur_block_nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sclp_print("No zipl boot menu data found. Booting default entry.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||||
|
block_number_t s1b_block_nr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned int loadparm = get_loadparm_index();
|
unsigned int loadparm = get_loadparm_index();
|
||||||
block_number_t block_nr;
|
block_number_t block_nr;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
ScsiMbr *bte = (void *)sec; /* Eckd bootmap table entry */
|
BootMapTable *bmt = (void *)sec;
|
||||||
BootMapScript *bms = (void *)sec;
|
BootMapScript *bms = (void *)sec;
|
||||||
|
|
||||||
|
if (menu_is_enabled_zipl()) {
|
||||||
|
loadparm = eckd_get_boot_menu_index(s1b_block_nr);
|
||||||
|
}
|
||||||
|
|
||||||
debug_print_int("loadparm", loadparm);
|
debug_print_int("loadparm", loadparm);
|
||||||
IPL_assert(loadparm < 31, "loadparm value greater than"
|
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
||||||
" maximum number of boot entries allowed");
|
" maximum number of boot entries allowed");
|
||||||
|
|
||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
read_block(mbr_block_nr, sec, "Cannot read MBR");
|
read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");
|
||||||
|
|
||||||
block_nr = eckd_block_num((void *)&(bte->blockptr[loadparm]));
|
block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs);
|
||||||
IPL_assert(block_nr != -1, "No Boot Map");
|
IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");
|
||||||
|
|
||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
read_block(block_nr, sec, "Cannot read Boot Map Script");
|
read_block(block_nr, sec, "Cannot read Boot Map Script");
|
||||||
|
|
||||||
for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) {
|
for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) {
|
||||||
address = bms->entry[i].address.load_address;
|
address = bms->entry[i].address.load_address;
|
||||||
block_nr = eckd_block_num(&(bms->entry[i].blkptr));
|
block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
block_nr = load_eckd_segments(block_nr, &address);
|
block_nr = load_eckd_segments(block_nr, &address);
|
||||||
@ -221,9 +299,9 @@ static void run_eckd_boot_script(block_number_t mbr_block_nr)
|
|||||||
static void ipl_eckd_cdl(void)
|
static void ipl_eckd_cdl(void)
|
||||||
{
|
{
|
||||||
XEckdMbr *mbr;
|
XEckdMbr *mbr;
|
||||||
Ipl2 *ipl2 = (void *)sec;
|
EckdCdlIpl2 *ipl2 = (void *)sec;
|
||||||
IplVolumeLabel *vlbl = (void *)sec;
|
IplVolumeLabel *vlbl = (void *)sec;
|
||||||
block_number_t block_nr;
|
block_number_t bmt_block_nr, s1b_block_nr;
|
||||||
|
|
||||||
/* we have just read the block #0 and recognized it as "IPL1" */
|
/* we have just read the block #0 and recognized it as "IPL1" */
|
||||||
sclp_print("CDL\n");
|
sclp_print("CDL\n");
|
||||||
@ -231,15 +309,18 @@ static void ipl_eckd_cdl(void)
|
|||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
read_block(1, ipl2, "Cannot read IPL2 record at block 1");
|
read_block(1, ipl2, "Cannot read IPL2 record at block 1");
|
||||||
|
|
||||||
mbr = &ipl2->u.x.mbr;
|
mbr = &ipl2->mbr;
|
||||||
IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record.");
|
IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record.");
|
||||||
IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size),
|
IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size),
|
||||||
"Bad block size in zIPL section of IPL2 record.");
|
"Bad block size in zIPL section of IPL2 record.");
|
||||||
IPL_assert(mbr->dev_type == DEV_TYPE_ECKD,
|
IPL_assert(mbr->dev_type == DEV_TYPE_ECKD,
|
||||||
"Non-ECKD device type in zIPL section of IPL2 record.");
|
"Non-ECKD device type in zIPL section of IPL2 record.");
|
||||||
|
|
||||||
/* save pointer to Boot Script */
|
/* save pointer to Boot Map Table */
|
||||||
block_nr = eckd_block_num((void *)&(mbr->blockptr));
|
bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs);
|
||||||
|
|
||||||
|
/* save pointer to Stage1b Data */
|
||||||
|
s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs);
|
||||||
|
|
||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
read_block(2, vlbl, "Cannot read Volume Label at block 2");
|
read_block(2, vlbl, "Cannot read Volume Label at block 2");
|
||||||
@ -249,7 +330,7 @@ static void ipl_eckd_cdl(void)
|
|||||||
"Invalid magic of volser block");
|
"Invalid magic of volser block");
|
||||||
print_volser(vlbl->f.volser);
|
print_volser(vlbl->f.volser);
|
||||||
|
|
||||||
run_eckd_boot_script(block_nr);
|
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||||
/* no return */
|
/* no return */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,8 +361,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
|
|||||||
|
|
||||||
static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||||
{
|
{
|
||||||
block_number_t block_nr;
|
block_number_t bmt_block_nr, s1b_block_nr;
|
||||||
BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */
|
EckdLdlIpl1 *ipl1 = (void *)sec;
|
||||||
|
|
||||||
if (mode != ECKD_LDL_UNLABELED) {
|
if (mode != ECKD_LDL_UNLABELED) {
|
||||||
print_eckd_ldl_msg(mode);
|
print_eckd_ldl_msg(mode);
|
||||||
@ -292,15 +373,20 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
|||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
read_block(0, sec, "Cannot read block 0 to grab boot info.");
|
read_block(0, sec, "Cannot read block 0 to grab boot info.");
|
||||||
if (mode == ECKD_LDL_UNLABELED) {
|
if (mode == ECKD_LDL_UNLABELED) {
|
||||||
if (!magic_match(bip->magic, ZIPL_MAGIC)) {
|
if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) {
|
||||||
return; /* not applicable layout */
|
return; /* not applicable layout */
|
||||||
}
|
}
|
||||||
sclp_print("unlabeled LDL.\n");
|
sclp_print("unlabeled LDL.\n");
|
||||||
}
|
}
|
||||||
verify_boot_info(bip);
|
verify_boot_info(&ipl1->bip);
|
||||||
|
|
||||||
block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr));
|
/* save pointer to Boot Map Table */
|
||||||
run_eckd_boot_script(block_nr);
|
bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs);
|
||||||
|
|
||||||
|
/* save pointer to Stage1b Data */
|
||||||
|
s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs);
|
||||||
|
|
||||||
|
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||||
/* no return */
|
/* no return */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +411,7 @@ static void print_eckd_msg(void)
|
|||||||
|
|
||||||
static void ipl_eckd(void)
|
static void ipl_eckd(void)
|
||||||
{
|
{
|
||||||
ScsiMbr *mbr = (void *)sec;
|
XEckdMbr *mbr = (void *)sec;
|
||||||
LDL_VTOC *vlbl = (void *)sec;
|
LDL_VTOC *vlbl = (void *)sec;
|
||||||
|
|
||||||
print_eckd_msg();
|
print_eckd_msg();
|
||||||
@ -449,10 +535,8 @@ static void zipl_run(ScsiBlockPtr *pte)
|
|||||||
static void ipl_scsi(void)
|
static void ipl_scsi(void)
|
||||||
{
|
{
|
||||||
ScsiMbr *mbr = (void *)sec;
|
ScsiMbr *mbr = (void *)sec;
|
||||||
uint8_t *ns, *ns_end;
|
|
||||||
int program_table_entries = 0;
|
int program_table_entries = 0;
|
||||||
const int pte_len = sizeof(ScsiBlockPtr);
|
BootMapTable *prog_table = (void *)sec;
|
||||||
ScsiBlockPtr *prog_table_entry = NULL;
|
|
||||||
unsigned int loadparm = get_loadparm_index();
|
unsigned int loadparm = get_loadparm_index();
|
||||||
|
|
||||||
/* Grab the MBR */
|
/* Grab the MBR */
|
||||||
@ -467,34 +551,32 @@ static void ipl_scsi(void)
|
|||||||
debug_print_int("MBR Version", mbr->version_id);
|
debug_print_int("MBR Version", mbr->version_id);
|
||||||
IPL_check(mbr->version_id == 1,
|
IPL_check(mbr->version_id == 1,
|
||||||
"Unknown MBR layout version, assuming version 1");
|
"Unknown MBR layout version, assuming version 1");
|
||||||
debug_print_int("program table", mbr->blockptr[0].blockno);
|
debug_print_int("program table", mbr->pt.blockno);
|
||||||
IPL_assert(mbr->blockptr[0].blockno, "No Program Table");
|
IPL_assert(mbr->pt.blockno, "No Program Table");
|
||||||
|
|
||||||
/* Parse the program table */
|
/* Parse the program table */
|
||||||
read_block(mbr->blockptr[0].blockno, sec,
|
read_block(mbr->pt.blockno, sec, "Error reading Program Table");
|
||||||
"Error reading Program Table");
|
|
||||||
|
|
||||||
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
|
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
|
||||||
|
|
||||||
debug_print_int("loadparm index", loadparm);
|
while (program_table_entries <= MAX_TABLE_ENTRIES) {
|
||||||
ns_end = sec + virtio_get_block_size();
|
if (!prog_table->entry[program_table_entries].scsi.blockno) {
|
||||||
for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) {
|
|
||||||
prog_table_entry = (ScsiBlockPtr *)ns;
|
|
||||||
if (!prog_table_entry->blockno) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
program_table_entries++;
|
program_table_entries++;
|
||||||
if (program_table_entries == loadparm + 1) {
|
|
||||||
break; /* selected entry found */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print_int("program table entries", program_table_entries);
|
debug_print_int("program table entries", program_table_entries);
|
||||||
|
|
||||||
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
||||||
|
|
||||||
zipl_run(prog_table_entry); /* no return */
|
if (menu_is_enabled_enum()) {
|
||||||
|
loadparm = menu_get_enum_boot_index(program_table_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_print_int("loadparm", loadparm);
|
||||||
|
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
||||||
|
" maximum number of boot entries allowed");
|
||||||
|
|
||||||
|
zipl_run(&prog_table->entry[loadparm].scsi); /* no return */
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
@ -512,7 +594,7 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
|
|||||||
"Failed to read image sector 0");
|
"Failed to read image sector 0");
|
||||||
|
|
||||||
/* Checking bytes 8 - 32 for S390 Linux magic */
|
/* Checking bytes 8 - 32 for S390 Linux magic */
|
||||||
return !_memcmp(magic_sec + 8, linux_s390_magic, 24);
|
return !memcmp(magic_sec + 8, linux_s390_magic, 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Location of the current sector of the directory */
|
/* Location of the current sector of the directory */
|
||||||
@ -641,7 +723,7 @@ static uint32_t find_iso_bc(void)
|
|||||||
if (vd->type == VOL_DESC_TYPE_BOOT) {
|
if (vd->type == VOL_DESC_TYPE_BOOT) {
|
||||||
IsoVdElTorito *et = &vd->vd.boot;
|
IsoVdElTorito *et = &vd->vd.boot;
|
||||||
|
|
||||||
if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) {
|
if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) {
|
||||||
return bswap32(et->bc_offset);
|
return bswap32(et->bc_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,14 @@ typedef struct FbaBlockPtr {
|
|||||||
uint16_t blockct;
|
uint16_t blockct;
|
||||||
} __attribute__ ((packed)) FbaBlockPtr;
|
} __attribute__ ((packed)) FbaBlockPtr;
|
||||||
|
|
||||||
typedef struct EckdBlockPtr {
|
typedef struct EckdCHS {
|
||||||
uint16_t cylinder; /* cylinder/head/sector is an address of the block */
|
uint16_t cylinder;
|
||||||
uint16_t head;
|
uint16_t head;
|
||||||
uint8_t sector;
|
uint8_t sector;
|
||||||
|
} __attribute__ ((packed)) EckdCHS;
|
||||||
|
|
||||||
|
typedef struct EckdBlockPtr {
|
||||||
|
EckdCHS chs; /* cylinder/head/sector is an address of the block */
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint8_t count; /* (size_in_blocks-1);
|
uint8_t count; /* (size_in_blocks-1);
|
||||||
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
|
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
|
||||||
@ -53,6 +57,15 @@ typedef union BootMapPointer {
|
|||||||
ExtEckdBlockPtr xeckd;
|
ExtEckdBlockPtr xeckd;
|
||||||
} __attribute__ ((packed)) BootMapPointer;
|
} __attribute__ ((packed)) BootMapPointer;
|
||||||
|
|
||||||
|
#define MAX_TABLE_ENTRIES 30
|
||||||
|
|
||||||
|
/* aka Program Table */
|
||||||
|
typedef struct BootMapTable {
|
||||||
|
uint8_t magic[4];
|
||||||
|
uint8_t reserved[12];
|
||||||
|
BootMapPointer entry[];
|
||||||
|
} __attribute__ ((packed)) BootMapTable;
|
||||||
|
|
||||||
typedef struct ComponentEntry {
|
typedef struct ComponentEntry {
|
||||||
ScsiBlockPtr data;
|
ScsiBlockPtr data;
|
||||||
uint8_t pad[7];
|
uint8_t pad[7];
|
||||||
@ -70,10 +83,11 @@ typedef struct ScsiMbr {
|
|||||||
uint8_t magic[4];
|
uint8_t magic[4];
|
||||||
uint32_t version_id;
|
uint32_t version_id;
|
||||||
uint8_t reserved[8];
|
uint8_t reserved[8];
|
||||||
ScsiBlockPtr blockptr[];
|
ScsiBlockPtr pt; /* block pointer to program table */
|
||||||
} __attribute__ ((packed)) ScsiMbr;
|
} __attribute__ ((packed)) ScsiMbr;
|
||||||
|
|
||||||
#define ZIPL_MAGIC "zIPL"
|
#define ZIPL_MAGIC "zIPL"
|
||||||
|
#define ZIPL_MAGIC_EBCDIC "\xa9\xc9\xd7\xd3"
|
||||||
#define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */
|
#define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */
|
||||||
#define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */
|
#define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */
|
||||||
#define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */
|
#define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */
|
||||||
@ -226,22 +240,45 @@ typedef struct BootInfo { /* @ 0x70, record #0 */
|
|||||||
} bp;
|
} bp;
|
||||||
} __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */
|
} __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */
|
||||||
|
|
||||||
typedef struct Ipl1 {
|
/*
|
||||||
unsigned char key[4]; /* == "IPL1" */
|
* Structs for IPL
|
||||||
unsigned char data[24];
|
*/
|
||||||
} __attribute__((packed)) Ipl1;
|
#define STAGE2_BLK_CNT_MAX 24 /* Stage 1b can load up to 24 blocks */
|
||||||
|
|
||||||
typedef struct Ipl2 {
|
typedef struct EckdCdlIpl1 {
|
||||||
unsigned char key[4]; /* == "IPL2" */
|
uint8_t key[4]; /* == "IPL1" */
|
||||||
union {
|
uint8_t data[24];
|
||||||
unsigned char data[144];
|
} __attribute__((packed)) EckdCdlIpl1;
|
||||||
struct {
|
|
||||||
unsigned char reserved1[92-4];
|
typedef struct EckdSeekArg {
|
||||||
XEckdMbr mbr;
|
uint16_t pad;
|
||||||
unsigned char reserved2[144-(92-4)-sizeof(XEckdMbr)];
|
EckdCHS chs;
|
||||||
} x;
|
uint8_t pad2;
|
||||||
} u;
|
} __attribute__ ((packed)) EckdSeekArg;
|
||||||
} __attribute__((packed)) Ipl2;
|
|
||||||
|
typedef struct EckdStage1b {
|
||||||
|
uint8_t reserved[32 * STAGE2_BLK_CNT_MAX];
|
||||||
|
struct EckdSeekArg seek[STAGE2_BLK_CNT_MAX];
|
||||||
|
uint8_t unused[64];
|
||||||
|
} __attribute__ ((packed)) EckdStage1b;
|
||||||
|
|
||||||
|
typedef struct EckdStage1 {
|
||||||
|
uint8_t reserved[72];
|
||||||
|
struct EckdSeekArg seek[2];
|
||||||
|
} __attribute__ ((packed)) EckdStage1;
|
||||||
|
|
||||||
|
typedef struct EckdCdlIpl2 {
|
||||||
|
uint8_t key[4]; /* == "IPL2" */
|
||||||
|
struct EckdStage1 stage1;
|
||||||
|
XEckdMbr mbr;
|
||||||
|
uint8_t reserved[24];
|
||||||
|
} __attribute__((packed)) EckdCdlIpl2;
|
||||||
|
|
||||||
|
typedef struct EckdLdlIpl1 {
|
||||||
|
uint8_t reserved[24];
|
||||||
|
struct EckdStage1 stage1;
|
||||||
|
BootInfo bip; /* BootInfo is MBR for LDL */
|
||||||
|
} __attribute__((packed)) EckdLdlIpl1;
|
||||||
|
|
||||||
typedef struct IplVolumeLabel {
|
typedef struct IplVolumeLabel {
|
||||||
unsigned char key[4]; /* == "VOL1" */
|
unsigned char key[4]; /* == "VOL1" */
|
||||||
@ -310,20 +347,6 @@ static inline bool magic_match(const void *data, const void *magic)
|
|||||||
return *((uint32_t *)data) == *((uint32_t *)magic);
|
return *((uint32_t *)data) == *((uint32_t *)magic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int _memcmp(const void *s1, const void *s2, size_t n)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
const uint8_t *p1 = s1, *p2 = s2;
|
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
if (p1[i] != p2[i]) {
|
|
||||||
return p1[i] > p2[i] ? 1 : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t iso_733_to_u32(uint64_t x)
|
static inline uint32_t iso_733_to_u32(uint64_t x)
|
||||||
{
|
{
|
||||||
return (uint32_t)x;
|
return (uint32_t)x;
|
||||||
@ -416,7 +439,7 @@ const uint8_t vol_desc_magic[] = "CD001";
|
|||||||
|
|
||||||
static inline bool is_iso_vd_valid(IsoVolDesc *vd)
|
static inline bool is_iso_vd_valid(IsoVolDesc *vd)
|
||||||
{
|
{
|
||||||
return !_memcmp(&vd->ident[0], vol_desc_magic, 5) &&
|
return !memcmp(&vd->ident[0], vol_desc_magic, 5) &&
|
||||||
vd->version == 0x1 &&
|
vd->version == 0x1 &&
|
||||||
vd->type <= VOL_DESC_TYPE_PARTITION;
|
vd->type <= VOL_DESC_TYPE_PARTITION;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
#define IPLB_H
|
#define IPLB_H
|
||||||
|
|
||||||
struct IplBlockCcw {
|
struct IplBlockCcw {
|
||||||
uint64_t netboot_start_addr;
|
uint8_t reserved0[85];
|
||||||
uint8_t reserved0[77];
|
|
||||||
uint8_t ssid;
|
uint8_t ssid;
|
||||||
uint16_t devno;
|
uint16_t devno;
|
||||||
uint8_t vm_flags;
|
uint8_t vm_flags;
|
||||||
@ -73,6 +72,27 @@ typedef struct IplParameterBlock IplParameterBlock;
|
|||||||
|
|
||||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
|
|
||||||
|
#define QIPL_ADDRESS 0xcc
|
||||||
|
|
||||||
|
/* Boot Menu flags */
|
||||||
|
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||||
|
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This definition must be kept in sync with the defininition
|
||||||
|
* in hw/s390x/ipl.h
|
||||||
|
*/
|
||||||
|
struct QemuIplParameters {
|
||||||
|
uint8_t qipl_flags;
|
||||||
|
uint8_t reserved1[3];
|
||||||
|
uint64_t netboot_start_addr;
|
||||||
|
uint32_t boot_menu_timeout;
|
||||||
|
uint8_t reserved2[12];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
typedef struct QemuIplParameters QemuIplParameters;
|
||||||
|
|
||||||
|
extern QemuIplParameters qipl;
|
||||||
|
|
||||||
#define S390_IPL_TYPE_FCP 0x00
|
#define S390_IPL_TYPE_FCP 0x00
|
||||||
#define S390_IPL_TYPE_CCW 0x02
|
#define S390_IPL_TYPE_CCW 0x02
|
||||||
#define S390_IPL_TYPE_QEMU_SCSI 0xff
|
#define S390_IPL_TYPE_QEMU_SCSI 0xff
|
||||||
|
88
pc-bios/s390-ccw/libc.c
Normal file
88
pc-bios/s390-ccw/libc.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* libc-style definitions and functions
|
||||||
|
*
|
||||||
|
* Copyright 2018 IBM Corp.
|
||||||
|
* Author(s): Collin L. Walling <walling@linux.vnet.ibm.com>
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libc.h"
|
||||||
|
#include "s390-ccw.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* atoui:
|
||||||
|
* @str: the string to be converted.
|
||||||
|
*
|
||||||
|
* Given a string @str, convert it to an integer. Leading spaces are
|
||||||
|
* ignored. Any other non-numerical value will terminate the conversion
|
||||||
|
* and return 0. This function only handles numbers between 0 and
|
||||||
|
* UINT64_MAX inclusive.
|
||||||
|
*
|
||||||
|
* Returns: an integer converted from the string @str, or the number 0
|
||||||
|
* if an error occurred.
|
||||||
|
*/
|
||||||
|
uint64_t atoui(const char *str)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
if (!str || !str[0]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*str == ' ') {
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*str) {
|
||||||
|
if (!isdigit(*str)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val = val * 10 + *str - '0';
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uitoa:
|
||||||
|
* @num: an integer (base 10) to be converted.
|
||||||
|
* @str: a pointer to a string to store the conversion.
|
||||||
|
* @len: the length of the passed string.
|
||||||
|
*
|
||||||
|
* Given an integer @num, convert it to a string. The string @str must be
|
||||||
|
* allocated beforehand. The resulting string will be null terminated and
|
||||||
|
* returned. This function only handles numbers between 0 and UINT64_MAX
|
||||||
|
* inclusive.
|
||||||
|
*
|
||||||
|
* Returns: the string @str of the converted integer @num
|
||||||
|
*/
|
||||||
|
char *uitoa(uint64_t num, char *str, size_t len)
|
||||||
|
{
|
||||||
|
size_t num_idx = 1; /* account for NUL */
|
||||||
|
uint64_t tmp = num;
|
||||||
|
|
||||||
|
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
|
||||||
|
|
||||||
|
/* Count indices of num */
|
||||||
|
while ((tmp /= 10) != 0) {
|
||||||
|
num_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we have enough space for num and NUL */
|
||||||
|
IPL_assert(len > num_idx, "uitoa: array too small for conversion");
|
||||||
|
|
||||||
|
str[num_idx--] = '\0';
|
||||||
|
|
||||||
|
/* Convert int to string */
|
||||||
|
while (num_idx >= 0) {
|
||||||
|
str[num_idx--] = num % 10 + '0';
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* libc-style definitions and functions
|
* libc-style definitions and functions
|
||||||
*
|
*
|
||||||
|
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||||
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the
|
* under the terms of the GNU General Public License as published by the
|
||||||
* Free Software Foundation; either version 2 of the License, or (at your
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
@ -19,7 +21,7 @@ typedef unsigned long long uint64_t;
|
|||||||
|
|
||||||
static inline void *memset(void *s, int c, size_t n)
|
static inline void *memset(void *s, int c, size_t n)
|
||||||
{
|
{
|
||||||
int i;
|
size_t i;
|
||||||
unsigned char *p = s;
|
unsigned char *p = s;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
@ -33,7 +35,7 @@ static inline void *memcpy(void *s1, const void *s2, size_t n)
|
|||||||
{
|
{
|
||||||
uint8_t *dest = s1;
|
uint8_t *dest = s1;
|
||||||
const uint8_t *src = s2;
|
const uint8_t *src = s2;
|
||||||
int i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
dest[i] = src[i];
|
dest[i] = src[i];
|
||||||
@ -42,4 +44,35 @@ static inline void *memcpy(void *s1, const void *s2, size_t n)
|
|||||||
return s1;
|
return s1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int memcmp(const void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const uint8_t *p1 = s1, *p2 = s2;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (p1[i] != p2[i]) {
|
||||||
|
return p1[i] > p2[i] ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t strlen(const char *str)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; *str; i++) {
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int isdigit(int c)
|
||||||
|
{
|
||||||
|
return (c >= '0') && (c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t atoui(const char *str);
|
||||||
|
char *uitoa(uint64_t num, char *str, size_t len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,6 +16,11 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
|||||||
static SubChannelId blk_schid = { .one = 1 };
|
static SubChannelId blk_schid = { .one = 1 };
|
||||||
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
QemuIplParameters qipl;
|
||||||
|
|
||||||
|
#define LOADPARM_PROMPT "PROMPT "
|
||||||
|
#define LOADPARM_EMPTY "........"
|
||||||
|
#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Priniciples of Operations (SA22-7832-09) chapter 17 requires that
|
* Priniciples of Operations (SA22-7832-09) chapter 17 requires that
|
||||||
@ -40,22 +45,7 @@ void panic(const char *string)
|
|||||||
|
|
||||||
unsigned int get_loadparm_index(void)
|
unsigned int get_loadparm_index(void)
|
||||||
{
|
{
|
||||||
const char *lp = loadparm;
|
return atoui(loadparm);
|
||||||
int i;
|
|
||||||
unsigned int idx = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
char c = lp[i];
|
|
||||||
|
|
||||||
if (c < '0' || c > '9') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx *= 10;
|
|
||||||
idx += c - '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool find_dev(Schib *schib, int dev_no)
|
static bool find_dev(Schib *schib, int dev_no)
|
||||||
@ -88,6 +78,27 @@ static bool find_dev(Schib *schib, int dev_no)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void menu_setup(void)
|
||||||
|
{
|
||||||
|
if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) {
|
||||||
|
menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If loadparm was set to any other value, then do not enable menu */
|
||||||
|
if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (iplb.pbt) {
|
||||||
|
case S390_IPL_TYPE_CCW:
|
||||||
|
case S390_IPL_TYPE_QEMU_SCSI:
|
||||||
|
menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK,
|
||||||
|
qipl.boot_menu_timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_setup(void)
|
static void virtio_setup(void)
|
||||||
{
|
{
|
||||||
Schib schib;
|
Schib schib;
|
||||||
@ -96,6 +107,7 @@ static void virtio_setup(void)
|
|||||||
uint16_t dev_no;
|
uint16_t dev_no;
|
||||||
char ldp[] = "LOADPARM=[________]\n";
|
char ldp[] = "LOADPARM=[________]\n";
|
||||||
VDev *vdev = virtio_get_device();
|
VDev *vdev = virtio_get_device();
|
||||||
|
QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We unconditionally enable mss support. In every sane configuration,
|
* We unconditionally enable mss support. In every sane configuration,
|
||||||
@ -108,6 +120,8 @@ static void virtio_setup(void)
|
|||||||
memcpy(ldp + 10, loadparm, 8);
|
memcpy(ldp + 10, loadparm, 8);
|
||||||
sclp_print(ldp);
|
sclp_print(ldp);
|
||||||
|
|
||||||
|
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
||||||
|
|
||||||
if (store_iplb(&iplb)) {
|
if (store_iplb(&iplb)) {
|
||||||
switch (iplb.pbt) {
|
switch (iplb.pbt) {
|
||||||
case S390_IPL_TYPE_CCW:
|
case S390_IPL_TYPE_CCW:
|
||||||
@ -128,6 +142,7 @@ static void virtio_setup(void)
|
|||||||
default:
|
default:
|
||||||
panic("List-directed IPL not supported yet!\n");
|
panic("List-directed IPL not supported yet!\n");
|
||||||
}
|
}
|
||||||
|
menu_setup();
|
||||||
} else {
|
} else {
|
||||||
for (ssid = 0; ssid < 0x3; ssid++) {
|
for (ssid = 0; ssid < 0x3; ssid++) {
|
||||||
blk_schid.ssid = ssid;
|
blk_schid.ssid = ssid;
|
||||||
@ -142,7 +157,7 @@ static void virtio_setup(void)
|
|||||||
|
|
||||||
if (virtio_get_device_type() == VIRTIO_ID_NET) {
|
if (virtio_get_device_type() == VIRTIO_ID_NET) {
|
||||||
sclp_print("Network boot device detected\n");
|
sclp_print("Network boot device detected\n");
|
||||||
vdev->netboot_start_addr = iplb.ccw.netboot_start_addr;
|
vdev->netboot_start_addr = qipl.netboot_start_addr;
|
||||||
} else {
|
} else {
|
||||||
virtio_blk_setup_device(blk_schid);
|
virtio_blk_setup_device(blk_schid);
|
||||||
|
|
||||||
|
249
pc-bios/s390-ccw/menu.c
Normal file
249
pc-bios/s390-ccw/menu.c
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* QEMU S390 Interactive Boot Menu
|
||||||
|
*
|
||||||
|
* Copyright 2018 IBM Corp.
|
||||||
|
* Author: Collin L. Walling <walling@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 "libc.h"
|
||||||
|
#include "s390-ccw.h"
|
||||||
|
#include "sclp.h"
|
||||||
|
|
||||||
|
#define KEYCODE_NO_INP '\0'
|
||||||
|
#define KEYCODE_ESCAPE '\033'
|
||||||
|
#define KEYCODE_BACKSP '\177'
|
||||||
|
#define KEYCODE_ENTER '\r'
|
||||||
|
|
||||||
|
/* Offsets from zipl fields to zipl banner start */
|
||||||
|
#define ZIPL_TIMEOUT_OFFSET 138
|
||||||
|
#define ZIPL_FLAG_OFFSET 140
|
||||||
|
|
||||||
|
#define TOD_CLOCK_MILLISECOND 0x3e8000
|
||||||
|
|
||||||
|
#define LOW_CORE_EXTERNAL_INT_ADDR 0x86
|
||||||
|
#define CLOCK_COMPARATOR_INT 0X1004
|
||||||
|
|
||||||
|
static uint8_t flag;
|
||||||
|
static uint64_t timeout;
|
||||||
|
|
||||||
|
static inline void enable_clock_int(void)
|
||||||
|
{
|
||||||
|
uint64_t tmp = 0;
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"stctg 0,0,%0\n"
|
||||||
|
"oi 6+%0, 0x8\n"
|
||||||
|
"lctlg 0,0,%0"
|
||||||
|
: : "Q" (tmp) : "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void disable_clock_int(void)
|
||||||
|
{
|
||||||
|
uint64_t tmp = 0;
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"stctg 0,0,%0\n"
|
||||||
|
"ni 6+%0, 0xf7\n"
|
||||||
|
"lctlg 0,0,%0"
|
||||||
|
: : "Q" (tmp) : "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_clock_comparator(uint64_t time)
|
||||||
|
{
|
||||||
|
asm volatile("sckc %0" : : "Q" (time));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool check_clock_int(void)
|
||||||
|
{
|
||||||
|
uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
|
||||||
|
|
||||||
|
consume_sclp_int();
|
||||||
|
|
||||||
|
return *code == CLOCK_COMPARATOR_INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_prompt(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
char inp[2] = {};
|
||||||
|
uint8_t idx = 0;
|
||||||
|
uint64_t time;
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
|
||||||
|
set_clock_comparator(time);
|
||||||
|
enable_clock_int();
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!check_clock_int()) {
|
||||||
|
|
||||||
|
sclp_read(inp, 1); /* Process only one character at a time */
|
||||||
|
|
||||||
|
switch (inp[0]) {
|
||||||
|
case KEYCODE_NO_INP:
|
||||||
|
case KEYCODE_ESCAPE:
|
||||||
|
continue;
|
||||||
|
case KEYCODE_BACKSP:
|
||||||
|
if (idx > 0) {
|
||||||
|
buf[--idx] = 0;
|
||||||
|
sclp_print("\b \b");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case KEYCODE_ENTER:
|
||||||
|
disable_clock_int();
|
||||||
|
return idx;
|
||||||
|
default:
|
||||||
|
/* Echo input and add to buffer */
|
||||||
|
if (idx < len) {
|
||||||
|
buf[idx++] = inp[0];
|
||||||
|
sclp_print(inp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_clock_int();
|
||||||
|
*buf = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_index(void)
|
||||||
|
{
|
||||||
|
char buf[11];
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
|
||||||
|
|
||||||
|
len = read_prompt(buf, sizeof(buf) - 1);
|
||||||
|
|
||||||
|
sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
|
||||||
|
|
||||||
|
/* If no input, boot default */
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for erroneous input */
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (!isdigit(buf[i])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return atoui(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boot_menu_prompt(bool retry)
|
||||||
|
{
|
||||||
|
char tmp[11];
|
||||||
|
|
||||||
|
if (retry) {
|
||||||
|
sclp_print("\nError: undefined configuration"
|
||||||
|
"\nPlease choose:\n");
|
||||||
|
} else if (timeout > 0) {
|
||||||
|
sclp_print("Please choose (default will boot in ");
|
||||||
|
sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
|
||||||
|
sclp_print(" seconds):\n");
|
||||||
|
} else {
|
||||||
|
sclp_print("Please choose:\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_boot_index(int entries)
|
||||||
|
{
|
||||||
|
int boot_index;
|
||||||
|
bool retry = false;
|
||||||
|
char tmp[5];
|
||||||
|
|
||||||
|
do {
|
||||||
|
boot_menu_prompt(retry);
|
||||||
|
boot_index = get_index();
|
||||||
|
retry = true;
|
||||||
|
} while (boot_index < 0 || boot_index >= entries);
|
||||||
|
|
||||||
|
sclp_print("\nBooting entry #");
|
||||||
|
sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
|
||||||
|
|
||||||
|
return boot_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zipl_println(const char *data, size_t len)
|
||||||
|
{
|
||||||
|
char buf[len + 2];
|
||||||
|
|
||||||
|
ebcdic_to_ascii(data, buf, len);
|
||||||
|
buf[len] = '\n';
|
||||||
|
buf[len + 1] = '\0';
|
||||||
|
|
||||||
|
sclp_print(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int menu_get_zipl_boot_index(const char *menu_data)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
int entries;
|
||||||
|
uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
|
||||||
|
uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
|
||||||
|
|
||||||
|
if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
|
||||||
|
if (!zipl_flag) {
|
||||||
|
return 0; /* Boot default */
|
||||||
|
}
|
||||||
|
/* zipl stores timeout as seconds */
|
||||||
|
timeout = zipl_timeout * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print and count all menu items, including the banner */
|
||||||
|
for (entries = 0; *menu_data; entries++) {
|
||||||
|
len = strlen(menu_data);
|
||||||
|
zipl_println(menu_data, len);
|
||||||
|
menu_data += len + 1;
|
||||||
|
|
||||||
|
if (entries < 2) {
|
||||||
|
sclp_print("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sclp_print("\n");
|
||||||
|
return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int menu_get_enum_boot_index(int entries)
|
||||||
|
{
|
||||||
|
char tmp[4];
|
||||||
|
|
||||||
|
sclp_print("s390x Enumerated Boot Menu.\n\n");
|
||||||
|
|
||||||
|
sclp_print(uitoa(entries, tmp, sizeof(tmp)));
|
||||||
|
sclp_print(" entries detected. Select from boot index 0 to ");
|
||||||
|
sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
|
||||||
|
sclp_print(".\n\n");
|
||||||
|
|
||||||
|
return get_boot_index(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
|
||||||
|
{
|
||||||
|
flag = boot_menu_flag;
|
||||||
|
timeout = boot_menu_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_is_enabled_zipl(void)
|
||||||
|
{
|
||||||
|
return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_is_enabled_enum(void)
|
||||||
|
{
|
||||||
|
return flag & QIPL_FLAG_BM_OPTS_CMD;
|
||||||
|
}
|
@ -69,8 +69,10 @@ unsigned int get_loadparm_index(void);
|
|||||||
|
|
||||||
/* sclp.c */
|
/* sclp.c */
|
||||||
void sclp_print(const char *string);
|
void sclp_print(const char *string);
|
||||||
|
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
|
||||||
void sclp_setup(void);
|
void sclp_setup(void);
|
||||||
void sclp_get_loadparm_ascii(char *loadparm);
|
void sclp_get_loadparm_ascii(char *loadparm);
|
||||||
|
int sclp_read(char *str, size_t count);
|
||||||
|
|
||||||
/* virtio.c */
|
/* virtio.c */
|
||||||
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||||
@ -79,11 +81,19 @@ bool virtio_is_supported(SubChannelId schid);
|
|||||||
void virtio_blk_setup_device(SubChannelId schid);
|
void virtio_blk_setup_device(SubChannelId schid);
|
||||||
int virtio_read(ulong sector, void *load_addr);
|
int virtio_read(ulong sector, void *load_addr);
|
||||||
int enable_mss_facility(void);
|
int enable_mss_facility(void);
|
||||||
|
u64 get_clock(void);
|
||||||
ulong get_second(void);
|
ulong get_second(void);
|
||||||
|
|
||||||
/* bootmap.c */
|
/* bootmap.c */
|
||||||
void zipl_load(void);
|
void zipl_load(void);
|
||||||
|
|
||||||
|
/* menu.c */
|
||||||
|
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout);
|
||||||
|
int menu_get_zipl_boot_index(const char *menu_data);
|
||||||
|
bool menu_is_enabled_zipl(void);
|
||||||
|
int menu_get_enum_boot_index(int entries);
|
||||||
|
bool menu_is_enabled_enum(void);
|
||||||
|
|
||||||
static inline void fill_hex(char *out, unsigned char val)
|
static inline void fill_hex(char *out, unsigned char val)
|
||||||
{
|
{
|
||||||
const char hex[] = "0123456789abcdef";
|
const char hex[] = "0123456789abcdef";
|
||||||
|
@ -46,31 +46,21 @@ static int sclp_service_call(unsigned int command, void *sccb)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sclp_set_write_mask(void)
|
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask)
|
||||||
{
|
{
|
||||||
WriteEventMask *sccb = (void *)_sccb;
|
WriteEventMask *sccb = (void *)_sccb;
|
||||||
|
|
||||||
sccb->h.length = sizeof(WriteEventMask);
|
sccb->h.length = sizeof(WriteEventMask);
|
||||||
sccb->mask_length = sizeof(unsigned int);
|
sccb->mask_length = sizeof(unsigned int);
|
||||||
sccb->receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
sccb->cp_receive_mask = receive_mask;
|
||||||
sccb->cp_receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
sccb->cp_send_mask = send_mask;
|
||||||
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);
|
sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sclp_setup(void)
|
void sclp_setup(void)
|
||||||
{
|
{
|
||||||
sclp_set_write_mask();
|
sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
|
||||||
}
|
|
||||||
|
|
||||||
static int _strlen(const char *str)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; *str; i++)
|
|
||||||
str++;
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long write(int fd, const void *str, size_t len)
|
long write(int fd, const void *str, size_t len)
|
||||||
@ -113,7 +103,7 @@ long write(int fd, const void *str, size_t len)
|
|||||||
|
|
||||||
void sclp_print(const char *str)
|
void sclp_print(const char *str)
|
||||||
{
|
{
|
||||||
write(1, str, _strlen(str));
|
write(1, str, strlen(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sclp_get_loadparm_ascii(char *loadparm)
|
void sclp_get_loadparm_ascii(char *loadparm)
|
||||||
@ -127,3 +117,22 @@ void sclp_get_loadparm_ascii(char *loadparm)
|
|||||||
ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8);
|
ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sclp_read(char *str, size_t count)
|
||||||
|
{
|
||||||
|
ReadEventData *sccb = (void *)_sccb;
|
||||||
|
char *buf = (char *)(&sccb->ebh) + 7;
|
||||||
|
|
||||||
|
/* If count exceeds max buffer size, then restrict it to the max size */
|
||||||
|
if (count > SCCB_SIZE - 8) {
|
||||||
|
count = SCCB_SIZE - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
sccb->h.length = SCCB_SIZE;
|
||||||
|
sccb->h.function_code = SCLP_UNCONDITIONAL_READ;
|
||||||
|
|
||||||
|
sclp_service_call(SCLP_CMD_READ_EVENT_DATA, sccb);
|
||||||
|
memcpy(str, buf, count);
|
||||||
|
|
||||||
|
return sccb->ebh.length - 7;
|
||||||
|
}
|
||||||
|
@ -176,7 +176,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 get_clock(void)
|
u64 get_clock(void)
|
||||||
{
|
{
|
||||||
u64 r;
|
u64 r;
|
||||||
|
|
||||||
|
BIN
pc-bios/s390-netboot.img
Executable file → Normal file
BIN
pc-bios/s390-netboot.img
Executable file → Normal file
Binary file not shown.
115
qapi-schema.json
115
qapi-schema.json
@ -408,12 +408,14 @@
|
|||||||
# @CpuInfoArch:
|
# @CpuInfoArch:
|
||||||
#
|
#
|
||||||
# An enumeration of cpu types that enable additional information during
|
# An enumeration of cpu types that enable additional information during
|
||||||
# @query-cpus.
|
# @query-cpus and @query-cpus-fast.
|
||||||
|
#
|
||||||
|
# @s390: since 2.12
|
||||||
#
|
#
|
||||||
# Since: 2.6
|
# Since: 2.6
|
||||||
##
|
##
|
||||||
{ 'enum': 'CpuInfoArch',
|
{ 'enum': 'CpuInfoArch',
|
||||||
'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] }
|
'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @CpuInfo:
|
# @CpuInfo:
|
||||||
@ -452,6 +454,7 @@
|
|||||||
'ppc': 'CpuInfoPPC',
|
'ppc': 'CpuInfoPPC',
|
||||||
'mips': 'CpuInfoMIPS',
|
'mips': 'CpuInfoMIPS',
|
||||||
'tricore': 'CpuInfoTricore',
|
'tricore': 'CpuInfoTricore',
|
||||||
|
's390': 'CpuInfoS390',
|
||||||
'other': 'CpuInfoOther' } }
|
'other': 'CpuInfoOther' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -521,11 +524,40 @@
|
|||||||
##
|
##
|
||||||
{ 'struct': 'CpuInfoOther', 'data': { } }
|
{ 'struct': 'CpuInfoOther', 'data': { } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @CpuS390State:
|
||||||
|
#
|
||||||
|
# An enumeration of cpu states that can be assumed by a virtual
|
||||||
|
# S390 CPU
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'enum': 'CpuS390State',
|
||||||
|
'prefix': 'S390_CPU_STATE',
|
||||||
|
'data': [ 'uninitialized', 'stopped', 'check-stop', 'operating', 'load' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @CpuInfoS390:
|
||||||
|
#
|
||||||
|
# Additional information about a virtual S390 CPU
|
||||||
|
#
|
||||||
|
# @cpu-state: the virtual CPU's state
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'struct': 'CpuInfoS390', 'data': { 'cpu-state': 'CpuS390State' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-cpus:
|
# @query-cpus:
|
||||||
#
|
#
|
||||||
# Returns a list of information about each virtual CPU.
|
# Returns a list of information about each virtual CPU.
|
||||||
#
|
#
|
||||||
|
# This command causes vCPU threads to exit to userspace, which causes
|
||||||
|
# a small interruption to guest CPU execution. This will have a negative
|
||||||
|
# impact on realtime guests and other latency sensitive guest workloads.
|
||||||
|
# It is recommended to use @query-cpus-fast instead of this command to
|
||||||
|
# avoid the vCPU interruption.
|
||||||
|
#
|
||||||
# Returns: a list of @CpuInfo for each virtual CPU
|
# Returns: a list of @CpuInfo for each virtual CPU
|
||||||
#
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
@ -555,9 +587,88 @@
|
|||||||
# ]
|
# ]
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
|
# Notes: This interface is deprecated (since 2.12.0), and it is strongly
|
||||||
|
# recommended that you avoid using it. Use @query-cpus-fast to
|
||||||
|
# obtain information about virtual CPUs.
|
||||||
|
#
|
||||||
##
|
##
|
||||||
{ 'command': 'query-cpus', 'returns': ['CpuInfo'] }
|
{ 'command': 'query-cpus', 'returns': ['CpuInfo'] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @CpuInfoFast:
|
||||||
|
#
|
||||||
|
# Information about a virtual CPU
|
||||||
|
#
|
||||||
|
# @cpu-index: index of the virtual CPU
|
||||||
|
#
|
||||||
|
# @qom-path: path to the CPU object in the QOM tree
|
||||||
|
#
|
||||||
|
# @thread-id: ID of the underlying host thread
|
||||||
|
#
|
||||||
|
# @props: properties describing to which node/socket/core/thread
|
||||||
|
# virtual CPU belongs to, provided if supported by board
|
||||||
|
#
|
||||||
|
# @arch: architecture of the cpu, which determines which additional fields
|
||||||
|
# will be listed
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'union': 'CpuInfoFast',
|
||||||
|
'base': {'cpu-index': 'int', 'qom-path': 'str',
|
||||||
|
'thread-id': 'int', '*props': 'CpuInstanceProperties',
|
||||||
|
'arch': 'CpuInfoArch' },
|
||||||
|
'discriminator': 'arch',
|
||||||
|
'data': { 'x86': 'CpuInfoOther',
|
||||||
|
'sparc': 'CpuInfoOther',
|
||||||
|
'ppc': 'CpuInfoOther',
|
||||||
|
'mips': 'CpuInfoOther',
|
||||||
|
'tricore': 'CpuInfoOther',
|
||||||
|
's390': 'CpuInfoS390',
|
||||||
|
'other': 'CpuInfoOther' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @query-cpus-fast:
|
||||||
|
#
|
||||||
|
# Returns information about all virtual CPUs. This command does not
|
||||||
|
# incur a performance penalty and should be used in production
|
||||||
|
# instead of query-cpus.
|
||||||
|
#
|
||||||
|
# Returns: list of @CpuInfoFast
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# -> { "execute": "query-cpus-fast" }
|
||||||
|
# <- { "return": [
|
||||||
|
# {
|
||||||
|
# "thread-id": 25627,
|
||||||
|
# "props": {
|
||||||
|
# "core-id": 0,
|
||||||
|
# "thread-id": 0,
|
||||||
|
# "socket-id": 0
|
||||||
|
# },
|
||||||
|
# "qom-path": "/machine/unattached/device[0]",
|
||||||
|
# "arch":"x86",
|
||||||
|
# "cpu-index": 0
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "thread-id": 25628,
|
||||||
|
# "props": {
|
||||||
|
# "core-id": 0,
|
||||||
|
# "thread-id": 0,
|
||||||
|
# "socket-id": 1
|
||||||
|
# },
|
||||||
|
# "qom-path": "/machine/unattached/device[2]",
|
||||||
|
# "arch":"x86",
|
||||||
|
# "cpu-index": 1
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
##
|
||||||
|
{ 'command': 'query-cpus-fast', 'returns': [ 'CpuInfoFast' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @IOThreadInfo:
|
# @IOThreadInfo:
|
||||||
#
|
#
|
||||||
|
@ -320,22 +320,29 @@
|
|||||||
#
|
#
|
||||||
# An enumeration of the guest panic information types
|
# An enumeration of the guest panic information types
|
||||||
#
|
#
|
||||||
|
# @hyper-v: hyper-v guest panic information type
|
||||||
|
#
|
||||||
|
# @s390: s390 guest panic information type (Since: 2.12)
|
||||||
|
#
|
||||||
# Since: 2.9
|
# Since: 2.9
|
||||||
##
|
##
|
||||||
{ 'enum': 'GuestPanicInformationType',
|
{ 'enum': 'GuestPanicInformationType',
|
||||||
'data': [ 'hyper-v'] }
|
'data': [ 'hyper-v', 's390' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @GuestPanicInformation:
|
# @GuestPanicInformation:
|
||||||
#
|
#
|
||||||
# Information about a guest panic
|
# Information about a guest panic
|
||||||
#
|
#
|
||||||
|
# @type: Crash type that defines the hypervisor specific information
|
||||||
|
#
|
||||||
# Since: 2.9
|
# Since: 2.9
|
||||||
##
|
##
|
||||||
{'union': 'GuestPanicInformation',
|
{'union': 'GuestPanicInformation',
|
||||||
'base': {'type': 'GuestPanicInformationType'},
|
'base': {'type': 'GuestPanicInformationType'},
|
||||||
'discriminator': 'type',
|
'discriminator': 'type',
|
||||||
'data': { 'hyper-v': 'GuestPanicInformationHyperV' } }
|
'data': { 'hyper-v': 'GuestPanicInformationHyperV',
|
||||||
|
's390': 'GuestPanicInformationS390' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @GuestPanicInformationHyperV:
|
# @GuestPanicInformationHyperV:
|
||||||
@ -350,3 +357,47 @@
|
|||||||
'arg3': 'uint64',
|
'arg3': 'uint64',
|
||||||
'arg4': 'uint64',
|
'arg4': 'uint64',
|
||||||
'arg5': 'uint64' } }
|
'arg5': 'uint64' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @S390CrashReason:
|
||||||
|
#
|
||||||
|
# Reason why the CPU is in a crashed state.
|
||||||
|
#
|
||||||
|
# @unknown: no crash reason was set
|
||||||
|
#
|
||||||
|
# @disabled-wait: the CPU has entered a disabled wait state
|
||||||
|
#
|
||||||
|
# @extint-loop: clock comparator or cpu timer interrupt with new PSW enabled
|
||||||
|
# for external interrupts
|
||||||
|
#
|
||||||
|
# @pgmint-loop: program interrupt with BAD new PSW
|
||||||
|
#
|
||||||
|
# @opint-loop: operation exception interrupt with invalid code at the program
|
||||||
|
# interrupt new PSW
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'enum': 'S390CrashReason',
|
||||||
|
'data': [ 'unknown',
|
||||||
|
'disabled-wait',
|
||||||
|
'extint-loop',
|
||||||
|
'pgmint-loop',
|
||||||
|
'opint-loop' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @GuestPanicInformationS390:
|
||||||
|
#
|
||||||
|
# S390 specific guest panic information (PSW)
|
||||||
|
#
|
||||||
|
# @core: core id of the CPU that crashed
|
||||||
|
# @psw-mask: control fields of guest PSW
|
||||||
|
# @psw-addr: guest instruction address
|
||||||
|
# @reason: guest crash reason
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{'struct': 'GuestPanicInformationS390',
|
||||||
|
'data': { 'core': 'uint32',
|
||||||
|
'psw-mask': 'uint64',
|
||||||
|
'psw-addr': 'uint64',
|
||||||
|
'reason': 'S390CrashReason' } }
|
||||||
|
@ -2762,6 +2762,10 @@ by the ``convert -l snapshot_param'' argument instead.
|
|||||||
"autoload" parameter is now ignored. All bitmaps are automatically loaded
|
"autoload" parameter is now ignored. All bitmaps are automatically loaded
|
||||||
from qcow2 images.
|
from qcow2 images.
|
||||||
|
|
||||||
|
@subsection query-cpus (since 2.12.0)
|
||||||
|
|
||||||
|
The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command.
|
||||||
|
|
||||||
@section System emulator human monitor commands
|
@section System emulator human monitor commands
|
||||||
|
|
||||||
@subsection host_net_add (since 2.10.0)
|
@subsection host_net_add (since 2.10.0)
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
#include "qapi-visit.h"
|
||||||
|
#include "sysemu/hw_accel.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
@ -59,8 +61,8 @@ static bool s390_cpu_has_work(CPUState *cs)
|
|||||||
S390CPU *cpu = S390_CPU(cs);
|
S390CPU *cpu = S390_CPU(cs);
|
||||||
|
|
||||||
/* STOPPED cpus can never wake up */
|
/* STOPPED cpus can never wake up */
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_LOAD &&
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD &&
|
||||||
s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
|
s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ static void s390_cpu_load_normal(CPUState *s)
|
|||||||
S390CPU *cpu = S390_CPU(s);
|
S390CPU *cpu = S390_CPU(s);
|
||||||
cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR;
|
cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR;
|
||||||
cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64;
|
cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64;
|
||||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ static void s390_cpu_reset(CPUState *s)
|
|||||||
env->bpbc = false;
|
env->bpbc = false;
|
||||||
scc->parent_reset(s);
|
scc->parent_reset(s);
|
||||||
cpu->env.sigp_order = 0;
|
cpu->env.sigp_order = 0;
|
||||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* S390CPUClass::initial_reset() */
|
/* S390CPUClass::initial_reset() */
|
||||||
@ -136,7 +138,7 @@ static void s390_cpu_full_reset(CPUState *s)
|
|||||||
|
|
||||||
scc->parent_reset(s);
|
scc->parent_reset(s);
|
||||||
cpu->env.sigp_order = 0;
|
cpu->env.sigp_order = 0;
|
||||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
|
||||||
|
|
||||||
memset(env, 0, offsetof(CPUS390XState, end_reset_fields));
|
memset(env, 0, offsetof(CPUS390XState, end_reset_fields));
|
||||||
|
|
||||||
@ -228,6 +230,46 @@ out:
|
|||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs)
|
||||||
|
{
|
||||||
|
GuestPanicInformation *panic_info;
|
||||||
|
S390CPU *cpu = S390_CPU(cs);
|
||||||
|
|
||||||
|
cpu_synchronize_state(cs);
|
||||||
|
panic_info = g_malloc0(sizeof(GuestPanicInformation));
|
||||||
|
|
||||||
|
panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390;
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
panic_info->u.s390.core = cpu->env.core_id;
|
||||||
|
#else
|
||||||
|
panic_info->u.s390.core = 0; /* sane default for non system emulation */
|
||||||
|
#endif
|
||||||
|
panic_info->u.s390.psw_mask = cpu->env.psw.mask;
|
||||||
|
panic_info->u.s390.psw_addr = cpu->env.psw.addr;
|
||||||
|
panic_info->u.s390.reason = cpu->env.crash_reason;
|
||||||
|
|
||||||
|
return panic_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v,
|
||||||
|
const char *name, void *opaque,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
CPUState *cs = CPU(obj);
|
||||||
|
GuestPanicInformation *panic_info;
|
||||||
|
|
||||||
|
if (!cs->crash_occurred) {
|
||||||
|
error_setg(errp, "No crash occurred");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic_info = s390_cpu_get_crash_info(cs);
|
||||||
|
|
||||||
|
visit_type_GuestPanicInformation(v, "crash-information", &panic_info,
|
||||||
|
errp);
|
||||||
|
qapi_free_GuestPanicInformation(panic_info);
|
||||||
|
}
|
||||||
|
|
||||||
static void s390_cpu_initfn(Object *obj)
|
static void s390_cpu_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(obj);
|
CPUState *cs = CPU(obj);
|
||||||
@ -240,6 +282,8 @@ static void s390_cpu_initfn(Object *obj)
|
|||||||
cs->env_ptr = env;
|
cs->env_ptr = env;
|
||||||
cs->halted = 1;
|
cs->halted = 1;
|
||||||
cs->exception_index = EXCP_HLT;
|
cs->exception_index = EXCP_HLT;
|
||||||
|
object_property_add(obj, "crash-information", "GuestPanicInformation",
|
||||||
|
s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL);
|
||||||
s390_cpu_model_register_props(obj);
|
s390_cpu_model_register_props(obj);
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
qemu_get_timedate(&tm, 0);
|
qemu_get_timedate(&tm, 0);
|
||||||
@ -248,7 +292,7 @@ static void s390_cpu_initfn(Object *obj)
|
|||||||
env->tod_basetime = 0;
|
env->tod_basetime = 0;
|
||||||
env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu);
|
env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu);
|
||||||
env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu);
|
env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu);
|
||||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +320,8 @@ static unsigned s390_count_running_cpus(void)
|
|||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
uint8_t state = S390_CPU(cpu)->env.cpu_state;
|
uint8_t state = S390_CPU(cpu)->env.cpu_state;
|
||||||
if (state == CPU_STATE_OPERATING ||
|
if (state == S390_CPU_STATE_OPERATING ||
|
||||||
state == CPU_STATE_LOAD) {
|
state == S390_CPU_STATE_LOAD) {
|
||||||
if (!disabled_wait(cpu)) {
|
if (!disabled_wait(cpu)) {
|
||||||
nr_running++;
|
nr_running++;
|
||||||
}
|
}
|
||||||
@ -316,13 +360,13 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
|
|||||||
trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state);
|
trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state);
|
||||||
|
|
||||||
switch (cpu_state) {
|
switch (cpu_state) {
|
||||||
case CPU_STATE_STOPPED:
|
case S390_CPU_STATE_STOPPED:
|
||||||
case CPU_STATE_CHECK_STOP:
|
case S390_CPU_STATE_CHECK_STOP:
|
||||||
/* halt the cpu for common infrastructure */
|
/* halt the cpu for common infrastructure */
|
||||||
s390_cpu_halt(cpu);
|
s390_cpu_halt(cpu);
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_OPERATING:
|
case S390_CPU_STATE_OPERATING:
|
||||||
case CPU_STATE_LOAD:
|
case S390_CPU_STATE_LOAD:
|
||||||
/*
|
/*
|
||||||
* Starting a CPU with a PSW WAIT bit set:
|
* Starting a CPU with a PSW WAIT bit set:
|
||||||
* KVM: handles this internally and triggers another WAIT exit.
|
* KVM: handles this internally and triggers another WAIT exit.
|
||||||
@ -393,15 +437,6 @@ void s390_cmma_reset(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int s390_get_memslot_count(void)
|
|
||||||
{
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
return kvm_s390_get_memslot_count();
|
|
||||||
} else {
|
|
||||||
return MAX_AVAIL_SLOTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id,
|
int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id,
|
||||||
int vq, bool assign)
|
int vq, bool assign)
|
||||||
{
|
{
|
||||||
@ -473,6 +508,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
|
|||||||
cc->do_interrupt = s390_cpu_do_interrupt;
|
cc->do_interrupt = s390_cpu_do_interrupt;
|
||||||
#endif
|
#endif
|
||||||
cc->dump_state = s390_cpu_dump_state;
|
cc->dump_state = s390_cpu_dump_state;
|
||||||
|
cc->get_crash_info = s390_cpu_get_crash_info;
|
||||||
cc->set_pc = s390_cpu_set_pc;
|
cc->set_pc = s390_cpu_set_pc;
|
||||||
cc->gdb_read_register = s390_cpu_gdb_read_register;
|
cc->gdb_read_register = s390_cpu_gdb_read_register;
|
||||||
cc->gdb_write_register = s390_cpu_gdb_write_register;
|
cc->gdb_write_register = s390_cpu_gdb_write_register;
|
||||||
|
@ -83,6 +83,8 @@ struct CPUS390XState {
|
|||||||
|
|
||||||
PSW psw;
|
PSW psw;
|
||||||
|
|
||||||
|
S390CrashReason crash_reason;
|
||||||
|
|
||||||
uint64_t cc_src;
|
uint64_t cc_src;
|
||||||
uint64_t cc_dst;
|
uint64_t cc_dst;
|
||||||
uint64_t cc_vr;
|
uint64_t cc_vr;
|
||||||
@ -139,12 +141,9 @@ struct CPUS390XState {
|
|||||||
* architectures, there is a difference between a halt and a stop on s390.
|
* architectures, there is a difference between a halt and a stop on s390.
|
||||||
* If all cpus are either stopped (including check stop) or in the disabled
|
* If all cpus are either stopped (including check stop) or in the disabled
|
||||||
* wait state, the vm can be shut down.
|
* wait state, the vm can be shut down.
|
||||||
|
* The acceptable cpu_state values are defined in the CpuInfoS390State
|
||||||
|
* enum.
|
||||||
*/
|
*/
|
||||||
#define CPU_STATE_UNINITIALIZED 0x00
|
|
||||||
#define CPU_STATE_STOPPED 0x01
|
|
||||||
#define CPU_STATE_CHECK_STOP 0x02
|
|
||||||
#define CPU_STATE_OPERATING 0x03
|
|
||||||
#define CPU_STATE_LOAD 0x04
|
|
||||||
uint8_t cpu_state;
|
uint8_t cpu_state;
|
||||||
|
|
||||||
/* currently processed sigp order */
|
/* currently processed sigp order */
|
||||||
@ -310,11 +309,12 @@ extern const struct VMStateDescription vmstate_s390_cpu;
|
|||||||
|
|
||||||
#define FLAG_MASK_PSW_SHIFT 31
|
#define FLAG_MASK_PSW_SHIFT 31
|
||||||
#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT)
|
||||||
|
#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT)
|
||||||
#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT)
|
||||||
#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT)
|
||||||
#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT)
|
||||||
#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT)
|
#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT)
|
||||||
#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_PSTATE \
|
#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \
|
||||||
| FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32)
|
| FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32)
|
||||||
|
|
||||||
/* Control register 0 bits */
|
/* Control register 0 bits */
|
||||||
@ -338,6 +338,10 @@ extern const struct VMStateDescription vmstate_s390_cpu;
|
|||||||
|
|
||||||
static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch)
|
static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch)
|
||||||
{
|
{
|
||||||
|
if (!(env->psw.mask & PSW_MASK_DAT)) {
|
||||||
|
return MMU_REAL_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
switch (env->psw.mask & PSW_MASK_ASC) {
|
switch (env->psw.mask & PSW_MASK_ASC) {
|
||||||
case PSW_ASC_PRIMARY:
|
case PSW_ASC_PRIMARY:
|
||||||
return MMU_PRIMARY_IDX;
|
return MMU_PRIMARY_IDX;
|
||||||
@ -617,10 +621,6 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096);
|
|||||||
/* SIGP order code mask corresponding to bit positions 56-63 */
|
/* SIGP order code mask corresponding to bit positions 56-63 */
|
||||||
#define SIGP_ORDER_MASK 0x000000ff
|
#define SIGP_ORDER_MASK 0x000000ff
|
||||||
|
|
||||||
/* from s390-virtio-ccw */
|
|
||||||
#define MEM_SECTION_SIZE 0x10000000UL
|
|
||||||
#define MAX_AVAIL_SLOTS 32
|
|
||||||
|
|
||||||
/* machine check interruption code */
|
/* machine check interruption code */
|
||||||
|
|
||||||
/* subclasses */
|
/* subclasses */
|
||||||
@ -692,7 +692,6 @@ int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low);
|
|||||||
int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low);
|
int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low);
|
||||||
void s390_crypto_reset(void);
|
void s390_crypto_reset(void);
|
||||||
bool s390_get_squash_mcss(void);
|
bool s390_get_squash_mcss(void);
|
||||||
int s390_get_memslot_count(void);
|
|
||||||
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit);
|
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit);
|
||||||
void s390_cmma_reset(void);
|
void s390_cmma_reset(void);
|
||||||
void s390_enable_css_support(S390CPU *cpu);
|
void s390_enable_css_support(S390CPU *cpu);
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
.desc = _desc, \
|
.desc = _desc, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* S390FeatDef.bit is not applicable as there is no feature block. */
|
||||||
|
#define FEAT_INIT_MISC(_name, _desc) \
|
||||||
|
FEAT_INIT(_name, S390_FEAT_TYPE_MISC, 0, _desc)
|
||||||
|
|
||||||
/* indexed by feature number for easy lookup */
|
/* indexed by feature number for easy lookup */
|
||||||
static const S390FeatDef s390_features[] = {
|
static const S390FeatDef s390_features[] = {
|
||||||
FEAT_INIT("esan3", S390_FEAT_TYPE_STFL, 0, "Instructions marked as n3"),
|
FEAT_INIT("esan3", S390_FEAT_TYPE_STFL, 0, "Instructions marked as n3"),
|
||||||
@ -123,8 +127,8 @@ static const S390FeatDef s390_features[] = {
|
|||||||
FEAT_INIT("ib", S390_FEAT_TYPE_SCLP_CPU, 42, "SIE: Intervention bypass facility"),
|
FEAT_INIT("ib", S390_FEAT_TYPE_SCLP_CPU, 42, "SIE: Intervention bypass facility"),
|
||||||
FEAT_INIT("cei", S390_FEAT_TYPE_SCLP_CPU, 43, "SIE: Conditional-external-interception facility"),
|
FEAT_INIT("cei", S390_FEAT_TYPE_SCLP_CPU, 43, "SIE: Conditional-external-interception facility"),
|
||||||
|
|
||||||
FEAT_INIT("dateh2", S390_FEAT_TYPE_MISC, 0, "DAT-enhancement facility 2"),
|
FEAT_INIT_MISC("dateh2", "DAT-enhancement facility 2"),
|
||||||
FEAT_INIT("cmm", S390_FEAT_TYPE_MISC, 0, "Collaborative-memory-management facility"),
|
FEAT_INIT_MISC("cmm", "Collaborative-memory-management facility"),
|
||||||
|
|
||||||
FEAT_INIT("plo-cl", S390_FEAT_TYPE_PLO, 0, "PLO Compare and load (32 bit in general registers)"),
|
FEAT_INIT("plo-cl", S390_FEAT_TYPE_PLO, 0, "PLO Compare and load (32 bit in general registers)"),
|
||||||
FEAT_INIT("plo-clg", S390_FEAT_TYPE_PLO, 1, "PLO Compare and load (64 bit in parameter list)"),
|
FEAT_INIT("plo-clg", S390_FEAT_TYPE_PLO, 1, "PLO Compare and load (64 bit in parameter list)"),
|
||||||
|
@ -107,6 +107,10 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (mmu_idx == MMU_REAL_IDX) {
|
} else if (mmu_idx == MMU_REAL_IDX) {
|
||||||
|
/* 31-Bit mode */
|
||||||
|
if (!(env->psw.mask & PSW_MASK_64)) {
|
||||||
|
vaddr &= 0x7fffffff;
|
||||||
|
}
|
||||||
if (mmu_translate_real(env, vaddr, rw, &raddr, &prot)) {
|
if (mmu_translate_real(env, vaddr, rw, &raddr, &prot)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,15 @@ static inline bool is_special_wait_psw(uint64_t psw_addr)
|
|||||||
|
|
||||||
void s390_handle_wait(S390CPU *cpu)
|
void s390_handle_wait(S390CPU *cpu)
|
||||||
{
|
{
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
|
||||||
if (s390_cpu_halt(cpu) == 0) {
|
if (s390_cpu_halt(cpu) == 0) {
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
if (is_special_wait_psw(cpu->env.psw.addr)) {
|
if (is_special_wait_psw(cpu->env.psw.addr)) {
|
||||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||||
} else {
|
} else {
|
||||||
qemu_system_guest_panicked(NULL);
|
cpu->env.crash_reason = S390_CRASH_REASON_DISABLED_WAIT;
|
||||||
|
qemu_system_guest_panicked(cpu_get_crash_info(cs));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1000,13 +1000,13 @@
|
|||||||
/* ??? Not implemented - is it necessary? */
|
/* ??? Not implemented - is it necessary? */
|
||||||
C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0)
|
C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0)
|
||||||
/* SET CLOCK COMPARATOR */
|
/* SET CLOCK COMPARATOR */
|
||||||
C(0xb206, SCKC, S, Z, 0, m2_64, 0, 0, sckc, 0)
|
C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0)
|
||||||
/* SET CLOCK PROGRAMMABLE FIELD */
|
/* SET CLOCK PROGRAMMABLE FIELD */
|
||||||
C(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0)
|
C(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0)
|
||||||
/* SET CPU TIMER */
|
/* SET CPU TIMER */
|
||||||
C(0xb208, SPT, S, Z, 0, m2_64, 0, 0, spt, 0)
|
C(0xb208, SPT, S, Z, 0, m2_64a, 0, 0, spt, 0)
|
||||||
/* SET PREFIX */
|
/* SET PREFIX */
|
||||||
C(0xb210, SPX, S, Z, 0, m2_32u, 0, 0, spx, 0)
|
C(0xb210, SPX, S, Z, 0, m2_32ua, 0, 0, spx, 0)
|
||||||
/* SET PSW KEY FROM ADDRESS */
|
/* SET PSW KEY FROM ADDRESS */
|
||||||
C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0)
|
C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0)
|
||||||
/* SET STORAGE KEY EXTENDED */
|
/* SET STORAGE KEY EXTENDED */
|
||||||
@ -1021,20 +1021,20 @@
|
|||||||
/* STORE CLOCK EXTENDED */
|
/* STORE CLOCK EXTENDED */
|
||||||
C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0)
|
C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0)
|
||||||
/* STORE CLOCK COMPARATOR */
|
/* STORE CLOCK COMPARATOR */
|
||||||
C(0xb207, STCKC, S, Z, la2, 0, new, m1_64, stckc, 0)
|
C(0xb207, STCKC, S, Z, la2, 0, new, m1_64a, stckc, 0)
|
||||||
/* STORE CONTROL */
|
/* STORE CONTROL */
|
||||||
C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0)
|
C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0)
|
||||||
C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0)
|
C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0)
|
||||||
/* STORE CPU ADDRESS */
|
/* STORE CPU ADDRESS */
|
||||||
C(0xb212, STAP, S, Z, la2, 0, new, m1_16, stap, 0)
|
C(0xb212, STAP, S, Z, la2, 0, new, m1_16a, stap, 0)
|
||||||
/* STORE CPU ID */
|
/* STORE CPU ID */
|
||||||
C(0xb202, STIDP, S, Z, la2, 0, new, 0, stidp, 0)
|
C(0xb202, STIDP, S, Z, la2, 0, new, m1_64a, stidp, 0)
|
||||||
/* STORE CPU TIMER */
|
/* STORE CPU TIMER */
|
||||||
C(0xb209, STPT, S, Z, la2, 0, new, m1_64, stpt, 0)
|
C(0xb209, STPT, S, Z, la2, 0, new, m1_64a, stpt, 0)
|
||||||
/* STORE FACILITY LIST */
|
/* STORE FACILITY LIST */
|
||||||
C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0)
|
C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0)
|
||||||
/* STORE PREFIX */
|
/* STORE PREFIX */
|
||||||
C(0xb211, STPX, S, Z, la2, 0, new, m1_32, stpx, 0)
|
C(0xb211, STPX, S, Z, la2, 0, new, m1_32a, stpx, 0)
|
||||||
/* STORE SYSTEM INFORMATION */
|
/* STORE SYSTEM INFORMATION */
|
||||||
C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0)
|
C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0)
|
||||||
/* STORE THEN AND SYSTEM MASK */
|
/* STORE THEN AND SYSTEM MASK */
|
||||||
|
@ -84,11 +84,6 @@ void kvm_s390_cmma_reset(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_s390_get_memslot_count(void)
|
|
||||||
{
|
|
||||||
return MAX_AVAIL_SLOTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_s390_reset_vcpu(S390CPU *cpu)
|
void kvm_s390_reset_vcpu(S390CPU *cpu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1541,15 +1541,14 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset)
|
static void unmanageable_intercept(S390CPU *cpu, S390CrashReason reason,
|
||||||
|
int pswoffset)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
CPUState *cs = CPU(cpu);
|
||||||
|
|
||||||
error_report("Unmanageable %s! CPU%i new PSW: 0x%016lx:%016lx",
|
|
||||||
str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset),
|
|
||||||
ldq_phys(cs->as, cpu->env.psa + pswoffset + 8));
|
|
||||||
s390_cpu_halt(cpu);
|
s390_cpu_halt(cpu);
|
||||||
qemu_system_guest_panicked(NULL);
|
cpu->env.crash_reason = reason;
|
||||||
|
qemu_system_guest_panicked(cpu_get_crash_info(cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* try to detect pgm check loops */
|
/* try to detect pgm check loops */
|
||||||
@ -1579,7 +1578,7 @@ static int handle_oper_loop(S390CPU *cpu, struct kvm_run *run)
|
|||||||
!(oldpsw.mask & PSW_MASK_PSTATE) &&
|
!(oldpsw.mask & PSW_MASK_PSTATE) &&
|
||||||
(newpsw.mask & PSW_MASK_ASC) == (oldpsw.mask & PSW_MASK_ASC) &&
|
(newpsw.mask & PSW_MASK_ASC) == (oldpsw.mask & PSW_MASK_ASC) &&
|
||||||
(newpsw.mask & PSW_MASK_DAT) == (oldpsw.mask & PSW_MASK_DAT)) {
|
(newpsw.mask & PSW_MASK_DAT) == (oldpsw.mask & PSW_MASK_DAT)) {
|
||||||
unmanageable_intercept(cpu, "operation exception loop",
|
unmanageable_intercept(cpu, S390_CRASH_REASON_OPINT_LOOP,
|
||||||
offsetof(LowCore, program_new_psw));
|
offsetof(LowCore, program_new_psw));
|
||||||
return EXCP_HALTED;
|
return EXCP_HALTED;
|
||||||
}
|
}
|
||||||
@ -1600,12 +1599,12 @@ static int handle_intercept(S390CPU *cpu)
|
|||||||
r = handle_instruction(cpu, run);
|
r = handle_instruction(cpu, run);
|
||||||
break;
|
break;
|
||||||
case ICPT_PROGRAM:
|
case ICPT_PROGRAM:
|
||||||
unmanageable_intercept(cpu, "program interrupt",
|
unmanageable_intercept(cpu, S390_CRASH_REASON_PGMINT_LOOP,
|
||||||
offsetof(LowCore, program_new_psw));
|
offsetof(LowCore, program_new_psw));
|
||||||
r = EXCP_HALTED;
|
r = EXCP_HALTED;
|
||||||
break;
|
break;
|
||||||
case ICPT_EXT_INT:
|
case ICPT_EXT_INT:
|
||||||
unmanageable_intercept(cpu, "external interrupt",
|
unmanageable_intercept(cpu, S390_CRASH_REASON_EXTINT_LOOP,
|
||||||
offsetof(LowCore, external_new_psw));
|
offsetof(LowCore, external_new_psw));
|
||||||
r = EXCP_HALTED;
|
r = EXCP_HALTED;
|
||||||
break;
|
break;
|
||||||
@ -1855,11 +1854,6 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
|||||||
return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
|
return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_s390_get_memslot_count(void)
|
|
||||||
{
|
|
||||||
return kvm_check_extension(kvm_state, KVM_CAP_NR_MEMSLOTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_s390_get_ri(void)
|
int kvm_s390_get_ri(void)
|
||||||
{
|
{
|
||||||
return cap_ri;
|
return cap_ri;
|
||||||
@ -1881,16 +1875,16 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (cpu_state) {
|
switch (cpu_state) {
|
||||||
case CPU_STATE_STOPPED:
|
case S390_CPU_STATE_STOPPED:
|
||||||
mp_state.mp_state = KVM_MP_STATE_STOPPED;
|
mp_state.mp_state = KVM_MP_STATE_STOPPED;
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_CHECK_STOP:
|
case S390_CPU_STATE_CHECK_STOP:
|
||||||
mp_state.mp_state = KVM_MP_STATE_CHECK_STOP;
|
mp_state.mp_state = KVM_MP_STATE_CHECK_STOP;
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_OPERATING:
|
case S390_CPU_STATE_OPERATING:
|
||||||
mp_state.mp_state = KVM_MP_STATE_OPERATING;
|
mp_state.mp_state = KVM_MP_STATE_OPERATING;
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_LOAD:
|
case S390_CPU_STATE_LOAD:
|
||||||
mp_state.mp_state = KVM_MP_STATE_LOAD;
|
mp_state.mp_state = KVM_MP_STATE_LOAD;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -30,7 +30,6 @@ int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_clock);
|
|||||||
void kvm_s390_enable_css_support(S390CPU *cpu);
|
void kvm_s390_enable_css_support(S390CPU *cpu);
|
||||||
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
|
||||||
int vq, bool assign);
|
int vq, bool assign);
|
||||||
int kvm_s390_get_memslot_count(void);
|
|
||||||
int kvm_s390_cmma_active(void);
|
int kvm_s390_cmma_active(void);
|
||||||
void kvm_s390_cmma_reset(void);
|
void kvm_s390_cmma_reset(void);
|
||||||
void kvm_s390_reset_vcpu(S390CPU *cpu);
|
void kvm_s390_reset_vcpu(S390CPU *cpu);
|
||||||
|
@ -693,6 +693,11 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (a2 & 0x3) {
|
||||||
|
/* we either came here by lam or lamy, which have different lengths */
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
env->aregs[i] = cpu_ldl_data_ra(env, a2, ra);
|
env->aregs[i] = cpu_ldl_data_ra(env, a2, ra);
|
||||||
a2 += 4;
|
a2 += 4;
|
||||||
@ -709,6 +714,10 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uintptr_t ra = GETPC();
|
uintptr_t ra = GETPC();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (a2 & 0x3) {
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
cpu_stl_data_ra(env, a2, env->aregs[i], ra);
|
cpu_stl_data_ra(env, a2, env->aregs[i], ra);
|
||||||
a2 += 4;
|
a2 += 4;
|
||||||
@ -1620,6 +1629,10 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uint64_t src = a2;
|
uint64_t src = a2;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (src & 0x7) {
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
uint64_t val = cpu_ldq_data_ra(env, src, ra);
|
uint64_t val = cpu_ldq_data_ra(env, src, ra);
|
||||||
if (env->cregs[i] != val && i >= 9 && i <= 11) {
|
if (env->cregs[i] != val && i >= 9 && i <= 11) {
|
||||||
@ -1650,6 +1663,10 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uint64_t src = a2;
|
uint64_t src = a2;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (src & 0x3) {
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
uint32_t val = cpu_ldl_data_ra(env, src, ra);
|
uint32_t val = cpu_ldl_data_ra(env, src, ra);
|
||||||
if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
|
if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
|
||||||
@ -1677,6 +1694,10 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uint64_t dest = a2;
|
uint64_t dest = a2;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (dest & 0x7) {
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
cpu_stq_data_ra(env, dest, env->cregs[i], ra);
|
cpu_stq_data_ra(env, dest, env->cregs[i], ra);
|
||||||
dest += sizeof(uint64_t);
|
dest += sizeof(uint64_t);
|
||||||
@ -1693,6 +1714,10 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
|
|||||||
uint64_t dest = a2;
|
uint64_t dest = a2;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
if (dest & 0x3) {
|
||||||
|
s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = r1;; i = (i + 1) % 16) {
|
for (i = r1;; i = (i + 1) % 16) {
|
||||||
cpu_stl_data_ra(env, dest, env->cregs[i], ra);
|
cpu_stl_data_ra(env, dest, env->cregs[i], ra);
|
||||||
dest += sizeof(uint32_t);
|
dest += sizeof(uint32_t);
|
||||||
|
@ -544,7 +544,7 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
|
|||||||
{
|
{
|
||||||
const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT;
|
const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT;
|
||||||
|
|
||||||
*flags = PAGE_READ | PAGE_WRITE;
|
*flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||||
if (is_low_address(raddr & TARGET_PAGE_MASK) && lowprot_enabled) {
|
if (is_low_address(raddr & TARGET_PAGE_MASK) && lowprot_enabled) {
|
||||||
/* see comment in mmu_translate() how this works */
|
/* see comment in mmu_translate() how this works */
|
||||||
*flags |= PAGE_WRITE_INV;
|
*flags |= PAGE_WRITE_INV;
|
||||||
|
@ -46,13 +46,13 @@ static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* sensing without locks is racy, but it's the same for real hw */
|
/* sensing without locks is racy, but it's the same for real hw */
|
||||||
if (state != CPU_STATE_STOPPED && !ext_call) {
|
if (state != S390_CPU_STATE_STOPPED && !ext_call) {
|
||||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||||
} else {
|
} else {
|
||||||
if (ext_call) {
|
if (ext_call) {
|
||||||
status |= SIGP_STAT_EXT_CALL_PENDING;
|
status |= SIGP_STAT_EXT_CALL_PENDING;
|
||||||
}
|
}
|
||||||
if (state == CPU_STATE_STOPPED) {
|
if (state == S390_CPU_STATE_STOPPED) {
|
||||||
status |= SIGP_STAT_STOPPED;
|
status |= SIGP_STAT_STOPPED;
|
||||||
}
|
}
|
||||||
set_sigp_status(si, status);
|
set_sigp_status(si, status);
|
||||||
@ -94,12 +94,12 @@ static void sigp_start(CPUState *cs, run_on_cpu_data arg)
|
|||||||
S390CPU *cpu = S390_CPU(cs);
|
S390CPU *cpu = S390_CPU(cs);
|
||||||
SigpInfo *si = arg.host_ptr;
|
SigpInfo *si = arg.host_ptr;
|
||||||
|
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
|
||||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
|
||||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,14 +108,14 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
|
|||||||
S390CPU *cpu = S390_CPU(cs);
|
S390CPU *cpu = S390_CPU(cs);
|
||||||
SigpInfo *si = arg.host_ptr;
|
SigpInfo *si = arg.host_ptr;
|
||||||
|
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
|
||||||
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disabled wait - sleeping in user space */
|
/* disabled wait - sleeping in user space */
|
||||||
if (cs->halted) {
|
if (cs->halted) {
|
||||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
|
||||||
} else {
|
} else {
|
||||||
/* execute the stop function */
|
/* execute the stop function */
|
||||||
cpu->env.sigp_order = SIGP_STOP;
|
cpu->env.sigp_order = SIGP_STOP;
|
||||||
@ -130,17 +130,17 @@ static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
|
|||||||
SigpInfo *si = arg.host_ptr;
|
SigpInfo *si = arg.host_ptr;
|
||||||
|
|
||||||
/* disabled wait - sleeping in user space */
|
/* disabled wait - sleeping in user space */
|
||||||
if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
|
if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) {
|
||||||
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (s390_cpu_get_state(cpu)) {
|
switch (s390_cpu_get_state(cpu)) {
|
||||||
case CPU_STATE_OPERATING:
|
case S390_CPU_STATE_OPERATING:
|
||||||
cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
|
cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
|
||||||
cpu_inject_stop(cpu);
|
cpu_inject_stop(cpu);
|
||||||
/* store will be performed in do_stop_interrup() */
|
/* store will be performed in do_stop_interrup() */
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_STOPPED:
|
case S390_CPU_STATE_STOPPED:
|
||||||
/* already stopped, just store the status */
|
/* already stopped, just store the status */
|
||||||
cpu_synchronize_state(cs);
|
cpu_synchronize_state(cs);
|
||||||
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
|
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
|
||||||
@ -156,7 +156,7 @@ static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
|
|||||||
uint32_t address = si->param & 0x7ffffe00u;
|
uint32_t address = si->param & 0x7ffffe00u;
|
||||||
|
|
||||||
/* cpu has to be stopped */
|
/* cpu has to be stopped */
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
|
||||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* cpu has to be stopped */
|
/* cpu has to be stopped */
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
|
||||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -229,17 +229,17 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
|
|||||||
SigpInfo *si = arg.host_ptr;
|
SigpInfo *si = arg.host_ptr;
|
||||||
|
|
||||||
switch (s390_cpu_get_state(cpu)) {
|
switch (s390_cpu_get_state(cpu)) {
|
||||||
case CPU_STATE_STOPPED:
|
case S390_CPU_STATE_STOPPED:
|
||||||
/* the restart irq has to be delivered prior to any other pending irq */
|
/* the restart irq has to be delivered prior to any other pending irq */
|
||||||
cpu_synchronize_state(cs);
|
cpu_synchronize_state(cs);
|
||||||
/*
|
/*
|
||||||
* Set OPERATING (and unhalting) before loading the restart PSW.
|
* Set OPERATING (and unhalting) before loading the restart PSW.
|
||||||
* load_psw() will then properly halt the CPU again if necessary (TCG).
|
* load_psw() will then properly halt the CPU again if necessary (TCG).
|
||||||
*/
|
*/
|
||||||
s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
|
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
|
||||||
do_restart_interrupt(&cpu->env);
|
do_restart_interrupt(&cpu->env);
|
||||||
break;
|
break;
|
||||||
case CPU_STATE_OPERATING:
|
case S390_CPU_STATE_OPERATING:
|
||||||
cpu_inject_restart(cpu);
|
cpu_inject_restart(cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* cpu has to be stopped */
|
/* cpu has to be stopped */
|
||||||
if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
|
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
|
||||||
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -318,7 +318,7 @@ static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
|
|||||||
p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */
|
p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */
|
||||||
s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */
|
s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */
|
||||||
|
|
||||||
if (s390_cpu_get_state(dst_cpu) != CPU_STATE_STOPPED ||
|
if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED ||
|
||||||
(psw_mask & psw_int_mask) != psw_int_mask ||
|
(psw_mask & psw_int_mask) != psw_int_mask ||
|
||||||
(idle && psw_addr != 0) ||
|
(idle && psw_addr != 0) ||
|
||||||
(!idle && (asn == p_asn || asn == s_asn))) {
|
(!idle && (asn == p_asn || asn == s_asn))) {
|
||||||
@ -435,7 +435,7 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
|
|||||||
if (cur_cpu == cpu) {
|
if (cur_cpu == cpu) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) {
|
if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) {
|
||||||
all_stopped = false;
|
all_stopped = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -492,7 +492,7 @@ void do_stop_interrupt(CPUS390XState *env)
|
|||||||
{
|
{
|
||||||
S390CPU *cpu = s390_env_get_cpu(env);
|
S390CPU *cpu = s390_env_get_cpu(env);
|
||||||
|
|
||||||
if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) {
|
if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
|
||||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||||
}
|
}
|
||||||
if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
|
if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
|
||||||
|
@ -252,13 +252,17 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc)
|
|||||||
|
|
||||||
static int get_mem_index(DisasContext *s)
|
static int get_mem_index(DisasContext *s)
|
||||||
{
|
{
|
||||||
|
if (!(s->tb->flags & FLAG_MASK_DAT)) {
|
||||||
|
return MMU_REAL_IDX;
|
||||||
|
}
|
||||||
|
|
||||||
switch (s->tb->flags & FLAG_MASK_ASC) {
|
switch (s->tb->flags & FLAG_MASK_ASC) {
|
||||||
case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT:
|
case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT:
|
||||||
return 0;
|
return MMU_PRIMARY_IDX;
|
||||||
case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT:
|
case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT:
|
||||||
return 1;
|
return MMU_SECONDARY_IDX;
|
||||||
case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT:
|
case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT:
|
||||||
return 2;
|
return MMU_HOME_IDX;
|
||||||
default:
|
default:
|
||||||
tcg_abort();
|
tcg_abort();
|
||||||
break;
|
break;
|
||||||
@ -4058,7 +4062,6 @@ static ExitStatus op_stidp(DisasContext *s, DisasOps *o)
|
|||||||
{
|
{
|
||||||
check_privileged(s);
|
check_privileged(s);
|
||||||
tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid));
|
tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid));
|
||||||
tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN);
|
|
||||||
return NO_EXIT;
|
return NO_EXIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5216,18 +5219,42 @@ static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o)
|
|||||||
}
|
}
|
||||||
#define SPEC_wout_m1_16 0
|
#define SPEC_wout_m1_16 0
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void wout_m1_16a(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
|
{
|
||||||
|
tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUW | MO_ALIGN);
|
||||||
|
}
|
||||||
|
#define SPEC_wout_m1_16a 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o)
|
static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
{
|
{
|
||||||
tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s));
|
tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s));
|
||||||
}
|
}
|
||||||
#define SPEC_wout_m1_32 0
|
#define SPEC_wout_m1_32 0
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void wout_m1_32a(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
|
{
|
||||||
|
tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUL | MO_ALIGN);
|
||||||
|
}
|
||||||
|
#define SPEC_wout_m1_32a 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o)
|
static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
{
|
{
|
||||||
tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s));
|
tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s));
|
||||||
}
|
}
|
||||||
#define SPEC_wout_m1_64 0
|
#define SPEC_wout_m1_64 0
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void wout_m1_64a(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
|
{
|
||||||
|
tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN);
|
||||||
|
}
|
||||||
|
#define SPEC_wout_m1_64a 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o)
|
static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
{
|
{
|
||||||
tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s));
|
tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s));
|
||||||
@ -5653,6 +5680,15 @@ static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
|
|||||||
}
|
}
|
||||||
#define SPEC_in2_m2_32u 0
|
#define SPEC_in2_m2_32u 0
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void in2_m2_32ua(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
|
{
|
||||||
|
in2_a2(s, f, o);
|
||||||
|
tcg_gen_qemu_ld_tl(o->in2, o->in2, get_mem_index(s), MO_TEUL | MO_ALIGN);
|
||||||
|
}
|
||||||
|
#define SPEC_in2_m2_32ua 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o)
|
static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
{
|
{
|
||||||
in2_a2(s, f, o);
|
in2_a2(s, f, o);
|
||||||
@ -5660,6 +5696,15 @@ static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o)
|
|||||||
}
|
}
|
||||||
#define SPEC_in2_m2_64 0
|
#define SPEC_in2_m2_64 0
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static void in2_m2_64a(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
|
{
|
||||||
|
in2_a2(s, f, o);
|
||||||
|
tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEQ | MO_ALIGN);
|
||||||
|
}
|
||||||
|
#define SPEC_in2_m2_64a 0
|
||||||
|
#endif
|
||||||
|
|
||||||
static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o)
|
static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o)
|
||||||
{
|
{
|
||||||
in2_ri2(s, f, o);
|
in2_ri2(s, f, o);
|
||||||
|
11
vl.c
11
vl.c
@ -1736,7 +1736,7 @@ void qemu_system_reset(ShutdownCause reason)
|
|||||||
|
|
||||||
void qemu_system_guest_panicked(GuestPanicInformation *info)
|
void qemu_system_guest_panicked(GuestPanicInformation *info)
|
||||||
{
|
{
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed\n");
|
qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed");
|
||||||
|
|
||||||
if (current_cpu) {
|
if (current_cpu) {
|
||||||
current_cpu->crash_occurred = true;
|
current_cpu->crash_occurred = true;
|
||||||
@ -1752,13 +1752,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
|
|||||||
|
|
||||||
if (info) {
|
if (info) {
|
||||||
if (info->type == GUEST_PANIC_INFORMATION_TYPE_HYPER_V) {
|
if (info->type == GUEST_PANIC_INFORMATION_TYPE_HYPER_V) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "HV crash parameters: (%#"PRIx64
|
qemu_log_mask(LOG_GUEST_ERROR, "\nHV crash parameters: (%#"PRIx64
|
||||||
" %#"PRIx64" %#"PRIx64" %#"PRIx64" %#"PRIx64")\n",
|
" %#"PRIx64" %#"PRIx64" %#"PRIx64" %#"PRIx64")\n",
|
||||||
info->u.hyper_v.arg1,
|
info->u.hyper_v.arg1,
|
||||||
info->u.hyper_v.arg2,
|
info->u.hyper_v.arg2,
|
||||||
info->u.hyper_v.arg3,
|
info->u.hyper_v.arg3,
|
||||||
info->u.hyper_v.arg4,
|
info->u.hyper_v.arg4,
|
||||||
info->u.hyper_v.arg5);
|
info->u.hyper_v.arg5);
|
||||||
|
} else if (info->type == GUEST_PANIC_INFORMATION_TYPE_S390) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, " on cpu %d: %s\n"
|
||||||
|
"PSW: 0x%016" PRIx64 " 0x%016" PRIx64"\n",
|
||||||
|
info->u.s390.core,
|
||||||
|
S390CrashReason_str(info->u.s390.reason),
|
||||||
|
info->u.s390.psw_mask,
|
||||||
|
info->u.s390.psw_addr);
|
||||||
}
|
}
|
||||||
qapi_free_GuestPanicInformation(info);
|
qapi_free_GuestPanicInformation(info);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user