/** * Initializes remaining bits of the KVM provider. * * This is called after initializing HM and almost all other VMM components. * * @returns VBox status code. * @param pVM Pointer to the VM. */ VMMR3_INT_DECL(int) gimR3KvmInitCompleted(PVM pVM) { PGIMKVM pKvm = &pVM->gim.s.u.Kvm; pKvm->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM); if (TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */)) { /** @todo We might want to consider just enabling this bit *always*. As far * as I can see in the Linux guest, the "TSC_STABLE" bit is only * translated as a "monotonic" bit which even in Async systems we * -should- be reporting a strictly monotonic TSC to the guest. */ pKvm->uBaseFeat |= GIM_KVM_BASE_FEAT_TSC_STABLE; CPUMCPUIDLEAF HyperLeaf; RT_ZERO(HyperLeaf); HyperLeaf.uLeaf = UINT32_C(0x40000001); HyperLeaf.uEax = pKvm->uBaseFeat; HyperLeaf.uEbx = 0; HyperLeaf.uEcx = 0; HyperLeaf.uEdx = 0; int rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); } return VINF_SUCCESS; }
/** * Initializes remaining bits of the Minimal provider. * This is called after initializing HM and almost all other VMM components. * * @returns VBox status code. * @param pVM Pointer to the VM. */ VMMR3_INT_DECL(int) gimR3MinimalInitCompleted(PVM pVM) { /* * Expose a generic hypervisor-agnostic leaf (originally defined VMware). * The leaves range from 0x40000010 to 0x400000FF. * * This is done in the init. completed routine as we need PDM to be * initialized (otherwise PDMApicGetTimerFreq() would fail). */ CPUMCPUIDLEAF HyperLeaf; int rc = CPUMR3CpuIdGetLeaf(pVM, &HyperLeaf, 0x40000000, 0 /* uSubLeaf */); if (RT_SUCCESS(rc)) { HyperLeaf.uEax = UINT32_C(0x40000010); /* Maximum leaf we implement. */ rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); /* * Insert missing zero leaves (you never know what missing leaves are * going to return when read). */ for (uint32_t uLeaf = UINT32_C(0x40000001); uLeaf < UINT32_C(0x40000010); uLeaf++) { rc = CPUMR3CpuIdGetLeaf(pVM, &HyperLeaf, uLeaf, 0 /* uSubLeaf */); if (RT_FAILURE(rc)) { RT_ZERO(HyperLeaf); HyperLeaf.uLeaf = uLeaf; rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); } } /* * Add the timing information hypervisor leaf. * MacOS X uses this to determine the TSC, bus frequency. See @bugref{7270}. * * EAX - TSC frequency in KHz. * EBX - APIC frequency in KHz. * ECX, EDX - Reserved. */ uint64_t uApicFreq; rc = PDMApicGetTimerFreq(pVM, &uApicFreq); AssertLogRelRCReturn(rc, rc); RT_ZERO(HyperLeaf); HyperLeaf.uLeaf = UINT32_C(0x40000010); HyperLeaf.uEax = TMCpuTicksPerSecond(pVM) / UINT64_C(1000); HyperLeaf.uEbx = (uApicFreq + 500) / UINT64_C(1000); rc = CPUMR3CpuIdInsert(pVM, &HyperLeaf); AssertLogRelRCReturn(rc, rc); } else LogRel(("GIM: Minimal: failed to get hypervisor leaf 0x40000000.\n")); return VINF_SUCCESS; }
/** * KVM state-load operation, final pass. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param pSSM Pointer to the SSM handle. * @param uSSMVersion The GIM saved-state version. */ VMMR3_INT_DECL(int) gimR3KvmLoad(PVM pVM, PSSMHANDLE pSSM, uint32_t uSSMVersion) { /* * Load the KVM SSM version first. */ uint32_t uKvmSavedStatVersion; int rc = SSMR3GetU32(pSSM, &uKvmSavedStatVersion); AssertRCReturn(rc, rc); if (uKvmSavedStatVersion != GIM_KVM_SAVED_STATE_VERSION) return SSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS, N_("Unsupported KVM saved-state version %u (expected %u)."), uKvmSavedStatVersion, GIM_KVM_SAVED_STATE_VERSION); /* * Update the TSC frequency from TM. */ PGIMKVM pKvm = &pVM->gim.s.u.Kvm; pKvm->cTscTicksPerSecond = TMCpuTicksPerSecond(pVM); /* * Load per-VCPU data. */ for (uint32_t i = 0; i < pVM->cCpus; i++) { PVMCPU pVCpu = &pVM->aCpus[i]; PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; uint8_t fSystemTimeFlags = 0; SSMR3GetU64(pSSM, &pKvmCpu->u64SystemTimeMsr); SSMR3GetU64(pSSM, &pKvmCpu->uTsc); SSMR3GetU64(pSSM, &pKvmCpu->uVirtNanoTS); SSMR3GetGCPhys(pSSM, &pKvmCpu->GCPhysSystemTime); SSMR3GetU32(pSSM, &pKvmCpu->u32SystemTimeVersion); rc = SSMR3GetU8(pSSM, &pKvmCpu->fSystemTimeFlags); AssertRCReturn(rc, rc); /* Enable the system-time struct. if necessary. */ /** @todo update guest struct only if cTscTicksPerSecond doesn't match host * anymore. */ if (MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr)) { Assert(!TMVirtualIsTicking(pVM)); /* paranoia. */ Assert(!TMCpuTickIsTicking(pVCpu)); rc = gimR3KvmEnableSystemTime(pVM, pVCpu); AssertRCReturn(rc, rc); } } /* * Load per-VM data. */ SSMR3GetU64(pSSM, &pKvm->u64WallClockMsr); rc = SSMR3GetU32(pSSM, &pKvm->uBaseFeat); AssertRCReturn(rc, rc); return VINF_SUCCESS; }
/** * Updates Hyper-V's reference TSC page. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param u64Offset The computed TSC offset. * @thread EMT. */ VMM_INT_DECL(int) gimR0HvUpdateParavirtTsc(PVM pVM, uint64_t u64Offset) { Assert(GIMIsEnabled(pVM)); bool fHvTscEnabled = MSR_GIM_HV_REF_TSC_IS_ENABLED(pVM->gim.s.u.Hv.u64TscPageMsr); if (RT_UNLIKELY(!fHvTscEnabled)) return VERR_GIM_PVTSC_NOT_ENABLED; PCGIMHV pcHv = &pVM->gim.s.u.Hv; PCGIMMMIO2REGION pcRegion = &pcHv->aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX]; PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pcRegion->CTX_SUFF(pvPage); Assert(pRefTsc); /* * Hyper-V reports the reference time in 100 nanosecond units. */ uint64_t u64Tsc100Ns = TMCpuTicksPerSecond(pVM) / RT_NS_10MS; int64_t i64TscOffset = (int64_t)u64Offset / u64Tsc100Ns; /* * The TSC page can be simulatenously read by other VCPUs in the guest. The * spinlock is only for protecting simultaneous hypervisor writes from other * EMTs. */ RTSpinlockAcquire(pcHv->hSpinlockR0); if (pRefTsc->i64TscOffset != i64TscOffset) { if (pRefTsc->u32TscSequence < UINT32_C(0xfffffffe)) ASMAtomicIncU32(&pRefTsc->u32TscSequence); else ASMAtomicWriteU32(&pRefTsc->u32TscSequence, 1); ASMAtomicWriteS64(&pRefTsc->i64TscOffset, i64TscOffset); } RTSpinlockRelease(pcHv->hSpinlockR0); Assert(pRefTsc->u32TscSequence != 0); Assert(pRefTsc->u32TscSequence != UINT32_C(0xffffffff)); return VINF_SUCCESS; }
/** * Enables the Hyper-V TSC page. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param GCPhysTscPage Where to map the TSC page. * @param fUseThisTscSeq Whether to set the TSC sequence number to the one * specified in @a uTscSeq. * @param uTscSeq The TSC sequence value to use. Ignored if * @a fUseThisTscSeq is false. */ VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq) { PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX]; AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); int rc; if (pRegion->fMapped) { /* * Is it already enabled at the given guest-address? */ if (pRegion->GCPhysPage == GCPhysTscPage) return VINF_SUCCESS; /* * If it's mapped at a different address, unmap the previous address. */ rc = gimR3HvDisableTscPage(pVM); AssertRC(rc); } /* * Map the TSC-page at the specified address. */ Assert(!pRegion->fMapped); rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysTscPage); if (RT_SUCCESS(rc)) { Assert(pRegion->GCPhysPage == GCPhysTscPage); /* * Update the TSC scale. Windows guests expect a non-zero TSC sequence, otherwise * they fallback to using the reference count MSR which is not ideal in terms of VM-exits. * * Also, Hyper-V normalizes the time in 10 MHz, see: * http://technet.microsoft.com/it-it/sysinternals/dn553408%28v=vs.110%29 */ PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pRegion->pvPageR3; Assert(pRefTsc); uint64_t const u64TscKHz = TMCpuTicksPerSecond(pVM) / UINT64_C(1000); uint32_t u32TscSeq = 1; if ( fUseThisTscSeq && uTscSeq < UINT32_C(0xfffffffe)) u32TscSeq = uTscSeq + 1; pRefTsc->u32TscSequence = u32TscSeq; pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32; pRefTsc->i64TscOffset = 0; LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n", GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence)); TMR3CpuTickParavirtEnable(pVM); return VINF_SUCCESS; } else LogRelFunc(("GIMR3Mmio2Map failed. rc=%Rrc\n", rc)); return VERR_GIM_OPERATION_FAILED; }
/** * Construct the DMI table. * * @returns VBox status code. * @param pDevIns The device instance. * @param pTable Where to create the DMI table. * @param cbMax The maximum size of the DMI table. * @param pUuid Pointer to the UUID to use if the DmiUuid * configuration string isn't present. * @param pCfg The handle to our config node. * @param cCpus Number of VCPUs. * @param pcbDmiTables Size of DMI data in bytes. * @param pcNumDmiTables Number of DMI tables. */ int FwCommonPlantDMITable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, PCRTUUID pUuid, PCFGMNODE pCfg, uint16_t cCpus, uint16_t *pcbDmiTables, uint16_t *pcNumDmiTables) { #define CHECKSIZE(cbWant) \ { \ size_t cbNeed = (size_t)(pszStr + cbWant - (char *)pTable) + 5; /* +1 for strtab terminator +4 for end-of-table entry */ \ if (cbNeed > cbMax) \ { \ if (fHideErrors) \ { \ LogRel(("One of the DMI strings is too long -- using default DMI data!\n")); \ continue; \ } \ return PDMDevHlpVMSetError(pDevIns, VERR_TOO_MUCH_DATA, RT_SRC_POS, \ N_("One of the DMI strings is too long. Check all bios/Dmi* configuration entries. At least %zu bytes are needed but there is no space for more than %d bytes"), cbNeed, cbMax); \ } \ } #define READCFGSTRDEF(variable, name, default_value) \ { \ if (fForceDefault) \ pszTmp = default_value; \ else \ { \ rc = CFGMR3QueryStringDef(pCfg, name, szBuf, sizeof(szBuf), default_value); \ if (RT_FAILURE(rc)) \ { \ if (fHideErrors) \ { \ LogRel(("Configuration error: Querying \"" name "\" as a string failed -- using default DMI data!\n")); \ continue; \ } \ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \ N_("Configuration error: Querying \"" name "\" as a string failed")); \ } \ else if (!strcmp(szBuf, "<EMPTY>")) \ pszTmp = ""; \ else \ pszTmp = szBuf; \ } \ if (!pszTmp[0]) \ variable = 0; /* empty string */ \ else \ { \ variable = iStrNr++; \ size_t cStr = strlen(pszTmp) + 1; \ CHECKSIZE(cStr); \ memcpy(pszStr, pszTmp, cStr); \ pszStr += cStr ; \ } \ } #define READCFGSTR(variable, name) \ READCFGSTRDEF(variable, # name, s_szDef ## name) #define READCFGINT(variable, name) \ { \ if (fForceDefault) \ variable = s_iDef ## name; \ else \ { \ rc = CFGMR3QueryS32Def(pCfg, # name, & variable, s_iDef ## name); \ if (RT_FAILURE(rc)) \ { \ if (fHideErrors) \ { \ LogRel(("Configuration error: Querying \"" # name "\" as an int failed -- using default DMI data!\n")); \ continue; \ } \ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \ N_("Configuration error: Querying \"" # name "\" as an int failed")); \ } \ } \ } #define START_STRUCT(tbl) \ pszStr = (char *)(tbl + 1); \ iStrNr = 1; #define TERM_STRUCT \ { \ *pszStr++ = '\0'; /* terminate set of text strings */ \ if (iStrNr == 1) \ *pszStr++ = '\0'; /* terminate a structure without strings */ \ } bool fForceDefault = false; #ifdef VBOX_BIOS_DMI_FALLBACK /* * There will be two passes. If an error occurs during the first pass, a * message will be written to the release log and we fall back to default * DMI data and start a second pass. */ bool fHideErrors = true; #else /* * There will be one pass, every error is fatal and will prevent the VM * from starting. */ bool fHideErrors = false; #endif uint8_t fDmiUseHostInfo; int rc = CFGMR3QueryU8Def(pCfg, "DmiUseHostInfo", &fDmiUseHostInfo, 0); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiUseHostInfo\"")); /* Sync up with host default DMI values */ if (fDmiUseHostInfo) fwCommonUseHostDMIStrings(); uint8_t fDmiExposeMemoryTable; rc = CFGMR3QueryU8Def(pCfg, "DmiExposeMemoryTable", &fDmiExposeMemoryTable, 0); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiExposeMemoryTable\"")); uint8_t fDmiExposeProcessorInf; rc = CFGMR3QueryU8Def(pCfg, "DmiExposeProcInf", &fDmiExposeProcessorInf, 0); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiExposeProcInf\"")); for (;; fForceDefault = true, fHideErrors = false) { int iStrNr; char szBuf[256]; char *pszStr = (char *)pTable; char szDmiSystemUuid[64]; char *pszDmiSystemUuid; const char *pszTmp; if (fForceDefault) pszDmiSystemUuid = NULL; else { rc = CFGMR3QueryString(pCfg, "DmiSystemUuid", szDmiSystemUuid, sizeof(szDmiSystemUuid)); if (rc == VERR_CFGM_VALUE_NOT_FOUND) pszDmiSystemUuid = NULL; else if (RT_FAILURE(rc)) { if (fHideErrors) { LogRel(("Configuration error: Querying \"DmiSystemUuid\" as a string failed, using default DMI data\n")); continue; } return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Querying \"DmiSystemUuid\" as a string failed")); } else pszDmiSystemUuid = szDmiSystemUuid; } /********************************* * DMI BIOS information (Type 0) * *********************************/ PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr; CHECKSIZE(sizeof(*pBIOSInf)); pszStr = (char *)&pBIOSInf->u8ReleaseMajor; pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8ReleaseMajor); /* don't set these fields by default for legacy compatibility */ int iDmiBIOSReleaseMajor, iDmiBIOSReleaseMinor; READCFGINT(iDmiBIOSReleaseMajor, DmiBIOSReleaseMajor); READCFGINT(iDmiBIOSReleaseMinor, DmiBIOSReleaseMinor); if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0) { pszStr = (char *)&pBIOSInf->u8FirmwareMajor; pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8FirmwareMajor); pBIOSInf->u8ReleaseMajor = iDmiBIOSReleaseMajor; pBIOSInf->u8ReleaseMinor = iDmiBIOSReleaseMinor; int iDmiBIOSFirmwareMajor, iDmiBIOSFirmwareMinor; READCFGINT(iDmiBIOSFirmwareMajor, DmiBIOSFirmwareMajor); READCFGINT(iDmiBIOSFirmwareMinor, DmiBIOSFirmwareMinor); if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0) { pszStr = (char *)(pBIOSInf + 1); pBIOSInf->header.u8Length = sizeof(DMIBIOSINF); pBIOSInf->u8FirmwareMajor = iDmiBIOSFirmwareMajor; pBIOSInf->u8FirmwareMinor = iDmiBIOSFirmwareMinor; } } iStrNr = 1; pBIOSInf->header.u8Type = 0; /* BIOS Information */ pBIOSInf->header.u16Handle = 0x0000; READCFGSTR(pBIOSInf->u8Vendor, DmiBIOSVendor); READCFGSTR(pBIOSInf->u8Version, DmiBIOSVersion); pBIOSInf->u16Start = 0xE000; READCFGSTR(pBIOSInf->u8Release, DmiBIOSReleaseDate); pBIOSInf->u8ROMSize = 1; /* 128K */ pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */ | RT_BIT(7) /* PCI is supported */ | RT_BIT(15) /* Boot from CD is supported */ | RT_BIT(16) /* Selectable Boot is supported */ | RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */ | RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */ /* any more?? */ ; pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */ /* any more?? */ ; pBIOSInf->u8CharacteristicsByte2 = 0 /* any more?? */ ; TERM_STRUCT; /*********************************** * DMI system information (Type 1) * ***********************************/ PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr; CHECKSIZE(sizeof(*pSystemInf)); START_STRUCT(pSystemInf); pSystemInf->header.u8Type = 1; /* System Information */ pSystemInf->header.u8Length = sizeof(*pSystemInf); pSystemInf->header.u16Handle = 0x0001; READCFGSTR(pSystemInf->u8Manufacturer, DmiSystemVendor); READCFGSTR(pSystemInf->u8ProductName, DmiSystemProduct); READCFGSTR(pSystemInf->u8Version, DmiSystemVersion); READCFGSTR(pSystemInf->u8SerialNumber, DmiSystemSerial); RTUUID uuid; if (pszDmiSystemUuid) { rc = RTUuidFromStr(&uuid, pszDmiSystemUuid); if (RT_FAILURE(rc)) { if (fHideErrors) { LogRel(("Configuration error: Invalid UUID for DMI tables specified, using default DMI data\n")); continue; } return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Configuration error: Invalid UUID for DMI tables specified")); } uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow); uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid); uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion); pUuid = &uuid; } memcpy(pSystemInf->au8Uuid, pUuid, sizeof(RTUUID)); pSystemInf->u8WakeupType = 6; /* Power Switch */ READCFGSTR(pSystemInf->u8SKUNumber, DmiSystemSKU); READCFGSTR(pSystemInf->u8Family, DmiSystemFamily); TERM_STRUCT; /********************************** * DMI board information (Type 2) * **********************************/ PDMIBOARDINF pBoardInf = (PDMIBOARDINF)pszStr; CHECKSIZE(sizeof(*pBoardInf)); START_STRUCT(pBoardInf); int iDmiBoardBoardType; pBoardInf->header.u8Type = 2; /* Board Information */ pBoardInf->header.u8Length = sizeof(*pBoardInf); pBoardInf->header.u16Handle = 0x0008; READCFGSTR(pBoardInf->u8Manufacturer, DmiBoardVendor); READCFGSTR(pBoardInf->u8Product, DmiBoardProduct); READCFGSTR(pBoardInf->u8Version, DmiBoardVersion); READCFGSTR(pBoardInf->u8SerialNumber, DmiBoardSerial); READCFGSTR(pBoardInf->u8AssetTag, DmiBoardAssetTag); pBoardInf->u8FeatureFlags = RT_BIT(0) /* hosting board, e.g. motherboard */ ; READCFGSTR(pBoardInf->u8LocationInChass, DmiBoardLocInChass); pBoardInf->u16ChassisHandle = 0x0003; /* see type 3 */ READCFGINT(iDmiBoardBoardType, DmiBoardBoardType); pBoardInf->u8BoardType = iDmiBoardBoardType; pBoardInf->u8cObjectHandles = 0; TERM_STRUCT; /******************************************** * DMI System Enclosure or Chassis (Type 3) * ********************************************/ PDMICHASSIS pChassis = (PDMICHASSIS)pszStr; CHECKSIZE(sizeof(*pChassis)); pszStr = (char*)&pChassis->u32OEMdefined; iStrNr = 1; #ifdef VBOX_WITH_DMI_CHASSIS pChassis->header.u8Type = 3; /* System Enclosure or Chassis */ #else pChassis->header.u8Type = 0x7e; /* inactive */ #endif pChassis->header.u8Length = RT_OFFSETOF(DMICHASSIS, u32OEMdefined); pChassis->header.u16Handle = 0x0003; READCFGSTR(pChassis->u8Manufacturer, DmiChassisVendor); int iDmiChassisType; READCFGINT(iDmiChassisType, DmiChassisType); pChassis->u8Type = iDmiChassisType; READCFGSTR(pChassis->u8Version, DmiChassisVersion); READCFGSTR(pChassis->u8SerialNumber, DmiChassisSerial); READCFGSTR(pChassis->u8AssetTag, DmiChassisAssetTag); pChassis->u8BootupState = 0x03; /* safe */ pChassis->u8PowerSupplyState = 0x03; /* safe */ pChassis->u8ThermalState = 0x03; /* safe */ pChassis->u8SecurityStatus = 0x03; /* none XXX */ # if 0 /* v2.3+, currently not supported */ pChassis->u32OEMdefined = 0; pChassis->u8Height = 0; /* unspecified */ pChassis->u8NumPowerChords = 0; /* unspecified */ pChassis->u8ContElems = 0; /* no contained elements */ pChassis->u8ContElemRecLen = 0; /* no contained elements */ # endif TERM_STRUCT; /************************************** * DMI Processor Information (Type 4) * **************************************/ /* * This is just a dummy processor. Should we expose the real guest CPU features * here? Accessing this information at this point is difficult. */ char szSocket[32]; PDMIPROCESSORINF pProcessorInf = (PDMIPROCESSORINF)pszStr; CHECKSIZE(sizeof(*pProcessorInf)); START_STRUCT(pProcessorInf); if (fDmiExposeProcessorInf) pProcessorInf->header.u8Type = 4; /* Processor Information */ else pProcessorInf->header.u8Type = 126; /* inactive structure */ pProcessorInf->header.u8Length = sizeof(*pProcessorInf); pProcessorInf->header.u16Handle = 0x0007; RTStrPrintf(szSocket, sizeof(szSocket), "Socket #%u", 0); pProcessorInf->u8SocketDesignation = iStrNr++; { size_t cStr = strlen(szSocket) + 1; CHECKSIZE(cStr); memcpy(pszStr, szSocket, cStr); pszStr += cStr; } pProcessorInf->u8ProcessorType = 0x03; /* Central Processor */ pProcessorInf->u8ProcessorFamily = 0xB1; /* Pentium III with Intel SpeedStep(TM) */ READCFGSTR(pProcessorInf->u8ProcessorManufacturer, DmiProcManufacturer); pProcessorInf->u64ProcessorID = UINT64_C(0x0FEBFBFF00010676); /* Ext Family ID = 0 * Ext Model ID = 2 * Processor Type = 0 * Family ID = 6 * Model = 7 * Stepping = 6 * Features: FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8, * APIC, SEP, MTRR, PGE, MCA, CMOV, PAT, PSE-36, * CFLSH, DS, ACPI, MMX, FXSR, SSE, SSE2, SS */ READCFGSTR(pProcessorInf->u8ProcessorVersion, DmiProcVersion); pProcessorInf->u8Voltage = 0x02; /* 3.3V */ pProcessorInf->u16ExternalClock = 0x00; /* unknown */ pProcessorInf->u16MaxSpeed = 3000; /* 3GHz */ pProcessorInf->u16CurrentSpeed = 3000; /* 3GHz */ pProcessorInf->u8Status = RT_BIT(6) /* CPU socket populated */ | RT_BIT(0) /* CPU enabled */ ; pProcessorInf->u8ProcessorUpgrade = 0x04; /* ZIF Socket */ pProcessorInf->u16L1CacheHandle = 0xFFFF; /* not specified */ pProcessorInf->u16L2CacheHandle = 0xFFFF; /* not specified */ pProcessorInf->u16L3CacheHandle = 0xFFFF; /* not specified */ pProcessorInf->u8SerialNumber = 0; /* not specified */ pProcessorInf->u8AssetTag = 0; /* not specified */ pProcessorInf->u8PartNumber = 0; /* not specified */ pProcessorInf->u8CoreCount = cCpus; /* */ pProcessorInf->u8CoreEnabled = cCpus; pProcessorInf->u8ThreadCount = 1; pProcessorInf->u16ProcessorCharacteristics = RT_BIT(2); /* 64-bit capable */ pProcessorInf->u16ProcessorFamily2 = 0; TERM_STRUCT; /*************************************** * DMI Physical Memory Array (Type 16) * ***************************************/ uint64_t u64RamSize; rc = CFGMR3QueryU64(pCfg, "RamSize", &u64RamSize); if (RT_FAILURE (rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"RamSize\"")); PDMIRAMARRAY pMemArray = (PDMIRAMARRAY)pszStr; CHECKSIZE(sizeof(*pMemArray)); START_STRUCT(pMemArray); if (fDmiExposeMemoryTable) pMemArray->header.u8Type = 16; /* Physical Memory Array */ else pMemArray->header.u8Type = 126; /* inactive structure */ pMemArray->header.u8Length = sizeof(*pMemArray); pMemArray->header.u16Handle = 0x0005; pMemArray->u8Location = 0x03; /* Motherboard */ pMemArray->u8Use = 0x03; /* System memory */ pMemArray->u8MemErrorCorrection = 0x01; /* Other */ pMemArray->u32MaxCapacity = (uint32_t)(u64RamSize / _1K); /* RAM size in K */ pMemArray->u16MemErrorHandle = 0xfffe; /* No error info structure */ pMemArray->u16NumberOfMemDevices = 1; TERM_STRUCT; /*************************************** * DMI Memory Device (Type 17) * ***************************************/ PDMIMEMORYDEV pMemDev = (PDMIMEMORYDEV)pszStr; CHECKSIZE(sizeof(*pMemDev)); START_STRUCT(pMemDev); if (fDmiExposeMemoryTable) pMemDev->header.u8Type = 17; /* Memory Device */ else pMemDev->header.u8Type = 126; /* inactive structure */ pMemDev->header.u8Length = sizeof(*pMemDev); pMemDev->header.u16Handle = 0x0006; pMemDev->u16PhysMemArrayHandle = 0x0005; /* handle of array we belong to */ pMemDev->u16MemErrHandle = 0xfffe; /* system doesn't provide this information */ pMemDev->u16TotalWidth = 0xffff; /* Unknown */ pMemDev->u16DataWidth = 0xffff; /* Unknown */ int16_t u16RamSizeM = (uint16_t)(u64RamSize / _1M); if (u16RamSizeM == 0) u16RamSizeM = 0x400; /* 1G */ pMemDev->u16Size = u16RamSizeM; /* RAM size */ pMemDev->u8FormFactor = 0x09; /* DIMM */ pMemDev->u8DeviceSet = 0x00; /* Not part of a device set */ READCFGSTRDEF(pMemDev->u8DeviceLocator, " ", "DIMM 0"); READCFGSTRDEF(pMemDev->u8BankLocator, " ", "Bank 0"); pMemDev->u8MemoryType = 0x03; /* DRAM */ pMemDev->u16TypeDetail = 0; /* Nothing special */ pMemDev->u16Speed = 1600; /* Unknown, shall be speed in MHz */ READCFGSTR(pMemDev->u8Manufacturer, DmiSystemVendor); READCFGSTRDEF(pMemDev->u8SerialNumber, " ", "00000000"); READCFGSTRDEF(pMemDev->u8AssetTag, " ", "00000000"); READCFGSTRDEF(pMemDev->u8PartNumber, " ", "00000000"); pMemDev->u8Attributes = 0; /* Unknown */ TERM_STRUCT; /***************************** * DMI OEM strings (Type 11) * *****************************/ PDMIOEMSTRINGS pOEMStrings = (PDMIOEMSTRINGS)pszStr; CHECKSIZE(sizeof(*pOEMStrings)); START_STRUCT(pOEMStrings); #ifdef VBOX_WITH_DMI_OEMSTRINGS pOEMStrings->header.u8Type = 0xb; /* OEM Strings */ #else pOEMStrings->header.u8Type = 126; /* inactive structure */ #endif pOEMStrings->header.u8Length = sizeof(*pOEMStrings); pOEMStrings->header.u16Handle = 0x0002; pOEMStrings->u8Count = 2; char szTmp[64]; RTStrPrintf(szTmp, sizeof(szTmp), "vboxVer_%u.%u.%u", RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild()); READCFGSTRDEF(pOEMStrings->u8VBoxVersion, "DmiOEMVBoxVer", szTmp); RTStrPrintf(szTmp, sizeof(szTmp), "vboxRev_%u", RTBldCfgRevision()); READCFGSTRDEF(pOEMStrings->u8VBoxRevision, "DmiOEMVBoxRev", szTmp); TERM_STRUCT; /************************************* * DMI OEM specific table (Type 128) * ************************************/ PDMIOEMSPECIFIC pOEMSpecific = (PDMIOEMSPECIFIC)pszStr; CHECKSIZE(sizeof(*pOEMSpecific)); START_STRUCT(pOEMSpecific); pOEMSpecific->header.u8Type = 0x80; /* OEM specific */ pOEMSpecific->header.u8Length = sizeof(*pOEMSpecific); pOEMSpecific->header.u16Handle = 0x0008; /* Just next free handle */ pOEMSpecific->u32CpuFreqKHz = RT_H2LE_U32((uint32_t)((uint64_t)TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns)) / 1000)); TERM_STRUCT; /* End-of-table marker - includes padding to account for fixed table size. */ PDMIHDR pEndOfTable = (PDMIHDR)pszStr; pszStr = (char *)(pEndOfTable + 1); pEndOfTable->u8Type = 0x7f; pEndOfTable->u8Length = sizeof(*pEndOfTable); pEndOfTable->u16Handle = 0xFEFF; *pcbDmiTables = ((uintptr_t)pszStr - (uintptr_t)pTable) + 2; /* We currently plant 10 DMI tables. Update this if tables number changed. */ *pcNumDmiTables = 10; /* If more fields are added here, fix the size check in READCFGSTR */ /* Success! */ break; } #undef READCFGSTR #undef READCFGINT #undef CHECKSIZE return VINF_SUCCESS; }
/** * MSR read handler for Hyper-V. * * @returns Strict VBox status code like CPUMQueryGuestMsr(). * @retval VINF_CPUM_R3_MSR_READ * @retval VERR_CPUM_RAISE_GP_0 * * @param pVCpu Pointer to the VMCPU. * @param idMsr The MSR being read. * @param pRange The range this MSR belongs to. * @param puValue Where to store the MSR value read. */ VMM_INT_DECL(VBOXSTRICTRC) gimHvReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue) { NOREF(pRange); PVM pVM = pVCpu->CTX_SUFF(pVM); PGIMHV pHv = &pVM->gim.s.u.Hv; switch (idMsr) { case MSR_GIM_HV_TIME_REF_COUNT: { /* Hyper-V reports the time in 100 ns units (10 MHz). */ uint64_t u64Tsc = TMCpuTickGet(pVCpu); uint64_t u64TscHz = pHv->cTscTicksPerSecond; uint64_t u64Tsc100Ns = u64TscHz / UINT64_C(10000000); /* 100 ns */ *puValue = (u64Tsc / u64Tsc100Ns); return VINF_SUCCESS; } case MSR_GIM_HV_VP_INDEX: *puValue = pVCpu->idCpu; return VINF_SUCCESS; case MSR_GIM_HV_TPR: PDMApicReadMSR(pVM, pVCpu->idCpu, 0x80, puValue); return VINF_SUCCESS; case MSR_GIM_HV_EOI: PDMApicReadMSR(pVM, pVCpu->idCpu, 0x0B, puValue); return VINF_SUCCESS; case MSR_GIM_HV_ICR: PDMApicReadMSR(pVM, pVCpu->idCpu, 0x30, puValue); return VINF_SUCCESS; case MSR_GIM_HV_GUEST_OS_ID: *puValue = pHv->u64GuestOsIdMsr; return VINF_SUCCESS; case MSR_GIM_HV_HYPERCALL: *puValue = pHv->u64HypercallMsr; return VINF_SUCCESS; case MSR_GIM_HV_REF_TSC: *puValue = pHv->u64TscPageMsr; return VINF_SUCCESS; case MSR_GIM_HV_TSC_FREQ: *puValue = TMCpuTicksPerSecond(pVM); return VINF_SUCCESS; case MSR_GIM_HV_APIC_FREQ: { int rc = PDMApicGetTimerFreq(pVM, puValue); if (RT_FAILURE(rc)) return VERR_CPUM_RAISE_GP_0; return VINF_SUCCESS; } case MSR_GIM_HV_RESET: *puValue = 0; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_CTL: *puValue = pHv->uCrashCtl; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P0: *puValue = pHv->uCrashP0; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P1: *puValue = pHv->uCrashP1; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P2: *puValue = pHv->uCrashP2; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P3: *puValue = pHv->uCrashP3; return VINF_SUCCESS; case MSR_GIM_HV_CRASH_P4: *puValue = pHv->uCrashP4; return VINF_SUCCESS; default: { #ifdef IN_RING3 static uint32_t s_cTimes = 0; if (s_cTimes++ < 20) LogRel(("GIM: HyperV: Unknown/invalid RdMsr (%#x) -> #GP(0)\n", idMsr)); #endif LogFunc(("Unknown/invalid RdMsr (%#RX32) -> #GP(0)\n", idMsr)); break; } } return VERR_CPUM_RAISE_GP_0; }