qemu/target/s390x/diag.c
Janosch Frank c3347ed0d2 s390x: protvirt: Support unpack facility
The unpack facility provides the means to setup a protected guest. A
protected guest cannot be introspected by the hypervisor or any
user/administrator of the machine it is running on.

Protected guests are encrypted at rest and need a special boot
mechanism via diag308 subcode 8 and 10.

Code 8 sets the PV specific IPLB which is retained separately from
those set via code 5.

Code 10 is used to unpack the VM into protected memory, verify its
integrity and start it.

Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
Co-developed-by: Christian Borntraeger <borntraeger@de.ibm.com> [Changes
to machine]
Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Message-Id: <20200323083606.24520-1-frankja@linux.ibm.com>
[CH: fixed up KVM_PV_VM_ -> KVM_PV_]
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
2020-04-29 14:30:54 +02:00

171 lines
5.0 KiB
C

/*
* S390x DIAG instruction helper functions
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
#include "exec/address-spaces.h"
#include "hw/watchdog/wdt_diag288.h"
#include "sysemu/cpus.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/pv.h"
#include "kvm_s390x.h"
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
uint64_t func = env->regs[r1];
uint64_t timeout = env->regs[r1 + 1];
uint64_t action = env->regs[r3];
Object *obj;
DIAG288State *diag288;
DIAG288Class *diag288_class;
if (r1 % 2 || action != 0) {
return -1;
}
/* Timeout must be more than 15 seconds except for timer deletion */
if (func != WDT_DIAG288_CANCEL && timeout < 15) {
return -1;
}
obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
if (!obj) {
return -1;
}
diag288 = DIAG288(obj);
diag288_class = DIAG288_GET_CLASS(diag288);
return diag288_class->handle_timer(diag288, func, timeout);
}
static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
uintptr_t ra, bool write)
{
/* Handled by the Ultravisor */
if (s390_is_pv()) {
return 0;
}
if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -1;
}
if (!address_space_access_valid(&address_space_memory, addr,
sizeof(IplParameterBlock), write,
MEMTXATTRS_UNSPECIFIED)) {
s390_program_interrupt(env, PGM_ADDRESSING, ra);
return -1;
}
return 0;
}
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
{
bool valid;
CPUState *cs = env_cpu(env);
uint64_t addr = env->regs[r1];
uint64_t subcode = env->regs[r3];
IplParameterBlock *iplb;
if (env->psw.mask & PSW_MASK_PSTATE) {
s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return;
}
if (subcode & ~0x0ffffULL) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
if (subcode >= DIAG308_PV_SET && !s390_has_feat(S390_FEAT_UNPACK)) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
switch (subcode) {
case DIAG308_RESET_MOD_CLR:
s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR);
break;
case DIAG308_RESET_LOAD_NORM:
s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL);
break;
case DIAG308_LOAD_CLEAR:
/* Well we still lack the clearing bit... */
s390_ipl_reset_request(cs, S390_RESET_REIPL);
break;
case DIAG308_SET:
case DIAG308_PV_SET:
if (diag308_parm_check(env, r1, addr, ra, false)) {
return;
}
iplb = g_new0(IplParameterBlock, 1);
cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
if (!iplb_valid_len(iplb)) {
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
goto out;
}
cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
if (!valid) {
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
goto out;
}
s390_ipl_update_diag308(iplb);
env->regs[r1 + 1] = DIAG_308_RC_OK;
out:
g_free(iplb);
return;
case DIAG308_STORE:
case DIAG308_PV_STORE:
if (diag308_parm_check(env, r1, addr, ra, true)) {
return;
}
if (subcode == DIAG308_PV_STORE) {
iplb = s390_ipl_get_iplb_pv();
} else {
iplb = s390_ipl_get_iplb();
}
if (iplb) {
cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
env->regs[r1 + 1] = DIAG_308_RC_OK;
} else {
env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
}
return;
case DIAG308_PV_START:
iplb = s390_ipl_get_iplb_pv();
if (!iplb) {
env->regs[r1 + 1] = DIAG_308_RC_NO_PV_CONF;
return;
}
if (kvm_s390_get_hpage_1m()) {
error_report("Protected VMs can currently not be backed with "
"huge pages");
env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
return;
}
s390_ipl_reset_request(cs, S390_RESET_PV);
break;
default:
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
break;
}
}