summaryrefslogtreecommitdiff
path: root/kvm_lapic.c
blob: 54db1e60e69c8a1ca574dea171f7e171d2ed8ef1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * Local APIC virtualization
 *
 * Copyright (C) 2006 Qumranet, Inc.
 * Copyright (C) 2007 Novell
 * Copyright (C) 2007 Intel
 *
 * Authors:
 *   Dor Laor <dor.laor@qumranet.com>
 *   Gregory Haskins <ghaskins@novell.com>
 *   Yaozu (Eddie) Dong <eddie.dong@intel.com>
 *
 * Based on Xen 3.1 code, Copyright (c) 2004, Intel Corporation.
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
 * Ported to illumos by Joyent.
 * Copyright 2011 Joyent, Inc. All rights reserved.
 */

#include <sys/atomic.h>

/*
 * XXX Need proper header files!
 */
#include "msr.h"
#include "irqflags.h"
#include "kvm_host.h"
#include "kvm_x86host.h"
#include "iodev.h"
#include "kvm.h"
#include "apicdef.h"
#include "irq.h"

extern uint32_t apic_get_reg(struct kvm_lapic *, int);
extern int apic_enabled(struct kvm_lapic *);
extern int apic_hw_enabled(struct kvm_lapic *);
extern int __apic_accept_irq(struct kvm_lapic *, int, int, int, int);
extern caddr_t page_address(page_t *);

static int
apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type)
{
	return (!(apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED));
}

int
apic_has_pending_timer(struct kvm_vcpu *vcpu)
{
	struct kvm_lapic *lapic = vcpu->arch.apic;

#ifdef XXX
	if (lapic && apic_enabled(lapic) && apic_lvt_enabled(lapic, APIC_LVTT))
		return (atomic_read(&lapic->lapic_timer.pending));
#else
	XXX_KVM_SYNC_PROBE;
	if (lapic && apic_enabled(lapic) && apic_lvt_enabled(lapic, APIC_LVTT))
		return (lapic->lapic_timer.pending);
#endif

	return (0);
}

static int
kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type)
{
	uint32_t reg = apic_get_reg(apic, lvt_type);
	int vector, mode, trig_mode;

	if (apic_hw_enabled(apic) && !(reg & APIC_LVT_MASKED)) {
		vector = reg & APIC_VECTOR_MASK;
		mode = reg & APIC_MODE_MASK;
		trig_mode = reg & APIC_LVT_LEVEL_TRIGGER;
		return (__apic_accept_irq(apic, mode, vector, 1, trig_mode));
	}
	return (0);
}

void
kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
{
	struct kvm_lapic *apic = vcpu->arch.apic;

#ifdef XXX
	if (apic && atomic_read(&apic->lapic_timer.pending) > 0) {
		if (kvm_apic_local_deliver(apic, APIC_LVTT))
			atomic_dec(&apic->lapic_timer.pending);
	}
#else
	XXX_KVM_SYNC_PROBE;
	if (apic && apic->lapic_timer.pending > 0) {
		if (kvm_apic_local_deliver(apic, APIC_LVTT))
			atomic_dec_32(&apic->lapic_timer.pending);
	}
#endif
}

void
kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu)
{
	struct kvm_lapic *apic = vcpu->arch.apic;

	if (apic)
		kvm_apic_local_deliver(apic, APIC_LVT0);
}

void
kvm_free_lapic(struct kvm_vcpu *vcpu)
{
	struct kvm_lapic *apic = vcpu->arch.apic;
	if (apic == NULL)
		return;

	mutex_enter(&cpu_lock);
	if (apic->lapic_timer.active)
		cyclic_remove(apic->lapic_timer.kvm_cyclic_id);
	mutex_exit(&cpu_lock);

	if (apic->regs)
		kmem_free(apic->regs, PAGESIZE);

	kmem_free(vcpu->arch.apic, sizeof (struct kvm_lapic));
}

void
__kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
{
#ifdef XXX
	struct kvm_lapic *apic = vcpu->arch.apic;
	struct hrtimer *timer;

	if (!apic)
		return;

	timer = &apic->lapic_timer.timer;
	if (hrtimer_cancel_p(timer))
		kvm_hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
#else
	XXX_KVM_PROBE;
#endif
}