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
platAddDevices(void)18 BOOT_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
measure_tsc_khz(void)37 BOOT_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
tsc_init(void)76 BOOT_CODE uint32_t tsc_init(void)
77 {
78
79 #if CONFIG_PC99_TSC_FREQUENCY > 0
80 return CONFIG_PC99_TSC_FREQUENCY / 1000000;
81 #endif
82
83 x86_cpu_identity_t *model_info = x86_cpuid_get_model_info();
84 uint32_t valid_models[] = {
85 NEHALEM_1_MODEL_ID, NEHALEM_2_MODEL_ID, NEHALEM_3_MODEL_ID,
86 SANDY_BRIDGE_1_MODEL_ID, SANDY_BRIDGE_2_MODEL_ID,
87 HASWELL_1_MODEL_ID, HASWELL_2_MODEL_ID, HASWELL_3_MODEL_ID, HASWELL_4_MODEL_ID,
88 IVY_BRIDGE_1_MODEL_ID, IVY_BRIDGE_2_MODEL_ID, IVY_BRIDGE_3_MODEL_ID,
89 /* BROADWELL_1_MODEL_ID is an Atom that doesn't support the MSR */
90 BROADWELL_2_MODEL_ID, BROADWELL_3_MODEL_ID, BROADWELL_4_MODEL_ID, BROADWELL_5_MODEL_ID,
91 SKYLAKE_1_MODEL_ID, SKYLAKE_2_MODEL_ID
92 };
93
94 /* try to read the frequency from the platform info MSR */
95 if (model_info->family == IA32_PREFETCHER_COMPATIBLE_FAMILIES_ID) {
96 for (int i = 0; i < ARRAY_SIZE(valid_models); i++) {
97 if (model_info->model == valid_models[i]) {
98
99 /* read tsc freq from the platform info msr. Under some environments such
100 * as KVM this MSR will cause a GP fault even though it should be there.
101 * As such we perform a 'safe' rdmsr, which will catch a GP fault and
102 * tells through .success whether or not one happened */
103 rdmsr_safe_result_t info = x86_rdmsr_safe(IA32_PLATFORM_INFO_MSR);
104 if (info.success) {
105 uint32_t ratio = (((uint32_t) info.value) & 0xFF00) >> 8u;
106 /* Ignore hardware that claims a tsc frequency of zero */
107 if (ratio != 0) {
108 /* Convert to MHz */
109 if (model_info->model == NEHALEM_1_MODEL_ID ||
110 model_info->model == NEHALEM_2_MODEL_ID ||
111 model_info->model == NEHALEM_3_MODEL_ID) {
112 return ratio * 13333u / 100u;
113 } else {
114 return ratio * 100u;
115 }
116 }
117 }
118 /* We just found the matching model, so no point continuing the search */
119 break;
120 }
121 }
122 }
123
124 /* otherwise use the pit to find out the tsc freq */
125 pit_init();
126
127 /* wait for pit to wraparound */
128 pit_wait_wraparound();
129
130 /* count tsc ticks per ms */
131 uint32_t tsc_khz = measure_tsc_khz();
132
133 /* finally, return mhz */
134 return tsc_khz / 1000u;
135 }
136