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