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