blob: 269043dedc13c976fd77f2fb9e6ee0cbbe9f1687 [file] [log] [blame]
wdenk591dda52002-11-18 00:14:45 +00001/*
Graeme Russ45fc1d82011-04-13 19:43:26 +10002 * (C) Copyright 2008-2011
3 * Graeme Russ, <graeme.russ@gmail.com>
4 *
wdenk591dda52002-11-18 00:14:45 +00005 * (C) Copyright 2002
Albert ARIBAUD60fbc8d2011-08-04 18:45:45 +02006 * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
wdenk57b2d802003-06-27 21:31:46 +00007 *
wdenk591dda52002-11-18 00:14:45 +00008 * (C) Copyright 2002
9 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
10 * Marius Groeger <mgroeger@sysgo.de>
11 *
12 * (C) Copyright 2002
13 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
14 * Alex Zuepke <azu@sysgo.de>
15 *
Bin Meng035c1d22014-11-09 22:18:56 +080016 * Part of this file is adapted from coreboot
17 * src/arch/x86/lib/cpu.c
18 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +020019 * SPDX-License-Identifier: GPL-2.0+
wdenk591dda52002-11-18 00:14:45 +000020 */
21
wdenk591dda52002-11-18 00:14:45 +000022#include <common.h>
23#include <command.h>
Bin Mengf967f9a2015-06-17 11:15:36 +080024#include <dm.h>
Simon Glass463fac22014-10-10 08:21:55 -060025#include <errno.h>
26#include <malloc.h>
Bin Menga4559642016-06-08 05:07:38 -070027#include <syscon.h>
Stefan Reinauer2acf8482012-12-02 04:49:50 +000028#include <asm/control_regs.h>
Bin Meng1c9da372016-05-11 07:45:01 -070029#include <asm/coreboot_tables.h>
Simon Glass463fac22014-10-10 08:21:55 -060030#include <asm/cpu.h>
Bin Mengf967f9a2015-06-17 11:15:36 +080031#include <asm/lapic.h>
Simon Glass8dda5872016-03-11 22:07:11 -070032#include <asm/microcode.h>
Bin Mengf967f9a2015-06-17 11:15:36 +080033#include <asm/mp.h>
Bin Meng1141fcf2016-05-11 07:45:00 -070034#include <asm/mrccache.h>
Bin Mengc45a93b2015-07-06 16:31:30 +080035#include <asm/msr.h>
36#include <asm/mtrr.h>
Simon Glass9f0afe72014-11-12 22:42:26 -070037#include <asm/post.h>
Graeme Russ25391d12011-02-12 15:11:30 +110038#include <asm/processor.h>
Graeme Russ93efcb22011-02-12 15:11:32 +110039#include <asm/processor-flags.h>
Graeme Russ278638d2008-12-07 10:29:02 +110040#include <asm/interrupt.h>
Bin Mengf17cea62015-04-24 18:10:04 +080041#include <asm/tables.h>
Gabe Black6ed18882011-11-16 23:32:50 +000042#include <linux/compiler.h>
wdenk591dda52002-11-18 00:14:45 +000043
Bin Meng035c1d22014-11-09 22:18:56 +080044DECLARE_GLOBAL_DATA_PTR;
45
Graeme Russ45fc1d82011-04-13 19:43:26 +100046/*
47 * Constructor for a conventional segment GDT (or LDT) entry
48 * This is a macro so it can be used in initialisers
49 */
Graeme Russ1ce0a602010-10-07 20:03:21 +110050#define GDT_ENTRY(flags, base, limit) \
51 ((((base) & 0xff000000ULL) << (56-24)) | \
52 (((flags) & 0x0000f0ffULL) << 40) | \
53 (((limit) & 0x000f0000ULL) << (48-16)) | \
54 (((base) & 0x00ffffffULL) << 16) | \
55 (((limit) & 0x0000ffffULL)))
56
Graeme Russ1ce0a602010-10-07 20:03:21 +110057struct gdt_ptr {
58 u16 len;
59 u32 ptr;
Graeme Russfdee8b12011-11-08 02:33:13 +000060} __packed;
Graeme Russ1ce0a602010-10-07 20:03:21 +110061
Bin Meng035c1d22014-11-09 22:18:56 +080062struct cpu_device_id {
63 unsigned vendor;
64 unsigned device;
65};
66
67struct cpuinfo_x86 {
68 uint8_t x86; /* CPU family */
69 uint8_t x86_vendor; /* CPU vendor */
70 uint8_t x86_model;
71 uint8_t x86_mask;
72};
73
74/*
75 * List of cpu vendor strings along with their normalized
76 * id values.
77 */
Simon Glasse79d2c62016-03-06 19:27:57 -070078static const struct {
Bin Meng035c1d22014-11-09 22:18:56 +080079 int vendor;
80 const char *name;
81} x86_vendors[] = {
82 { X86_VENDOR_INTEL, "GenuineIntel", },
83 { X86_VENDOR_CYRIX, "CyrixInstead", },
84 { X86_VENDOR_AMD, "AuthenticAMD", },
85 { X86_VENDOR_UMC, "UMC UMC UMC ", },
86 { X86_VENDOR_NEXGEN, "NexGenDriven", },
87 { X86_VENDOR_CENTAUR, "CentaurHauls", },
88 { X86_VENDOR_RISE, "RiseRiseRise", },
89 { X86_VENDOR_TRANSMETA, "GenuineTMx86", },
90 { X86_VENDOR_TRANSMETA, "TransmetaCPU", },
91 { X86_VENDOR_NSC, "Geode by NSC", },
92 { X86_VENDOR_SIS, "SiS SiS SiS ", },
93};
94
95static const char *const x86_vendor_name[] = {
96 [X86_VENDOR_INTEL] = "Intel",
97 [X86_VENDOR_CYRIX] = "Cyrix",
98 [X86_VENDOR_AMD] = "AMD",
99 [X86_VENDOR_UMC] = "UMC",
100 [X86_VENDOR_NEXGEN] = "NexGen",
101 [X86_VENDOR_CENTAUR] = "Centaur",
102 [X86_VENDOR_RISE] = "Rise",
103 [X86_VENDOR_TRANSMETA] = "Transmeta",
104 [X86_VENDOR_NSC] = "NSC",
105 [X86_VENDOR_SIS] = "SiS",
106};
107
Graeme Russ14d37612011-12-29 21:45:33 +1100108static void load_ds(u32 segment)
Graeme Russ1ce0a602010-10-07 20:03:21 +1100109{
Graeme Russ14d37612011-12-29 21:45:33 +1100110 asm volatile("movl %0, %%ds" : : "r" (segment * X86_GDT_ENTRY_SIZE));
111}
112
113static void load_es(u32 segment)
114{
115 asm volatile("movl %0, %%es" : : "r" (segment * X86_GDT_ENTRY_SIZE));
116}
117
118static void load_fs(u32 segment)
119{
120 asm volatile("movl %0, %%fs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
121}
122
123static void load_gs(u32 segment)
124{
125 asm volatile("movl %0, %%gs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
126}
127
128static void load_ss(u32 segment)
129{
130 asm volatile("movl %0, %%ss" : : "r" (segment * X86_GDT_ENTRY_SIZE));
131}
Graeme Russ1ce0a602010-10-07 20:03:21 +1100132
Graeme Russ14d37612011-12-29 21:45:33 +1100133static void load_gdt(const u64 *boot_gdt, u16 num_entries)
134{
135 struct gdt_ptr gdt;
136
Simon Glass9fc71c12014-11-14 20:56:29 -0700137 gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;
Graeme Russ14d37612011-12-29 21:45:33 +1100138 gdt.ptr = (u32)boot_gdt;
Graeme Russ1ce0a602010-10-07 20:03:21 +1100139
Graeme Russ14d37612011-12-29 21:45:33 +1100140 asm volatile("lgdtl %0\n" : : "m" (gdt));
Graeme Russ1ce0a602010-10-07 20:03:21 +1100141}
142
Simon Glass0e27b872015-08-10 20:44:32 -0600143void arch_setup_gd(gd_t *new_gd)
Graeme Russ35368962011-12-31 22:58:15 +1100144{
Simon Glass0e27b872015-08-10 20:44:32 -0600145 u64 *gdt_addr;
146
Simon Glass9909bf32015-08-10 20:44:31 -0600147 gdt_addr = new_gd->arch.gdt;
148
Bin Mengbf6edd62015-10-07 20:19:09 -0700149 /*
150 * CS: code, read/execute, 4 GB, base 0
151 *
152 * Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS
153 */
154 gdt_addr[X86_GDT_ENTRY_UNUSED] = GDT_ENTRY(0xc09b, 0, 0xfffff);
Graeme Russ35368962011-12-31 22:58:15 +1100155 gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
156
157 /* DS: data, read/write, 4 GB, base 0 */
158 gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);
159
160 /* FS: data, read/write, 4 GB, base (Global Data Pointer) */
Simon Glass9909bf32015-08-10 20:44:31 -0600161 new_gd->arch.gd_addr = new_gd;
Simon Glass2c5ca202012-12-13 20:48:42 +0000162 gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0xc093,
Simon Glass9909bf32015-08-10 20:44:31 -0600163 (ulong)&new_gd->arch.gd_addr, 0xfffff);
Graeme Russ35368962011-12-31 22:58:15 +1100164
165 /* 16-bit CS: code, read/execute, 64 kB, base 0 */
Simon Glass9fc71c12014-11-14 20:56:29 -0700166 gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);
Graeme Russ35368962011-12-31 22:58:15 +1100167
168 /* 16-bit DS: data, read/write, 64 kB, base 0 */
Simon Glass9fc71c12014-11-14 20:56:29 -0700169 gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);
170
171 gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);
172 gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);
Graeme Russ35368962011-12-31 22:58:15 +1100173
174 load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
175 load_ds(X86_GDT_ENTRY_32BIT_DS);
176 load_es(X86_GDT_ENTRY_32BIT_DS);
177 load_gs(X86_GDT_ENTRY_32BIT_DS);
178 load_ss(X86_GDT_ENTRY_32BIT_DS);
179 load_fs(X86_GDT_ENTRY_32BIT_FS);
180}
181
Bin Menga3c9fb02015-06-07 11:33:13 +0800182#ifdef CONFIG_HAVE_FSP
183/*
184 * Setup FSP execution environment GDT
185 *
186 * Per Intel FSP external architecture specification, before calling any FSP
187 * APIs, we need make sure the system is in flat 32-bit mode and both the code
188 * and data selectors should have full 4GB access range. Here we reuse the one
189 * we used in arch/x86/cpu/start16.S, and reload the segement registers.
190 */
191void setup_fsp_gdt(void)
192{
193 load_gdt((const u64 *)(gdt_rom + CONFIG_RESET_SEG_START), 4);
194 load_ds(X86_GDT_ENTRY_32BIT_DS);
195 load_ss(X86_GDT_ENTRY_32BIT_DS);
196 load_es(X86_GDT_ENTRY_32BIT_DS);
197 load_fs(X86_GDT_ENTRY_32BIT_DS);
198 load_gs(X86_GDT_ENTRY_32BIT_DS);
199}
200#endif
201
Gabe Black846d08e2012-10-20 12:33:10 +0000202int __weak x86_cleanup_before_linux(void)
203{
Simon Glassbcc28da2013-04-17 16:13:35 +0000204#ifdef CONFIG_BOOTSTAGE_STASH
Simon Glass5322d622015-03-02 17:04:37 -0700205 bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR,
Simon Glassbcc28da2013-04-17 16:13:35 +0000206 CONFIG_BOOTSTAGE_STASH_SIZE);
207#endif
208
Gabe Black846d08e2012-10-20 12:33:10 +0000209 return 0;
210}
211
Bin Meng035c1d22014-11-09 22:18:56 +0800212/*
213 * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
214 * by the fact that they preserve the flags across the division of 5/2.
215 * PII and PPro exhibit this behavior too, but they have cpuid available.
216 */
217
218/*
219 * Perform the Cyrix 5/2 test. A Cyrix won't change
220 * the flags, while other 486 chips will.
221 */
222static inline int test_cyrix_52div(void)
223{
224 unsigned int test;
225
226 __asm__ __volatile__(
227 "sahf\n\t" /* clear flags (%eax = 0x0005) */
228 "div %b2\n\t" /* divide 5 by 2 */
229 "lahf" /* store flags into %ah */
230 : "=a" (test)
231 : "0" (5), "q" (2)
232 : "cc");
233
234 /* AH is 0x02 on Cyrix after the divide.. */
235 return (unsigned char) (test >> 8) == 0x02;
236}
237
238/*
239 * Detect a NexGen CPU running without BIOS hypercode new enough
240 * to have CPUID. (Thanks to Herbert Oppmann)
241 */
242
243static int deep_magic_nexgen_probe(void)
244{
245 int ret;
246
247 __asm__ __volatile__ (
248 " movw $0x5555, %%ax\n"
249 " xorw %%dx,%%dx\n"
250 " movw $2, %%cx\n"
251 " divw %%cx\n"
252 " movl $0, %%eax\n"
253 " jnz 1f\n"
254 " movl $1, %%eax\n"
255 "1:\n"
256 : "=a" (ret) : : "cx", "dx");
257 return ret;
258}
259
260static bool has_cpuid(void)
261{
262 return flag_is_changeable_p(X86_EFLAGS_ID);
263}
264
Bin Meng47eac042015-01-22 11:29:40 +0800265static bool has_mtrr(void)
266{
267 return cpuid_edx(0x00000001) & (1 << 12) ? true : false;
268}
269
Bin Meng035c1d22014-11-09 22:18:56 +0800270static int build_vendor_name(char *vendor_name)
271{
272 struct cpuid_result result;
273 result = cpuid(0x00000000);
274 unsigned int *name_as_ints = (unsigned int *)vendor_name;
275
276 name_as_ints[0] = result.ebx;
277 name_as_ints[1] = result.edx;
278 name_as_ints[2] = result.ecx;
279
280 return result.eax;
281}
282
283static void identify_cpu(struct cpu_device_id *cpu)
284{
285 char vendor_name[16];
286 int i;
287
288 vendor_name[0] = '\0'; /* Unset */
Simon Glass14a89a92014-11-12 20:27:55 -0700289 cpu->device = 0; /* fix gcc 4.4.4 warning */
Bin Meng035c1d22014-11-09 22:18:56 +0800290
291 /* Find the id and vendor_name */
292 if (!has_cpuid()) {
293 /* Its a 486 if we can modify the AC flag */
294 if (flag_is_changeable_p(X86_EFLAGS_AC))
295 cpu->device = 0x00000400; /* 486 */
296 else
297 cpu->device = 0x00000300; /* 386 */
298 if ((cpu->device == 0x00000400) && test_cyrix_52div()) {
299 memcpy(vendor_name, "CyrixInstead", 13);
300 /* If we ever care we can enable cpuid here */
301 }
302 /* Detect NexGen with old hypercode */
303 else if (deep_magic_nexgen_probe())
304 memcpy(vendor_name, "NexGenDriven", 13);
305 }
306 if (has_cpuid()) {
307 int cpuid_level;
308
309 cpuid_level = build_vendor_name(vendor_name);
310 vendor_name[12] = '\0';
311
312 /* Intel-defined flags: level 0x00000001 */
313 if (cpuid_level >= 0x00000001) {
314 cpu->device = cpuid_eax(0x00000001);
315 } else {
316 /* Have CPUID level 0 only unheard of */
317 cpu->device = 0x00000400;
318 }
319 }
320 cpu->vendor = X86_VENDOR_UNKNOWN;
321 for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) {
322 if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) {
323 cpu->vendor = x86_vendors[i].vendor;
324 break;
325 }
326 }
327}
328
329static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms)
330{
331 c->x86 = (tfms >> 8) & 0xf;
332 c->x86_model = (tfms >> 4) & 0xf;
333 c->x86_mask = tfms & 0xf;
334 if (c->x86 == 0xf)
335 c->x86 += (tfms >> 20) & 0xff;
336 if (c->x86 >= 0x6)
337 c->x86_model += ((tfms >> 16) & 0xF) << 4;
338}
339
Simon Glass2f462fd2016-03-11 22:06:52 -0700340u32 cpu_get_family_model(void)
341{
342 return gd->arch.x86_device & 0x0fff0ff0;
343}
344
345u32 cpu_get_stepping(void)
346{
347 return gd->arch.x86_mask;
348}
349
Graeme Russ121931c2011-02-12 15:11:35 +1100350int x86_cpu_init_f(void)
wdenk591dda52002-11-18 00:14:45 +0000351{
Graeme Russ93efcb22011-02-12 15:11:32 +1100352 const u32 em_rst = ~X86_CR0_EM;
353 const u32 mp_ne_set = X86_CR0_MP | X86_CR0_NE;
354
Simon Glass2b6d80b2015-08-04 12:34:00 -0600355 if (ll_boot_init()) {
356 /* initialize FPU, reset EM, set MP and NE */
357 asm ("fninit\n" \
358 "movl %%cr0, %%eax\n" \
359 "andl %0, %%eax\n" \
360 "orl %1, %%eax\n" \
361 "movl %%eax, %%cr0\n" \
362 : : "i" (em_rst), "i" (mp_ne_set) : "eax");
363 }
wdenk57b2d802003-06-27 21:31:46 +0000364
Bin Meng035c1d22014-11-09 22:18:56 +0800365 /* identify CPU via cpuid and store the decoded info into gd->arch */
366 if (has_cpuid()) {
367 struct cpu_device_id cpu;
368 struct cpuinfo_x86 c;
369
370 identify_cpu(&cpu);
371 get_fms(&c, cpu.device);
372 gd->arch.x86 = c.x86;
373 gd->arch.x86_vendor = cpu.vendor;
374 gd->arch.x86_model = c.x86_model;
375 gd->arch.x86_mask = c.x86_mask;
376 gd->arch.x86_device = cpu.device;
Bin Meng47eac042015-01-22 11:29:40 +0800377
378 gd->arch.has_mtrr = has_mtrr();
Bin Meng035c1d22014-11-09 22:18:56 +0800379 }
Simon Glass5888bd22015-07-03 18:28:27 -0600380 /* Don't allow PCI region 3 to use memory in the 2-4GB memory hole */
381 gd->pci_ram_top = 0x80000000U;
Bin Meng035c1d22014-11-09 22:18:56 +0800382
Bin Mengc45a93b2015-07-06 16:31:30 +0800383 /* Configure fixed range MTRRs for some legacy regions */
384 if (gd->arch.has_mtrr) {
385 u64 mtrr_cap;
386
387 mtrr_cap = native_read_msr(MTRR_CAP_MSR);
388 if (mtrr_cap & MTRR_CAP_FIX) {
389 /* Mark the VGA RAM area as uncacheable */
Bin Meng268ca832015-07-15 16:23:38 +0800390 native_write_msr(MTRR_FIX_16K_A0000_MSR,
391 MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE),
392 MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE));
Bin Mengc45a93b2015-07-06 16:31:30 +0800393
Bin Meng268ca832015-07-15 16:23:38 +0800394 /*
395 * Mark the PCI ROM area as cacheable to improve ROM
396 * execution performance.
397 */
398 native_write_msr(MTRR_FIX_4K_C0000_MSR,
399 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
400 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
401 native_write_msr(MTRR_FIX_4K_C8000_MSR,
402 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
403 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
404 native_write_msr(MTRR_FIX_4K_D0000_MSR,
405 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
406 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
407 native_write_msr(MTRR_FIX_4K_D8000_MSR,
408 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
409 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
Bin Mengc45a93b2015-07-06 16:31:30 +0800410
411 /* Enable the fixed range MTRRs */
412 msr_setbits_64(MTRR_DEF_TYPE_MSR, MTRR_DEF_TYPE_FIX_EN);
413 }
414 }
415
Bin Mengb2e78982015-12-08 17:31:39 -0800416#ifdef CONFIG_I8254_TIMER
417 /* Set up the i8254 timer if required */
418 i8254_init();
419#endif
420
Graeme Russ078395c2009-11-24 20:04:21 +1100421 return 0;
422}
423
Graeme Russ6e256002011-12-27 22:46:43 +1100424void x86_enable_caches(void)
425{
Stefan Reinauer2acf8482012-12-02 04:49:50 +0000426 unsigned long cr0;
Graeme Russ121931c2011-02-12 15:11:35 +1100427
Stefan Reinauer2acf8482012-12-02 04:49:50 +0000428 cr0 = read_cr0();
429 cr0 &= ~(X86_CR0_NW | X86_CR0_CD);
430 write_cr0(cr0);
431 wbinvd();
Graeme Russ6e256002011-12-27 22:46:43 +1100432}
433void enable_caches(void) __attribute__((weak, alias("x86_enable_caches")));
Graeme Russ121931c2011-02-12 15:11:35 +1100434
Stefan Reinauer2acf8482012-12-02 04:49:50 +0000435void x86_disable_caches(void)
436{
437 unsigned long cr0;
438
439 cr0 = read_cr0();
440 cr0 |= X86_CR0_NW | X86_CR0_CD;
441 wbinvd();
442 write_cr0(cr0);
443 wbinvd();
444}
445void disable_caches(void) __attribute__((weak, alias("x86_disable_caches")));
446
Graeme Russ6e256002011-12-27 22:46:43 +1100447int x86_init_cache(void)
448{
449 enable_caches();
450
wdenk591dda52002-11-18 00:14:45 +0000451 return 0;
452}
Graeme Russ6e256002011-12-27 22:46:43 +1100453int init_cache(void) __attribute__((weak, alias("x86_init_cache")));
wdenk591dda52002-11-18 00:14:45 +0000454
Wolfgang Denk6262d0212010-06-28 22:00:46 +0200455int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk591dda52002-11-18 00:14:45 +0000456{
Graeme Russfdee8b12011-11-08 02:33:13 +0000457 printf("resetting ...\n");
Graeme Russ45fc1d82011-04-13 19:43:26 +1000458
459 /* wait 50 ms */
460 udelay(50000);
wdenk591dda52002-11-18 00:14:45 +0000461 disable_interrupts();
462 reset_cpu(0);
463
464 /*NOTREACHED*/
465 return 0;
466}
467
Graeme Russfdee8b12011-11-08 02:33:13 +0000468void flush_cache(unsigned long dummy1, unsigned long dummy2)
wdenk591dda52002-11-18 00:14:45 +0000469{
470 asm("wbinvd\n");
wdenk591dda52002-11-18 00:14:45 +0000471}
Graeme Russ278638d2008-12-07 10:29:02 +1100472
Simon Glass83374332014-11-06 13:20:08 -0700473__weak void reset_cpu(ulong addr)
Graeme Russ278638d2008-12-07 10:29:02 +1100474{
Simon Glasse0e7bd02015-04-28 20:11:29 -0600475 /* Do a hard reset through the chipset's reset control register */
Simon Glass8b73e9f2016-03-11 22:06:59 -0700476 outb(SYS_RST | RST_CPU, IO_PORT_RESET);
Simon Glasse0e7bd02015-04-28 20:11:29 -0600477 for (;;)
478 cpu_hlt();
479}
480
481void x86_full_reset(void)
482{
Simon Glass8b73e9f2016-03-11 22:06:59 -0700483 outb(FULL_RST | SYS_RST | RST_CPU, IO_PORT_RESET);
Graeme Russ278638d2008-12-07 10:29:02 +1100484}
Stefan Reinauer2acf8482012-12-02 04:49:50 +0000485
486int dcache_status(void)
487{
Simon Glasscecf90e2015-07-31 09:31:26 -0600488 return !(read_cr0() & X86_CR0_CD);
Stefan Reinauer2acf8482012-12-02 04:49:50 +0000489}
490
491/* Define these functions to allow ehch-hcd to function */
492void flush_dcache_range(unsigned long start, unsigned long stop)
493{
494}
495
496void invalidate_dcache_range(unsigned long start, unsigned long stop)
497{
498}
Simon Glass2baa3bb2013-02-28 19:26:11 +0000499
500void dcache_enable(void)
501{
502 enable_caches();
503}
504
505void dcache_disable(void)
506{
507 disable_caches();
508}
509
510void icache_enable(void)
511{
512}
513
514void icache_disable(void)
515{
516}
517
518int icache_status(void)
519{
520 return 1;
521}
Simon Glassd8d9fec2014-10-10 08:21:52 -0600522
523void cpu_enable_paging_pae(ulong cr3)
524{
525 __asm__ __volatile__(
526 /* Load the page table address */
527 "movl %0, %%cr3\n"
528 /* Enable pae */
529 "movl %%cr4, %%eax\n"
530 "orl $0x00000020, %%eax\n"
531 "movl %%eax, %%cr4\n"
532 /* Enable paging */
533 "movl %%cr0, %%eax\n"
534 "orl $0x80000000, %%eax\n"
535 "movl %%eax, %%cr0\n"
536 :
537 : "r" (cr3)
538 : "eax");
539}
540
541void cpu_disable_paging_pae(void)
542{
543 /* Turn off paging */
544 __asm__ __volatile__ (
545 /* Disable paging */
546 "movl %%cr0, %%eax\n"
547 "andl $0x7fffffff, %%eax\n"
548 "movl %%eax, %%cr0\n"
549 /* Disable pae */
550 "movl %%cr4, %%eax\n"
551 "andl $0xffffffdf, %%eax\n"
552 "movl %%eax, %%cr4\n"
553 :
554 :
555 : "eax");
556}
Simon Glass2f2efbc2014-10-10 08:21:54 -0600557
Bin Meng035c1d22014-11-09 22:18:56 +0800558static bool can_detect_long_mode(void)
Simon Glass2f2efbc2014-10-10 08:21:54 -0600559{
Bin Meng035c1d22014-11-09 22:18:56 +0800560 return cpuid_eax(0x80000000) > 0x80000000UL;
561}
Simon Glass2f2efbc2014-10-10 08:21:54 -0600562
Bin Meng035c1d22014-11-09 22:18:56 +0800563static bool has_long_mode(void)
564{
565 return cpuid_edx(0x80000001) & (1 << 29) ? true : false;
Simon Glass2f2efbc2014-10-10 08:21:54 -0600566}
567
Bin Meng035c1d22014-11-09 22:18:56 +0800568int cpu_has_64bit(void)
Simon Glass2f2efbc2014-10-10 08:21:54 -0600569{
Bin Meng035c1d22014-11-09 22:18:56 +0800570 return has_cpuid() && can_detect_long_mode() &&
571 has_long_mode();
572}
Simon Glass2f2efbc2014-10-10 08:21:54 -0600573
Bin Meng035c1d22014-11-09 22:18:56 +0800574const char *cpu_vendor_name(int vendor)
575{
576 const char *name;
577 name = "<invalid cpu vendor>";
578 if ((vendor < (ARRAY_SIZE(x86_vendor_name))) &&
579 (x86_vendor_name[vendor] != 0))
580 name = x86_vendor_name[vendor];
Simon Glass2f2efbc2014-10-10 08:21:54 -0600581
Bin Meng035c1d22014-11-09 22:18:56 +0800582 return name;
Simon Glass2f2efbc2014-10-10 08:21:54 -0600583}
584
Simon Glass543bb142014-11-10 18:00:26 -0700585char *cpu_get_name(char *name)
Simon Glass2f2efbc2014-10-10 08:21:54 -0600586{
Simon Glass543bb142014-11-10 18:00:26 -0700587 unsigned int *name_as_ints = (unsigned int *)name;
Bin Meng035c1d22014-11-09 22:18:56 +0800588 struct cpuid_result regs;
Simon Glass543bb142014-11-10 18:00:26 -0700589 char *ptr;
Bin Meng035c1d22014-11-09 22:18:56 +0800590 int i;
Simon Glass2f2efbc2014-10-10 08:21:54 -0600591
Simon Glass543bb142014-11-10 18:00:26 -0700592 /* This bit adds up to 48 bytes */
Bin Meng035c1d22014-11-09 22:18:56 +0800593 for (i = 0; i < 3; i++) {
594 regs = cpuid(0x80000002 + i);
595 name_as_ints[i * 4 + 0] = regs.eax;
596 name_as_ints[i * 4 + 1] = regs.ebx;
597 name_as_ints[i * 4 + 2] = regs.ecx;
598 name_as_ints[i * 4 + 3] = regs.edx;
599 }
Simon Glass543bb142014-11-10 18:00:26 -0700600 name[CPU_MAX_NAME_LEN - 1] = '\0';
Simon Glass2f2efbc2014-10-10 08:21:54 -0600601
Bin Meng035c1d22014-11-09 22:18:56 +0800602 /* Skip leading spaces. */
Simon Glass543bb142014-11-10 18:00:26 -0700603 ptr = name;
604 while (*ptr == ' ')
605 ptr++;
Bin Meng035c1d22014-11-09 22:18:56 +0800606
Simon Glass543bb142014-11-10 18:00:26 -0700607 return ptr;
Simon Glass2f2efbc2014-10-10 08:21:54 -0600608}
609
Simon Glass543bb142014-11-10 18:00:26 -0700610int default_print_cpuinfo(void)
Simon Glass2f2efbc2014-10-10 08:21:54 -0600611{
Bin Meng035c1d22014-11-09 22:18:56 +0800612 printf("CPU: %s, vendor %s, device %xh\n",
613 cpu_has_64bit() ? "x86_64" : "x86",
614 cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
Simon Glass2f2efbc2014-10-10 08:21:54 -0600615
616 return 0;
617}
Simon Glass463fac22014-10-10 08:21:55 -0600618
619#define PAGETABLE_SIZE (6 * 4096)
620
621/**
622 * build_pagetable() - build a flat 4GiB page table structure for 64-bti mode
623 *
624 * @pgtable: Pointer to a 24iKB block of memory
625 */
626static void build_pagetable(uint32_t *pgtable)
627{
628 uint i;
629
630 memset(pgtable, '\0', PAGETABLE_SIZE);
631
632 /* Level 4 needs a single entry */
633 pgtable[0] = (uint32_t)&pgtable[1024] + 7;
634
635 /* Level 3 has one 64-bit entry for each GiB of memory */
636 for (i = 0; i < 4; i++) {
637 pgtable[1024 + i * 2] = (uint32_t)&pgtable[2048] +
638 0x1000 * i + 7;
639 }
640
641 /* Level 2 has 2048 64-bit entries, each repesenting 2MiB */
642 for (i = 0; i < 2048; i++)
643 pgtable[2048 + i * 2] = 0x183 + (i << 21UL);
644}
645
646int cpu_jump_to_64bit(ulong setup_base, ulong target)
647{
648 uint32_t *pgtable;
649
650 pgtable = memalign(4096, PAGETABLE_SIZE);
651 if (!pgtable)
652 return -ENOMEM;
653
654 build_pagetable(pgtable);
655 cpu_call64((ulong)pgtable, setup_base, target);
656 free(pgtable);
657
658 return -EFAULT;
659}
Simon Glass9f0afe72014-11-12 22:42:26 -0700660
661void show_boot_progress(int val)
662{
Simon Glass9f0afe72014-11-12 22:42:26 -0700663 outb(val, POST_PORT);
664}
Bin Mengf17cea62015-04-24 18:10:04 +0800665
666#ifndef CONFIG_SYS_COREBOOT
Bin Meng2f8560c2016-05-11 07:44:56 -0700667/*
668 * Implement a weak default function for boards that optionally
669 * need to clean up the system before jumping to the kernel.
670 */
671__weak void board_final_cleanup(void)
672{
673}
674
Bin Mengf17cea62015-04-24 18:10:04 +0800675int last_stage_init(void)
676{
677 write_tables();
678
Bin Meng2f8560c2016-05-11 07:44:56 -0700679 board_final_cleanup();
680
Bin Mengf17cea62015-04-24 18:10:04 +0800681 return 0;
682}
683#endif
Simon Glass02fe5e62015-04-29 22:26:01 -0600684
Bin Mengf967f9a2015-06-17 11:15:36 +0800685#ifdef CONFIG_SMP
686static int enable_smis(struct udevice *cpu, void *unused)
687{
688 return 0;
689}
690
691static struct mp_flight_record mp_steps[] = {
692 MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL),
693 /* Wait for APs to finish initialization before proceeding */
694 MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL),
695};
696
697static int x86_mp_init(void)
698{
699 struct mp_params mp_params;
700
Bin Mengf967f9a2015-06-17 11:15:36 +0800701 mp_params.parallel_microcode_load = 0,
702 mp_params.flight_plan = &mp_steps[0];
703 mp_params.num_records = ARRAY_SIZE(mp_steps);
704 mp_params.microcode_pointer = 0;
705
706 if (mp_init(&mp_params)) {
707 printf("Warning: MP init failure\n");
708 return -EIO;
709 }
710
711 return 0;
712}
713#endif
714
Simon Glass0aa7bfa2016-01-17 16:11:28 -0700715static int x86_init_cpus(void)
Simon Glass02fe5e62015-04-29 22:26:01 -0600716{
Bin Mengf967f9a2015-06-17 11:15:36 +0800717#ifdef CONFIG_SMP
718 debug("Init additional CPUs\n");
719 x86_mp_init();
Bin Meng89727762015-07-22 01:21:12 -0700720#else
721 struct udevice *dev;
722
723 /*
724 * This causes the cpu-x86 driver to be probed.
725 * We don't check return value here as we want to allow boards
726 * which have not been converted to use cpu uclass driver to boot.
727 */
728 uclass_first_device(UCLASS_CPU, &dev);
Bin Mengf967f9a2015-06-17 11:15:36 +0800729#endif
730
Simon Glass02fe5e62015-04-29 22:26:01 -0600731 return 0;
732}
733
734int cpu_init_r(void)
735{
Simon Glass00431f62016-01-17 16:11:30 -0700736 struct udevice *dev;
737 int ret;
738
739 if (!ll_boot_init())
740 return 0;
741
742 ret = x86_init_cpus();
743 if (ret)
744 return ret;
745
746 /*
747 * Set up the northbridge, PCH and LPC if available. Note that these
748 * may have had some limited pre-relocation init if they were probed
749 * before relocation, but this is post relocation.
750 */
751 uclass_first_device(UCLASS_NORTHBRIDGE, &dev);
752 uclass_first_device(UCLASS_PCH, &dev);
753 uclass_first_device(UCLASS_LPC, &dev);
Simon Glass2b6d80b2015-08-04 12:34:00 -0600754
Bin Menga4559642016-06-08 05:07:38 -0700755 /* Set up pin control if available */
756 ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
757 debug("%s, pinctrl=%p, ret=%d\n", __func__, dev, ret);
758
Simon Glass2b6d80b2015-08-04 12:34:00 -0600759 return 0;
Simon Glass02fe5e62015-04-29 22:26:01 -0600760}
Bin Meng1141fcf2016-05-11 07:45:00 -0700761
762#ifndef CONFIG_EFI_STUB
763int reserve_arch(void)
764{
765#ifdef CONFIG_ENABLE_MRC_CACHE
Bin Meng1c9da372016-05-11 07:45:01 -0700766 mrccache_reserve();
767#endif
768
769#ifdef CONFIG_SEABIOS
770 high_table_reserve();
Bin Meng1141fcf2016-05-11 07:45:00 -0700771#endif
Bin Meng1c9da372016-05-11 07:45:01 -0700772
773 return 0;
Bin Meng1141fcf2016-05-11 07:45:00 -0700774}
775#endif