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