VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAll.cpp@ 27046

Last change on this file since 27046 was 26939, checked in by vboxsync, 15 years ago

ACPI, APIC, HPET: emulate typical configuration wrt ISA interrupt routing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.5 KB
Line 
1/* $Id: PDMAll.cpp 26939 2010-03-02 12:13:40Z vboxsync $ */
2/** @file
3 * PDM Critical Sections
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_PDM
27#include "PDMInternal.h"
28#include <VBox/pdm.h>
29#include <VBox/mm.h>
30#include <VBox/vm.h>
31#include <VBox/err.h>
32
33#include <VBox/log.h>
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36
37
38/**
39 * Gets the pending interrupt.
40 *
41 * @returns VBox status code.
42 * @param pVCpu VMCPU handle.
43 * @param pu8Interrupt Where to store the interrupt on success.
44 */
45VMMDECL(int) PDMGetInterrupt(PVMCPU pVCpu, uint8_t *pu8Interrupt)
46{
47 PVM pVM = pVCpu->CTX_SUFF(pVM);
48
49 pdmLock(pVM);
50
51 /*
52 * The local APIC has a higer priority than the PIC.
53 */
54 if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
55 {
56 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC);
57 Assert(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
58 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetInterrupt));
59 int i = pVM->pdm.s.Apic.CTX_SUFF(pfnGetInterrupt)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
60 AssertMsg(i <= 255 && i >= 0, ("i=%d\n", i));
61 if (i >= 0)
62 {
63 pdmUnlock(pVM);
64 *pu8Interrupt = (uint8_t)i;
65 return VINF_SUCCESS;
66 }
67 }
68
69 /*
70 * Check the PIC.
71 */
72 if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INTERRUPT_PIC))
73 {
74 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_PIC);
75 Assert(pVM->pdm.s.Pic.CTX_SUFF(pDevIns));
76 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt));
77 int i = pVM->pdm.s.Pic.CTX_SUFF(pfnGetInterrupt)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns));
78 AssertMsg(i <= 255 && i >= 0, ("i=%d\n", i));
79 if (i >= 0)
80 {
81 pdmUnlock(pVM);
82 *pu8Interrupt = (uint8_t)i;
83 return VINF_SUCCESS;
84 }
85 }
86
87 /** @todo Figure out exactly why we can get here without anything being set. (REM) */
88
89 pdmUnlock(pVM);
90 return VERR_NO_DATA;
91}
92
93
94/**
95 * Sets the pending interrupt coming from ISA source or HPET.
96 *
97 * @returns VBox status code.
98 * @param pVM VM handle.
99 * @param u8Irq The IRQ line.
100 * @param u8Level The new level.
101 */
102VMMDECL(int) PDMIsaSetIrq(PVM pVM, uint8_t u8Irq, uint8_t u8Level, bool fIsaSource)
103{
104 pdmLock(pVM);
105
106 int rc = VERR_PDM_NO_PIC_INSTANCE;
107 if (pVM->pdm.s.Pic.CTX_SUFF(pDevIns))
108 {
109 Assert(pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq));
110 pVM->pdm.s.Pic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.Pic.CTX_SUFF(pDevIns), u8Irq, u8Level);
111 rc = VINF_SUCCESS;
112 }
113
114 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
115 {
116 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
117
118 /**
119 * Apply Interrupt Source Override rules.
120 * See ACPI 4.0 specification 5.2.12.4 and 5.2.12.5 for details on
121 * interrupt source override.
122 * Shortly, ISA IRQ0 is electically connected to pin 2 on IO-APIC, and some OSes,
123 * notably recent OS X rely upon this configuration.
124 * If changing, also update override rules in MADT and MPS.
125 */
126 if (fIsaSource)
127 {
128 /* ISA IRQ0 routed to pin 2, all others ISA sources are identity mapped */
129 if (u8Irq == 0)
130 u8Irq = 2;
131 }
132 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), u8Irq, u8Level);
133 rc = VINF_SUCCESS;
134 }
135
136 pdmUnlock(pVM);
137 return rc;
138}
139
140
141/**
142 * Sets the pending I/O APIC interrupt.
143 *
144 * @returns VBox status code.
145 * @param pVM VM handle.
146 * @param u8Irq The IRQ line.
147 * @param u8Level The new level.
148 */
149VMMDECL(int) PDMIoApicSetIrq(PVM pVM, uint8_t u8Irq, uint8_t u8Level)
150{
151 if (pVM->pdm.s.IoApic.CTX_SUFF(pDevIns))
152 {
153 Assert(pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq));
154 pdmLock(pVM);
155 pVM->pdm.s.IoApic.CTX_SUFF(pfnSetIrq)(pVM->pdm.s.IoApic.CTX_SUFF(pDevIns), u8Irq, u8Level);
156 pdmUnlock(pVM);
157 return VINF_SUCCESS;
158 }
159 return VERR_PDM_NO_PIC_INSTANCE;
160}
161
162
163/**
164 * Returns presence of an IO-APIC
165 *
166 * @returns VBox true if IO-APIC is present
167 * @param pVM VM handle.
168 */
169VMMDECL(bool) PDMHasIoApic(PVM pVM)
170{
171 return pVM->pdm.s.IoApic.CTX_SUFF(pDevIns) != NULL;
172}
173
174
175/**
176 * Set the APIC base.
177 *
178 * @returns VBox status code.
179 * @param pVM VM handle.
180 * @param u64Base The new base.
181 */
182VMMDECL(int) PDMApicSetBase(PVM pVM, uint64_t u64Base)
183{
184 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
185 {
186 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnSetBase));
187 pdmLock(pVM);
188 pVM->pdm.s.Apic.CTX_SUFF(pfnSetBase)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), u64Base);
189 pdmUnlock(pVM);
190 return VINF_SUCCESS;
191 }
192 return VERR_PDM_NO_APIC_INSTANCE;
193}
194
195
196/**
197 * Get the APIC base.
198 *
199 * @returns VBox status code.
200 * @param pVM VM handle.
201 * @param pu64Base Where to store the APIC base.
202 */
203VMMDECL(int) PDMApicGetBase(PVM pVM, uint64_t *pu64Base)
204{
205 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
206 {
207 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetBase));
208 pdmLock(pVM);
209 *pu64Base = pVM->pdm.s.Apic.CTX_SUFF(pfnGetBase)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
210 pdmUnlock(pVM);
211 return VINF_SUCCESS;
212 }
213 *pu64Base = 0;
214 return VERR_PDM_NO_APIC_INSTANCE;
215}
216
217
218/**
219 * Check if the APIC has a pending interrupt/if a TPR change would active one.
220 *
221 * @returns VINF_SUCCESS or VERR_PDM_NO_APIC_INSTANCE.
222 * @param pDevIns Device instance of the APIC.
223 * @param pfPending Pending state (out).
224 */
225VMMDECL(int) PDMApicHasPendingIrq(PVM pVM, bool *pfPending)
226{
227 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
228 {
229 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnSetTPR));
230 pdmLock(pVM);
231 *pfPending = pVM->pdm.s.Apic.CTX_SUFF(pfnHasPendingIrq)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
232 pdmUnlock(pVM);
233 return VINF_SUCCESS;
234 }
235 return VERR_PDM_NO_APIC_INSTANCE;
236}
237
238
239/**
240 * Set the TPR (task priority register?).
241 *
242 * @returns VBox status code.
243 * @param pVCpu VMCPU handle.
244 * @param u8TPR The new TPR.
245 */
246VMMDECL(int) PDMApicSetTPR(PVMCPU pVCpu, uint8_t u8TPR)
247{
248 PVM pVM = pVCpu->CTX_SUFF(pVM);
249 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
250 {
251 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnSetTPR));
252 pdmLock(pVM);
253 pVM->pdm.s.Apic.CTX_SUFF(pfnSetTPR)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu->idCpu, u8TPR);
254 pdmUnlock(pVM);
255 return VINF_SUCCESS;
256 }
257 return VERR_PDM_NO_APIC_INSTANCE;
258}
259
260
261/**
262 * Get the TPR (task priority register).
263 *
264 * @returns The current TPR.
265 * @param pVCpu VMCPU handle.
266 * @param pu8TPR Where to store the TRP.
267 * @param pfPending Pending interrupt state (out).
268*/
269VMMDECL(int) PDMApicGetTPR(PVMCPU pVCpu, uint8_t *pu8TPR, bool *pfPending)
270{
271 PVM pVM = pVCpu->CTX_SUFF(pVM);
272 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
273 {
274 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnGetTPR));
275 /* We don't acquire the PDM lock here as we're just reading information. Doing so causes massive
276 * contention as this function is called very often by each and every VCPU.
277 */
278 *pu8TPR = pVM->pdm.s.Apic.CTX_SUFF(pfnGetTPR)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), pVCpu->idCpu);
279 if (pfPending)
280 *pfPending = pVM->pdm.s.Apic.CTX_SUFF(pfnHasPendingIrq)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns));
281 return VINF_SUCCESS;
282 }
283 *pu8TPR = 0;
284 return VERR_PDM_NO_APIC_INSTANCE;
285}
286
287/**
288 * Write MSR in APIC range.
289 *
290 * @returns VBox status code.
291 * @param pVM VM handle.
292 * @param iCpu Target CPU.
293 * @param u32Reg MSR to write.
294 * @param u64Value Value to write.
295 */
296VMMDECL(int) PDMApicWriteMSR(PVM pVM, VMCPUID iCpu, uint32_t u32Reg, uint64_t u64Value)
297{
298 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
299 {
300 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnWriteMSR));
301 pdmLock(pVM);
302 pVM->pdm.s.Apic.CTX_SUFF(pfnWriteMSR)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), iCpu, u32Reg, u64Value);
303 pdmUnlock(pVM);
304 return VINF_SUCCESS;
305 }
306 return VERR_PDM_NO_APIC_INSTANCE;
307}
308
309/**
310 * Read MSR in APIC range.
311 *
312 * @returns VBox status code.
313 * @param pVM VM handle.
314 * @param iCpu Target CPU.
315 * @param u32Reg MSR to read.
316 * @param pu64Value Value read.
317 */
318VMMDECL(int) PDMApicReadMSR(PVM pVM, VMCPUID iCpu, uint32_t u32Reg, uint64_t *pu64Value)
319{
320 if (pVM->pdm.s.Apic.CTX_SUFF(pDevIns))
321 {
322 Assert(pVM->pdm.s.Apic.CTX_SUFF(pfnReadMSR));
323 pdmLock(pVM);
324 pVM->pdm.s.Apic.CTX_SUFF(pfnReadMSR)(pVM->pdm.s.Apic.CTX_SUFF(pDevIns), iCpu, u32Reg, pu64Value);
325 pdmUnlock(pVM);
326 return VINF_SUCCESS;
327 }
328 return VERR_PDM_NO_APIC_INSTANCE;
329}
330
331
332/**
333 * Locks PDM.
334 * This might call back to Ring-3 in order to deal with lock contention in GC and R3.
335 *
336 * @param pVM The VM handle.
337 */
338void pdmLock(PVM pVM)
339{
340#ifdef IN_RING3
341 int rc = PDMCritSectEnter(&pVM->pdm.s.CritSect, VERR_INTERNAL_ERROR);
342#else
343 int rc = PDMCritSectEnter(&pVM->pdm.s.CritSect, VERR_GENERAL_FAILURE);
344 if (rc == VERR_GENERAL_FAILURE)
345 rc = VMMRZCallRing3NoCpu(pVM, VMMCALLRING3_PDM_LOCK, 0);
346#endif
347 AssertRC(rc);
348}
349
350
351/**
352 * Locks PDM but don't go to ring-3 if it's owned by someone.
353 *
354 * @returns VINF_SUCCESS on success.
355 * @returns rc if we're in GC or R0 and can't get the lock.
356 * @param pVM The VM handle.
357 * @param rc The RC to return in GC or R0 when we can't get the lock.
358 */
359int pdmLockEx(PVM pVM, int rc)
360{
361 return PDMCritSectEnter(&pVM->pdm.s.CritSect, rc);
362}
363
364
365/**
366 * Unlocks PDM.
367 *
368 * @param pVM The VM handle.
369 */
370void pdmUnlock(PVM pVM)
371{
372 PDMCritSectLeave(&pVM->pdm.s.CritSect);
373}
374
375
376/**
377 * Converts ring 3 VMM heap pointer to a guest physical address
378 *
379 * @returns VBox status code.
380 * @param pVM VM handle.
381 * @param pv Ring-3 pointer.
382 * @param pGCPhys GC phys address (out).
383 */
384VMMDECL(int) PDMVMMDevHeapR3ToGCPhys(PVM pVM, RTR3PTR pv, RTGCPHYS *pGCPhys)
385{
386 /* Don't assert here as this is called before we can catch ring-0 assertions. */
387 if (RT_UNLIKELY((RTR3UINTPTR)pv - (RTR3UINTPTR)pVM->pdm.s.pvVMMDevHeap >= pVM->pdm.s.cbVMMDevHeap))
388 {
389 Log(("PDMVMMDevHeapR3ToGCPhys: pv=%p pvVMMDevHeap=%p cbVMMDevHeap=%#x\n",
390 pv, pVM->pdm.s.pvVMMDevHeap, pVM->pdm.s.cbVMMDevHeap));
391 return VERR_INTERNAL_ERROR_3;
392 }
393
394 *pGCPhys = (pVM->pdm.s.GCPhysVMMDevHeap + ((RTR3UINTPTR)pv - (RTR3UINTPTR)pVM->pdm.s.pvVMMDevHeap));
395 return VINF_SUCCESS;
396}
397
398/**
399 * Checks if the vmm device heap is enabled (== vmm device's pci region mapped)
400 *
401 * @returns dev heap enabled status (true/false)
402 * @param pVM VM handle.
403 */
404VMMDECL(bool) PDMVMMDevHeapIsEnabled(PVM pVM)
405{
406 return (pVM->pdm.s.pvVMMDevHeap != NULL);
407}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette