1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#include <config.h> 8#include <machine/io.h> 9#include <arch/kernel/apic.h> 10#include <arch/model/statedata.h> 11#include <linker.h> 12#include <plat/machine/pic.h> 13#include <plat/machine/ioapic.h> 14#include <plat/machine.h> 15 16#include <plat/machine/intel-vtd.h> 17 18BOOT_CODE bool_t platAddDevices(void) 19{ 20 /* remove the MSI region as poking at this is undefined and may allow for 21 * the user to generate arbitrary MSI interrupts. Only need to consider 22 * this if it would actually be in the user device region */ 23 if (CONFIG_PADDR_USER_DEVICE_TOP > 0xFFFFFFF8) { 24 if (!reserve_region((p_region_t) { 25 (word_t)0xFFFFFFF8, (word_t)0xFFFFFFF8 + 8 26 })) { 27 return false; 28 } 29 } 30 return true; 31} 32 33/* ============================== timer ============================== */ 34 35#define TSC_FREQ_RETRIES 10 36 37BOOT_CODE static inline uint32_t measure_tsc_khz(void) 38{ 39 /* The frequency is repeatedly measured until the number of TSC 40 * ticks in the pit wraparound interval (~50ms) fits in 32 bits. 41 * On bare metal, this should succeed immediately, since x86 42 * guarantees the number of TSC ticks in a second can be stored 43 * in 32 bits. When running in a simulator, it's possible for 44 * the emulation (or not) of the PIT and TSC to occasionally 45 * allow too many TSC ticks per PIT wraparound. This loop 46 * repeatedly measures the TSC ticks per PIT wraparound under 47 * the expectation that it will eventually yield a sensible 48 * result. 49 */ 50 for (int i = 0; i < TSC_FREQ_RETRIES; i++) { 51 52 /* read tsc */ 53 uint64_t old_ticks = x86_rdtsc(); 54 55 /* measure how many tsc cycles pass while PIT wraps around */ 56 pit_wait_wraparound(); 57 58 uint64_t new_ticks = x86_rdtsc(); 59 60 uint64_t diff = new_ticks - old_ticks; 61 62 if ((uint32_t)diff == diff && new_ticks > old_ticks) { 63 return (uint32_t)diff / PIT_WRAPAROUND_MS; 64 } 65 66 printf("warning: TSC frequency too high (%d retries remaining)\n", 67 TSC_FREQ_RETRIES - i - 1); 68 } 69 70 fail("TSC frequency too high"); 71 72 /* should never get here */ 73 return 0; 74} 75 76BOOT_CODE uint32_t tsc_init(void) 77{ 78 79 x86_cpu_identity_t *model_info = x86_cpuid_get_model_info(); 80 uint32_t valid_models[] = { 81 NEHALEM_1_MODEL_ID, NEHALEM_2_MODEL_ID, NEHALEM_3_MODEL_ID, 82 SANDY_BRIDGE_1_MODEL_ID, SANDY_BRIDGE_2_MODEL_ID, 83 HASWELL_1_MODEL_ID, HASWELL_2_MODEL_ID, HASWELL_3_MODEL_ID, HASWELL_4_MODEL_ID, 84 IVY_BRIDGE_1_MODEL_ID, IVY_BRIDGE_2_MODEL_ID, IVY_BRIDGE_3_MODEL_ID, 85 /* BROADWELL_1_MODEL_ID is an Atom that doesn't support the MSR */ 86 BROADWELL_2_MODEL_ID, BROADWELL_3_MODEL_ID, BROADWELL_4_MODEL_ID, BROADWELL_5_MODEL_ID, 87 SKYLAKE_1_MODEL_ID, SKYLAKE_2_MODEL_ID 88 }; 89 90 /* try to read the frequency from the platform info MSR */ 91 if (model_info->family == IA32_PREFETCHER_COMPATIBLE_FAMILIES_ID) { 92 for (int i = 0; i < ARRAY_SIZE(valid_models); i++) { 93 if (model_info->model == valid_models[i]) { 94 95 /* read tsc freq from the platform info msr. Under some environments such 96 * as KVM this MSR will cause a GP fault even though it should be there. 97 * As such we perform a 'safe' rdmsr, which will catch a GP fault and 98 * tells through .success whether or not one happened */ 99 rdmsr_safe_result_t info = x86_rdmsr_safe(IA32_PLATFORM_INFO_MSR); 100 if (info.success) { 101 uint32_t ratio = (((uint32_t) info.value) & 0xFF00) >> 8u; 102 /* Ignore hardware that claims a tsc frequency of zero */ 103 if (ratio != 0) { 104 /* Convert to MHz */ 105 if (model_info->model == NEHALEM_1_MODEL_ID || 106 model_info->model == NEHALEM_2_MODEL_ID || 107 model_info->model == NEHALEM_3_MODEL_ID) { 108 return ratio * 13333u / 100u; 109 } else { 110 return ratio * 100u; 111 } 112 } 113 } 114 /* We just found the matching model, so no point continuing the search */ 115 break; 116 } 117 } 118 } 119 120 /* otherwise use the pit to find out the tsc freq */ 121 pit_init(); 122 123 /* wait for pit to wraparound */ 124 pit_wait_wraparound(); 125 126 /* count tsc ticks per ms */ 127 uint32_t tsc_khz = measure_tsc_khz(); 128 129 /* finally, return mhz */ 130 return tsc_khz / 1000u; 131} 132