1/*
2 * Copyright (c) 2015 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <cpuid_internal.h>
11
12/*
13 * ===============================================================================
14 * library internal, global variables
15 * ===============================================================================
16 */
17
18///< maximum input value for basic CPUID information
19uint32_t cpuid_g_max_input_basic = 0;
20
21///< maximum input value for extended CPUID information
22uint32_t cpuid_g_max_input_extended = 0;
23
24///< the vendor of the core this code is executed
25cpuid_vendor_t cpuid_g_vendor = CPUID_VENDOR_UNKNOWN;
26
27///< function pointer table for vendor specific handlers
28struct cpuid_functions cpuid_fn;
29
30///< cpu cache names in readable representation
31char *cpuid_cache_names[4][4] = {
32    {""},
33    {"", "l1i","l1d","l1"},
34    {"", "l2i","l2d","l2"},
35    {"", "l3i","l3d","l3"}
36};
37
38/*
39 * ===============================================================================
40 * library initialization
41 * ===============================================================================
42 */
43
44/**
45 * \brief initializes the cpuid library to handle vendor specific stuff
46 */
47errval_t cpuid_init(void)
48{
49    cpuid_g_vendor = cpuid_vendor();
50
51    assert(cpuid_fn.proc_max_input_basic);
52    cpuid_g_max_input_basic = cpuid_fn.proc_max_input_basic();
53    cpuid_g_max_input_extended = cpuid_fn.proc_max_input_extended();
54
55    CPUID_PRINTF("initializing for %s CPU. Max input = [0x%08" PRIx32 " / 0x%08"
56                 PRIx32"]\n", cpuid_vendor_string(), cpuid_g_max_input_basic,
57                 cpuid_g_max_input_extended);
58
59    return SYS_ERR_OK;
60}
61
62
63/*
64 * ===============================================================================
65 * vendor information
66 * ===============================================================================
67 */
68
69/**
70 * \brief reads the CPU vendor string and returns the vendor information
71 *
72 * \return CPU vendor information CPUID_VENDOR_*
73 *
74 * This function also updates the function table pointer based on the read values
75 */
76cpuid_vendor_t cpuid_vendor(void)
77{
78    if (cpuid_intel_check_vendor()) {
79        cpuid_intel_set_handlers(&cpuid_fn);
80        return CPUID_VENDOR_INTEL;
81    } else if (cpuid_amd_check_vendor()) {
82        cpuid_amd_set_handlers(&cpuid_fn);
83        return CPUID_VENDOR_AMD;
84    }
85    return CPUID_VENDOR_UNKNOWN;
86}
87
88/**
89 * \brief returns a string representation of the vendor
90 *
91 * \return string representation of the vendor
92 */
93char *cpuid_vendor_string(void)
94{
95    switch(cpuid_g_vendor) {
96        case CPUID_VENDOR_AMD :
97            return "amd";
98            break;
99        case CPUID_VENDOR_INTEL :
100            return "intel";
101            break;
102        default:
103            return "unknown";
104    }
105}
106
107
108/*
109 * ===============================================================================
110 * basic processor information
111 * ===============================================================================
112 */
113
114/**
115 * \brief obtains the family information from the CPU
116 *
117 * \param memory to fill in the family information
118 */
119errval_t cpuid_proc_family(struct cpuid_proc_family *fmly)
120{
121    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
122        return CPUID_ERR_UNKNOWN_VENDOR;
123    }
124    assert(cpuid_fn.proc_family);
125
126    return cpuid_fn.proc_family(fmly);
127}
128
129/**
130 * \brief obtains the processor name
131 *
132 * \param buf   buffer where to store the processor name string
133 * \param len   length of the buffer
134 */
135errval_t cpuid_proc_name(char *buf, size_t len)
136{
137    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
138        return CPUID_ERR_UNKNOWN_VENDOR;
139    }
140    assert(cpuid_fn.proc_name);
141
142    return cpuid_fn.proc_name(buf, len);
143}
144
145/**
146 * \brief returns the maximum input value for basic CPUID functions
147 *
148 * \return integer representing the maximum input
149 */
150uint32_t cpuid_proc_max_input_basic(void)
151{
152    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
153        return 0;
154    }
155    cpuid_g_max_input_basic = cpuid_fn.proc_max_input_basic();
156
157    return cpuid_g_max_input_basic;
158}
159
160/**
161 * \brief returns the maximum input value for extended CPUID functions
162 *
163 * \return integer representing the maximum input
164 */
165uint32_t cpuid_proc_max_input_extended(void)
166{
167    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
168        return 0;
169    }
170    cpuid_g_max_input_extended = cpuid_fn.proc_max_input_extended();
171
172    return cpuid_g_max_input_extended;
173}
174
175/**
176 * \brief obtains the processor frequency information
177 *
178 * \param fi returned frequency information
179 *
180 * \returns SYS_ERR_OK on success
181 *          CPUID_ERR_*on failure
182 */
183errval_t cpuid_frequency_info(struct cpuid_freqinfo *fi)
184{
185    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
186        return 0;
187    }
188    assert(cpuid_fn.frequency_info);
189
190    return cpuid_fn.frequency_info(fi);
191}
192
193/*
194 * ===============================================================================
195 * cache topology information
196 * ===============================================================================
197 */
198
199/**
200 * \brief calculates the system coherency size (cacheline size)
201 *
202 * \returns the coherency size of the core in bytes
203 */
204uint16_t cpuid_system_coherency_size(void)
205{
206    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
207        return CPUID_ERR_UNKNOWN_VENDOR;
208    }
209    assert(cpuid_fn.cache_line_size);
210
211    return cpuid_fn.cache_line_size();
212}
213
214/**
215 * \brief obtains cache parameters for a given index
216 *
217 * \param ci   memory to be filled in with information
218 * \param idx  index of the cache to obtain information for
219 *
220 * \return  SYS_ERR_OK  on success
221 *          CPUID_ERR_NO_SUCH_INDEX if there is no cache associated with the index
222 */
223errval_t cpuid_cache_info(struct cpuid_cacheinfo *ci, uint32_t idx)
224{
225    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
226        return CPUID_ERR_UNKNOWN_VENDOR;
227    }
228    assert(cpuid_fn.cache_info);
229
230    return cpuid_fn.cache_info(ci, idx);
231}
232
233/**
234 * \brief returns a string representation of the cache type
235 *
236 * \param ct    type of the cache
237 *
238 * \return cache name string
239 */
240char *cpuid_cache_type_string(cpuid_cachetype_t ct)
241{
242    switch(ct) {
243        case CPUID_CACHE_TYPE_DATA:
244            return "data";
245            break;
246        case CPUID_CACHE_TYPE_INSTR:
247            return "instr";
248            break;
249        case CPUID_CACHE_TYPE_UNIFIED:
250            return "unified";
251            break;
252        default:
253            return "invalid";
254            break;
255    }
256}
257
258
259/*
260 * ===============================================================================
261 * TLB information
262 * ===============================================================================
263 */
264
265/**
266 * \brief obtains the TLB topology information
267 *
268 * \param ti
269 * \param idx
270 *
271 * \returns SYS_ERR_OK on success
272 *          CPUID_ERR_* on failure
273 */
274errval_t cpuid_tlb_info(struct cpuid_tlbinfo *ti, uint32_t idx)
275{
276    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
277        return CPUID_ERR_UNKNOWN_VENDOR;
278    }
279    assert(cpuid_fn.tlb_info);
280
281    return cpuid_fn.tlb_info(ti, idx);
282}
283
284/*
285 * ===============================================================================
286 * thread and topology information
287 * ===============================================================================
288 */
289
290
291/**
292 * \brief obtains the topology information for a given thread
293 *
294 * \param ti    returns the thread information
295 *
296 * \return  SYS_ERR_OK on success
297 *          CPUID_ERR_* on failure
298 */
299errval_t cpuid_thread_info(struct cpuid_threadinfo *ti)
300{
301    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
302        return CPUID_ERR_UNKNOWN_VENDOR;
303    }
304    assert(cpuid_fn.thread_info);
305
306    return cpuid_fn.thread_info(ti);
307}
308
309/**
310 * \brief obtains topology information from the CPU
311 *
312 * \param topo  pointer to store the information
313 * \param idx   topology level index
314 *
315 * \return SYS_ERR_OK on success
316 *         CPUID_ERR_* on failure
317 */
318errval_t cpuid_topology_info(struct cpuid_topologyinfo *topo, uint8_t idx)
319{
320    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
321        return CPUID_ERR_UNKNOWN_VENDOR;
322    }
323    assert(cpuid_fn.topology_info);
324
325    return cpuid_fn.topology_info(topo, idx);
326}
327
328
329/*
330 * ===============================================================================
331 * feature information
332 * ===============================================================================
333 */
334/**
335 * \brief obtains the CPU's feature information
336 *
337 * \param fi    structure to be filled in
338 *
339 * \return  SYS_ERR_OK on success
340 *          CPUID_ERR_* on failure
341 */
342errval_t cpuid_feature_info(struct cpuid_featureinfo *fi)
343{
344    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
345        return CPUID_ERR_UNKNOWN_VENDOR;
346    }
347    assert(cpuid_fn.feature_info);
348
349    return cpuid_fn.feature_info(fi);
350}
351
352
353/*
354 * ===============================================================================
355 * address space information
356 * ===============================================================================
357 */
358
359/**
360 * \brief obtains the address space size information of the CPU
361 *
362 * \param ai  struct to be filled in with adderss space information
363 *
364 * \returns SYS_ERR_OK on susccess
365 *          CPUID_ERR_* on failure
366 */
367errval_t cpuid_address_space_info(struct cpuid_adressspaceinfo *ai)
368{
369    if (cpuid_g_vendor == CPUID_VENDOR_UNKNOWN) {
370        return CPUID_ERR_UNKNOWN_VENDOR;
371    }
372    assert(cpuid_fn.address_space_info);
373
374    return cpuid_fn.address_space_info(ai);
375}
376