Commit 45024f09 authored by Alexander Graf's avatar Alexander Graf
Browse files

PPC: Add PV hypercall transport through fw_cfg



On KVM for PPC we need to tell the guest which instructions to use when
doing a hypercall. The clean way to do this is to go through an ioctl
from userspace and passing it on to the guest using the device tree.

So let's do the qemu part here: read out the hypercall and pass it on
to the guest's fw_cfg so openBIOS can read it out and expose it again.

Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent 2446333c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -47,5 +47,8 @@ enum {
#define FW_CFG_PPC_HEIGHT	(FW_CFG_ARCH_LOCAL + 0x01)
#define FW_CFG_PPC_DEPTH	(FW_CFG_ARCH_LOCAL + 0x02)
#define FW_CFG_PPC_TBFREQ	(FW_CFG_ARCH_LOCAL + 0x03)
#define FW_CFG_PPC_IS_KVM       (FW_CFG_ARCH_LOCAL + 0x05)
#define FW_CFG_PPC_KVM_HC       (FW_CFG_ARCH_LOCAL + 0x06)
#define FW_CFG_PPC_KVM_PID      (FW_CFG_ARCH_LOCAL + 0x07)

#define PPC_SERIAL_MM_BAUDBASE 399193
+7 −0
Original line number Diff line number Diff line
@@ -427,9 +427,16 @@ static void ppc_core99_init (ram_addr_t ram_size,
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);

    fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
    if (kvm_enabled()) {
#ifdef CONFIG_KVM
        uint8_t *hypercall;

        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
        hypercall = qemu_malloc(16);
        kvmppc_get_hypercall(env, hypercall, 16);
        fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
#endif
    } else {
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+7 −0
Original line number Diff line number Diff line
@@ -399,9 +399,16 @@ static void ppc_heathrow_init (ram_addr_t ram_size,
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);

    fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
    if (kvm_enabled()) {
#ifdef CONFIG_KVM
        uint8_t *hypercall;

        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
        hypercall = qemu_malloc(16);
        kvmppc_get_hypercall(env, hypercall, 16);
        fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
#endif
    } else {
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+32 −0
Original line number Diff line number Diff line
@@ -327,6 +327,38 @@ uint32_t kvmppc_get_tbfreq(void)
    return retval;
}

int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
{
    uint32_t *hc = (uint32_t*)buf;

#ifdef KVM_CAP_PPC_GET_PVINFO
    struct kvm_ppc_pvinfo pvinfo;

    if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO) &&
        !kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_PVINFO, &pvinfo)) {
        memcpy(buf, pvinfo.hcall, buf_len);

        return 0;
    }
#endif

    /*
     * Fallback to always fail hypercalls:
     *
     *     li r3, -1
     *     nop
     *     nop
     *     nop
     */

    hc[0] = 0x3860ffff;
    hc[1] = 0x60000000;
    hc[2] = 0x60000000;
    hc[3] = 0x60000000;

    return 0;
}

bool kvm_arch_stop_on_emulation_error(CPUState *env)
{
    return true;
+1 −0
Original line number Diff line number Diff line
@@ -15,5 +15,6 @@ int kvmppc_read_host_property(const char *node_path, const char *prop,
                                     void *val, size_t len);

uint32_t kvmppc_get_tbfreq(void);
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);

#endif /* __KVM_PPC_H__ */