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