blob: a7981b17795f0c02c37dd04b73512e280247d159 [file] [log] [blame]
/*
* qemu/kvm integration, x86 specific code
*
* Copyright (C) 2006-2008 Qumranet Technologies
*
* Licensed under the terms of the GNU GPL version 2 or higher.
*/
#include "config.h"
#include "config-host.h"
#include <string.h>
#include "hw/hw.h"
#include "gdbstub.h"
#include <sys/io.h>
#include "qemu-kvm.h"
#include <pthread.h>
#include <sys/utsname.h>
#include <linux/kvm_para.h>
#include <sys/ioctl.h>
#include "kvm.h"
#include "hw/apic.h"
static int kvm_create_pit(KVMState *s)
{
int r;
if (kvm_pit) {
r = kvm_vm_ioctl(s, KVM_CREATE_PIT);
if (r < 0) {
fprintf(stderr, "Create kernel PIC irqchip failed\n");
return r;
}
if (!kvm_pit_reinject) {
r = kvm_reinject_control(s, 0);
if (r < 0) {
fprintf(stderr,
"failure to disable in-kernel PIT reinjection\n");
return r;
}
}
}
return 0;
}
int kvm_handle_tpr_access(CPUState *env)
{
struct kvm_run *run = env->kvm_run;
kvm_tpr_access_report(env,
run->tpr_access.rip,
run->tpr_access.is_write);
return 1;
}
int kvm_enable_vapic(CPUState *env, uint64_t vapic)
{
struct kvm_vapic_addr va = {
.vapic_addr = vapic,
};
return kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va);
}
int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s)
{
int r = 0;
if (!kvm_irqchip_in_kernel()) {
return r;
}
r = kvm_vcpu_ioctl(env, KVM_GET_LAPIC, s);
if (r < 0) {
fprintf(stderr, "KVM_GET_LAPIC failed\n");
}
return r;
}
int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s)
{
int r = 0;
if (!kvm_irqchip_in_kernel()) {
return 0;
}
r = kvm_vcpu_ioctl(env, KVM_SET_LAPIC, s);
if (r < 0) {
fprintf(stderr, "KVM_SET_LAPIC failed\n");
}
return r;
}
int kvm_get_pit(KVMState *s, struct kvm_pit_state *pit_state)
{
if (!kvm_pit_in_kernel()) {
return 0;
}
return kvm_vm_ioctl(s, KVM_GET_PIT, pit_state);
}
int kvm_set_pit(KVMState *s, struct kvm_pit_state *pit_state)
{
if (!kvm_pit_in_kernel()) {
return 0;
}
return kvm_vm_ioctl(s, KVM_SET_PIT, pit_state);
}
int kvm_get_pit2(KVMState *s, struct kvm_pit_state2 *ps2)
{
if (!kvm_pit_in_kernel()) {
return 0;
}
return kvm_vm_ioctl(s, KVM_GET_PIT2, ps2);
}
int kvm_set_pit2(KVMState *s, struct kvm_pit_state2 *ps2)
{
if (!kvm_pit_in_kernel()) {
return 0;
}
return kvm_vm_ioctl(s, KVM_SET_PIT2, ps2);
}
static int kvm_enable_tpr_access_reporting(CPUState *env)
{
int r;
struct kvm_tpr_access_ctl tac = { .enabled = 1 };
r = kvm_ioctl(env->kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC);
if (r <= 0) {
return -ENOSYS;
}
return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac);
}
static int _kvm_arch_init_vcpu(CPUState *env)
{
kvm_arch_reset_vcpu(env);
kvm_enable_tpr_access_reporting(env);
return kvm_update_ioport_access(env);
}
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
int kvm_arch_set_ioport_access(unsigned long start, unsigned long size,
bool enable)
{
if (ioperm(start, size, enable) < 0) {
return -errno;
}
return 0;
}
#endif
/*
* Setup x86 specific IRQ routing
*/
int kvm_arch_init_irq_routing(void)
{
int i, r;
if (kvm_has_gsi_routing()) {
kvm_clear_gsi_routes();
for (i = 0; i < 8; ++i) {
if (i == 2) {
continue;
}
r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_MASTER, i);
if (r < 0) {
return r;
}
}
for (i = 8; i < 16; ++i) {
r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_SLAVE, i - 8);
if (r < 0) {
return r;
}
}
for (i = 0; i < 24; ++i) {
if (i == 0) {
r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, 2);
} else if (i != 2) {
r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, i);
}
if (r < 0) {
return r;
}
}
kvm_commit_irq_routes();
if (!kvm_has_pit_state2()) {
no_hpet = 1;
}
} else {
/* If kernel can't do irq routing, interrupt source
* override 0->2 can not be set up as required by HPET.
* so we have to disable it.
*/
no_hpet = 1;
}
return 0;
}