xref: /rk3399_rockchip-uboot/arch/x86/cpu/cpu.c (revision e1ffd81797d59652124bd9cda813a58644f5dea9)
1fea25720SGraeme Russ /*
2fea25720SGraeme Russ  * (C) Copyright 2008-2011
3fea25720SGraeme Russ  * Graeme Russ, <graeme.russ@gmail.com>
4fea25720SGraeme Russ  *
5fea25720SGraeme Russ  * (C) Copyright 2002
6fa82f871SAlbert ARIBAUD  * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
7fea25720SGraeme Russ  *
8fea25720SGraeme Russ  * (C) Copyright 2002
9fea25720SGraeme Russ  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
10fea25720SGraeme Russ  * Marius Groeger <mgroeger@sysgo.de>
11fea25720SGraeme Russ  *
12fea25720SGraeme Russ  * (C) Copyright 2002
13fea25720SGraeme Russ  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
14fea25720SGraeme Russ  * Alex Zuepke <azu@sysgo.de>
15fea25720SGraeme Russ  *
161a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
17fea25720SGraeme Russ  */
18fea25720SGraeme Russ 
19fea25720SGraeme Russ #include <common.h>
20fea25720SGraeme Russ #include <command.h>
21200182a7SSimon Glass #include <errno.h>
22200182a7SSimon Glass #include <malloc.h>
23095593c0SStefan Reinauer #include <asm/control_regs.h>
24200182a7SSimon Glass #include <asm/cpu.h>
25fea25720SGraeme Russ #include <asm/processor.h>
26fea25720SGraeme Russ #include <asm/processor-flags.h>
27fea25720SGraeme Russ #include <asm/interrupt.h>
2860a9b6bfSGabe Black #include <linux/compiler.h>
29fea25720SGraeme Russ 
30fea25720SGraeme Russ /*
31fea25720SGraeme Russ  * Constructor for a conventional segment GDT (or LDT) entry
32fea25720SGraeme Russ  * This is a macro so it can be used in initialisers
33fea25720SGraeme Russ  */
34fea25720SGraeme Russ #define GDT_ENTRY(flags, base, limit)			\
35fea25720SGraeme Russ 	((((base)  & 0xff000000ULL) << (56-24)) |	\
36fea25720SGraeme Russ 	 (((flags) & 0x0000f0ffULL) << 40) |		\
37fea25720SGraeme Russ 	 (((limit) & 0x000f0000ULL) << (48-16)) |	\
38fea25720SGraeme Russ 	 (((base)  & 0x00ffffffULL) << 16) |		\
39fea25720SGraeme Russ 	 (((limit) & 0x0000ffffULL)))
40fea25720SGraeme Russ 
41fea25720SGraeme Russ struct gdt_ptr {
42fea25720SGraeme Russ 	u16 len;
43fea25720SGraeme Russ 	u32 ptr;
44717979fdSGraeme Russ } __packed;
45fea25720SGraeme Russ 
4674bfbe1bSGraeme Russ static void load_ds(u32 segment)
47fea25720SGraeme Russ {
4874bfbe1bSGraeme Russ 	asm volatile("movl %0, %%ds" : : "r" (segment * X86_GDT_ENTRY_SIZE));
4974bfbe1bSGraeme Russ }
50fea25720SGraeme Russ 
5174bfbe1bSGraeme Russ static void load_es(u32 segment)
5274bfbe1bSGraeme Russ {
5374bfbe1bSGraeme Russ 	asm volatile("movl %0, %%es" : : "r" (segment * X86_GDT_ENTRY_SIZE));
5474bfbe1bSGraeme Russ }
55fea25720SGraeme Russ 
5674bfbe1bSGraeme Russ static void load_fs(u32 segment)
5774bfbe1bSGraeme Russ {
5874bfbe1bSGraeme Russ 	asm volatile("movl %0, %%fs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
5974bfbe1bSGraeme Russ }
6074bfbe1bSGraeme Russ 
6174bfbe1bSGraeme Russ static void load_gs(u32 segment)
6274bfbe1bSGraeme Russ {
6374bfbe1bSGraeme Russ 	asm volatile("movl %0, %%gs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
6474bfbe1bSGraeme Russ }
6574bfbe1bSGraeme Russ 
6674bfbe1bSGraeme Russ static void load_ss(u32 segment)
6774bfbe1bSGraeme Russ {
6874bfbe1bSGraeme Russ 	asm volatile("movl %0, %%ss" : : "r" (segment * X86_GDT_ENTRY_SIZE));
6974bfbe1bSGraeme Russ }
7074bfbe1bSGraeme Russ 
7174bfbe1bSGraeme Russ static void load_gdt(const u64 *boot_gdt, u16 num_entries)
7274bfbe1bSGraeme Russ {
7374bfbe1bSGraeme Russ 	struct gdt_ptr gdt;
7474bfbe1bSGraeme Russ 
7574bfbe1bSGraeme Russ 	gdt.len = (num_entries * 8) - 1;
7674bfbe1bSGraeme Russ 	gdt.ptr = (u32)boot_gdt;
7774bfbe1bSGraeme Russ 
7874bfbe1bSGraeme Russ 	asm volatile("lgdtl %0\n" : : "m" (gdt));
79fea25720SGraeme Russ }
80fea25720SGraeme Russ 
819e6c572fSGraeme Russ void setup_gdt(gd_t *id, u64 *gdt_addr)
829e6c572fSGraeme Russ {
839e6c572fSGraeme Russ 	/* CS: code, read/execute, 4 GB, base 0 */
849e6c572fSGraeme Russ 	gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
859e6c572fSGraeme Russ 
869e6c572fSGraeme Russ 	/* DS: data, read/write, 4 GB, base 0 */
879e6c572fSGraeme Russ 	gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);
889e6c572fSGraeme Russ 
899e6c572fSGraeme Russ 	/* FS: data, read/write, 4 GB, base (Global Data Pointer) */
905a35e6c4SSimon Glass 	id->arch.gd_addr = id;
910cecc3b6SSimon Glass 	gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0xc093,
925a35e6c4SSimon Glass 		     (ulong)&id->arch.gd_addr, 0xfffff);
939e6c572fSGraeme Russ 
949e6c572fSGraeme Russ 	/* 16-bit CS: code, read/execute, 64 kB, base 0 */
959e6c572fSGraeme Russ 	gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x109b, 0, 0x0ffff);
969e6c572fSGraeme Russ 
979e6c572fSGraeme Russ 	/* 16-bit DS: data, read/write, 64 kB, base 0 */
989e6c572fSGraeme Russ 	gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x1093, 0, 0x0ffff);
999e6c572fSGraeme Russ 
1009e6c572fSGraeme Russ 	load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
1019e6c572fSGraeme Russ 	load_ds(X86_GDT_ENTRY_32BIT_DS);
1029e6c572fSGraeme Russ 	load_es(X86_GDT_ENTRY_32BIT_DS);
1039e6c572fSGraeme Russ 	load_gs(X86_GDT_ENTRY_32BIT_DS);
1049e6c572fSGraeme Russ 	load_ss(X86_GDT_ENTRY_32BIT_DS);
1059e6c572fSGraeme Russ 	load_fs(X86_GDT_ENTRY_32BIT_FS);
1069e6c572fSGraeme Russ }
1079e6c572fSGraeme Russ 
108f30fc4deSGabe Black int __weak x86_cleanup_before_linux(void)
109f30fc4deSGabe Black {
1107949703aSSimon Glass #ifdef CONFIG_BOOTSTAGE_STASH
1117949703aSSimon Glass 	bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH,
1127949703aSSimon Glass 			CONFIG_BOOTSTAGE_STASH_SIZE);
1137949703aSSimon Glass #endif
1147949703aSSimon Glass 
115f30fc4deSGabe Black 	return 0;
116f30fc4deSGabe Black }
117f30fc4deSGabe Black 
118fea25720SGraeme Russ int x86_cpu_init_f(void)
119fea25720SGraeme Russ {
120fea25720SGraeme Russ 	const u32 em_rst = ~X86_CR0_EM;
121fea25720SGraeme Russ 	const u32 mp_ne_set = X86_CR0_MP | X86_CR0_NE;
122fea25720SGraeme Russ 
123fea25720SGraeme Russ 	/* initialize FPU, reset EM, set MP and NE */
124fea25720SGraeme Russ 	asm ("fninit\n" \
125fea25720SGraeme Russ 	     "movl %%cr0, %%eax\n" \
126fea25720SGraeme Russ 	     "andl %0, %%eax\n" \
127fea25720SGraeme Russ 	     "orl  %1, %%eax\n" \
128fea25720SGraeme Russ 	     "movl %%eax, %%cr0\n" \
129fea25720SGraeme Russ 	     : : "i" (em_rst), "i" (mp_ne_set) : "eax");
130fea25720SGraeme Russ 
131fea25720SGraeme Russ 	return 0;
132fea25720SGraeme Russ }
133fea25720SGraeme Russ 
134fea25720SGraeme Russ int x86_cpu_init_r(void)
135fea25720SGraeme Russ {
136d653244bSGraeme Russ 	/* Initialize core interrupt and exception functionality of CPU */
137d653244bSGraeme Russ 	cpu_init_interrupts();
138d653244bSGraeme Russ 	return 0;
139d653244bSGraeme Russ }
140d653244bSGraeme Russ int cpu_init_r(void) __attribute__((weak, alias("x86_cpu_init_r")));
141d653244bSGraeme Russ 
142d653244bSGraeme Russ void x86_enable_caches(void)
143d653244bSGraeme Russ {
144095593c0SStefan Reinauer 	unsigned long cr0;
145fea25720SGraeme Russ 
146095593c0SStefan Reinauer 	cr0 = read_cr0();
147095593c0SStefan Reinauer 	cr0 &= ~(X86_CR0_NW | X86_CR0_CD);
148095593c0SStefan Reinauer 	write_cr0(cr0);
149095593c0SStefan Reinauer 	wbinvd();
150d653244bSGraeme Russ }
151d653244bSGraeme Russ void enable_caches(void) __attribute__((weak, alias("x86_enable_caches")));
152fea25720SGraeme Russ 
153095593c0SStefan Reinauer void x86_disable_caches(void)
154095593c0SStefan Reinauer {
155095593c0SStefan Reinauer 	unsigned long cr0;
156095593c0SStefan Reinauer 
157095593c0SStefan Reinauer 	cr0 = read_cr0();
158095593c0SStefan Reinauer 	cr0 |= X86_CR0_NW | X86_CR0_CD;
159095593c0SStefan Reinauer 	wbinvd();
160095593c0SStefan Reinauer 	write_cr0(cr0);
161095593c0SStefan Reinauer 	wbinvd();
162095593c0SStefan Reinauer }
163095593c0SStefan Reinauer void disable_caches(void) __attribute__((weak, alias("x86_disable_caches")));
164095593c0SStefan Reinauer 
165d653244bSGraeme Russ int x86_init_cache(void)
166d653244bSGraeme Russ {
167d653244bSGraeme Russ 	enable_caches();
168d653244bSGraeme Russ 
169fea25720SGraeme Russ 	return 0;
170fea25720SGraeme Russ }
171d653244bSGraeme Russ int init_cache(void) __attribute__((weak, alias("x86_init_cache")));
172fea25720SGraeme Russ 
173fea25720SGraeme Russ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
174fea25720SGraeme Russ {
175fea25720SGraeme Russ 	printf("resetting ...\n");
176fea25720SGraeme Russ 
177fea25720SGraeme Russ 	/* wait 50 ms */
178fea25720SGraeme Russ 	udelay(50000);
179fea25720SGraeme Russ 	disable_interrupts();
180fea25720SGraeme Russ 	reset_cpu(0);
181fea25720SGraeme Russ 
182fea25720SGraeme Russ 	/*NOTREACHED*/
183fea25720SGraeme Russ 	return 0;
184fea25720SGraeme Russ }
185fea25720SGraeme Russ 
186fea25720SGraeme Russ void  flush_cache(unsigned long dummy1, unsigned long dummy2)
187fea25720SGraeme Russ {
188fea25720SGraeme Russ 	asm("wbinvd\n");
189fea25720SGraeme Russ }
190fea25720SGraeme Russ 
191fea25720SGraeme Russ void __attribute__ ((regparm(0))) generate_gpf(void);
192fea25720SGraeme Russ 
193fea25720SGraeme Russ /* segment 0x70 is an arbitrary segment which does not exist */
194fea25720SGraeme Russ asm(".globl generate_gpf\n"
195fea25720SGraeme Russ 	".hidden generate_gpf\n"
196fea25720SGraeme Russ 	".type generate_gpf, @function\n"
197fea25720SGraeme Russ 	"generate_gpf:\n"
198fea25720SGraeme Russ 	"ljmp   $0x70, $0x47114711\n");
199fea25720SGraeme Russ 
200*e1ffd817SSimon Glass __weak void reset_cpu(ulong addr)
201fea25720SGraeme Russ {
202fea25720SGraeme Russ 	printf("Resetting using x86 Triple Fault\n");
203fea25720SGraeme Russ 	set_vector(13, generate_gpf);	/* general protection fault handler */
204fea25720SGraeme Russ 	set_vector(8, generate_gpf);	/* double fault handler */
205fea25720SGraeme Russ 	generate_gpf();			/* start the show */
206fea25720SGraeme Russ }
207095593c0SStefan Reinauer 
208095593c0SStefan Reinauer int dcache_status(void)
209095593c0SStefan Reinauer {
210095593c0SStefan Reinauer 	return !(read_cr0() & 0x40000000);
211095593c0SStefan Reinauer }
212095593c0SStefan Reinauer 
213095593c0SStefan Reinauer /* Define these functions to allow ehch-hcd to function */
214095593c0SStefan Reinauer void flush_dcache_range(unsigned long start, unsigned long stop)
215095593c0SStefan Reinauer {
216095593c0SStefan Reinauer }
217095593c0SStefan Reinauer 
218095593c0SStefan Reinauer void invalidate_dcache_range(unsigned long start, unsigned long stop)
219095593c0SStefan Reinauer {
220095593c0SStefan Reinauer }
22189371409SSimon Glass 
22289371409SSimon Glass void dcache_enable(void)
22389371409SSimon Glass {
22489371409SSimon Glass 	enable_caches();
22589371409SSimon Glass }
22689371409SSimon Glass 
22789371409SSimon Glass void dcache_disable(void)
22889371409SSimon Glass {
22989371409SSimon Glass 	disable_caches();
23089371409SSimon Glass }
23189371409SSimon Glass 
23289371409SSimon Glass void icache_enable(void)
23389371409SSimon Glass {
23489371409SSimon Glass }
23589371409SSimon Glass 
23689371409SSimon Glass void icache_disable(void)
23789371409SSimon Glass {
23889371409SSimon Glass }
23989371409SSimon Glass 
24089371409SSimon Glass int icache_status(void)
24189371409SSimon Glass {
24289371409SSimon Glass 	return 1;
24389371409SSimon Glass }
2447bddac94SSimon Glass 
2457bddac94SSimon Glass void cpu_enable_paging_pae(ulong cr3)
2467bddac94SSimon Glass {
2477bddac94SSimon Glass 	__asm__ __volatile__(
2487bddac94SSimon Glass 		/* Load the page table address */
2497bddac94SSimon Glass 		"movl	%0, %%cr3\n"
2507bddac94SSimon Glass 		/* Enable pae */
2517bddac94SSimon Glass 		"movl	%%cr4, %%eax\n"
2527bddac94SSimon Glass 		"orl	$0x00000020, %%eax\n"
2537bddac94SSimon Glass 		"movl	%%eax, %%cr4\n"
2547bddac94SSimon Glass 		/* Enable paging */
2557bddac94SSimon Glass 		"movl	%%cr0, %%eax\n"
2567bddac94SSimon Glass 		"orl	$0x80000000, %%eax\n"
2577bddac94SSimon Glass 		"movl	%%eax, %%cr0\n"
2587bddac94SSimon Glass 		:
2597bddac94SSimon Glass 		: "r" (cr3)
2607bddac94SSimon Glass 		: "eax");
2617bddac94SSimon Glass }
2627bddac94SSimon Glass 
2637bddac94SSimon Glass void cpu_disable_paging_pae(void)
2647bddac94SSimon Glass {
2657bddac94SSimon Glass 	/* Turn off paging */
2667bddac94SSimon Glass 	__asm__ __volatile__ (
2677bddac94SSimon Glass 		/* Disable paging */
2687bddac94SSimon Glass 		"movl	%%cr0, %%eax\n"
2697bddac94SSimon Glass 		"andl	$0x7fffffff, %%eax\n"
2707bddac94SSimon Glass 		"movl	%%eax, %%cr0\n"
2717bddac94SSimon Glass 		/* Disable pae */
2727bddac94SSimon Glass 		"movl	%%cr4, %%eax\n"
2737bddac94SSimon Glass 		"andl	$0xffffffdf, %%eax\n"
2747bddac94SSimon Glass 		"movl	%%eax, %%cr4\n"
2757bddac94SSimon Glass 		:
2767bddac94SSimon Glass 		:
2777bddac94SSimon Glass 		: "eax");
2787bddac94SSimon Glass }
27992cc94a1SSimon Glass 
28092cc94a1SSimon Glass static bool has_cpuid(void)
28192cc94a1SSimon Glass {
28292cc94a1SSimon Glass 	unsigned long flag;
28392cc94a1SSimon Glass 
28492cc94a1SSimon Glass 	asm volatile("pushf\n" \
28592cc94a1SSimon Glass 		"pop %%eax\n"
28692cc94a1SSimon Glass 		"mov %%eax, %%ecx\n"	/* ecx = flags */
28792cc94a1SSimon Glass 		"xor %1, %%eax\n"
28892cc94a1SSimon Glass 		"push %%eax\n"
28992cc94a1SSimon Glass 		"popf\n"		/* flags ^= $2 */
29092cc94a1SSimon Glass 		"pushf\n"
29192cc94a1SSimon Glass 		"pop %%eax\n"		/* eax = flags */
29292cc94a1SSimon Glass 		"push %%ecx\n"
29392cc94a1SSimon Glass 		"popf\n"		/* flags = ecx */
29492cc94a1SSimon Glass 		"xor %%ecx, %%eax\n"
29592cc94a1SSimon Glass 		"mov %%eax, %0"
29692cc94a1SSimon Glass 		: "=r" (flag)
29792cc94a1SSimon Glass 		: "i" (1 << 21)
29892cc94a1SSimon Glass 		: "eax", "ecx", "memory");
29992cc94a1SSimon Glass 
30092cc94a1SSimon Glass 	return flag != 0;
30192cc94a1SSimon Glass }
30292cc94a1SSimon Glass 
30392cc94a1SSimon Glass static bool can_detect_long_mode(void)
30492cc94a1SSimon Glass {
30592cc94a1SSimon Glass 	unsigned long flag;
30692cc94a1SSimon Glass 
30792cc94a1SSimon Glass 	asm volatile("mov $0x80000000, %%eax\n"
30892cc94a1SSimon Glass 		"cpuid\n"
30992cc94a1SSimon Glass 		"mov %%eax, %0"
31092cc94a1SSimon Glass 		: "=r" (flag)
31192cc94a1SSimon Glass 		:
31292cc94a1SSimon Glass 		: "eax", "ebx", "ecx", "edx", "memory");
31392cc94a1SSimon Glass 
31492cc94a1SSimon Glass 	return flag > 0x80000000UL;
31592cc94a1SSimon Glass }
31692cc94a1SSimon Glass 
31792cc94a1SSimon Glass static bool has_long_mode(void)
31892cc94a1SSimon Glass {
31992cc94a1SSimon Glass 	unsigned long flag;
32092cc94a1SSimon Glass 
32192cc94a1SSimon Glass 	asm volatile("mov $0x80000001, %%eax\n"
32292cc94a1SSimon Glass 		"cpuid\n"
32392cc94a1SSimon Glass 		"mov %%edx, %0"
32492cc94a1SSimon Glass 		: "=r" (flag)
32592cc94a1SSimon Glass 		:
32692cc94a1SSimon Glass 		: "eax", "ebx", "ecx", "edx", "memory");
32792cc94a1SSimon Glass 
32892cc94a1SSimon Glass 	return flag & (1 << 29) ? true : false;
32992cc94a1SSimon Glass }
33092cc94a1SSimon Glass 
33192cc94a1SSimon Glass int cpu_has_64bit(void)
33292cc94a1SSimon Glass {
33392cc94a1SSimon Glass 	return has_cpuid() && can_detect_long_mode() &&
33492cc94a1SSimon Glass 		has_long_mode();
33592cc94a1SSimon Glass }
33692cc94a1SSimon Glass 
33792cc94a1SSimon Glass int print_cpuinfo(void)
33892cc94a1SSimon Glass {
33992cc94a1SSimon Glass 	printf("CPU:   %s\n", cpu_has_64bit() ? "x86_64" : "x86");
34092cc94a1SSimon Glass 
34192cc94a1SSimon Glass 	return 0;
34292cc94a1SSimon Glass }
343200182a7SSimon Glass 
344200182a7SSimon Glass #define PAGETABLE_SIZE		(6 * 4096)
345200182a7SSimon Glass 
346200182a7SSimon Glass /**
347200182a7SSimon Glass  * build_pagetable() - build a flat 4GiB page table structure for 64-bti mode
348200182a7SSimon Glass  *
349200182a7SSimon Glass  * @pgtable: Pointer to a 24iKB block of memory
350200182a7SSimon Glass  */
351200182a7SSimon Glass static void build_pagetable(uint32_t *pgtable)
352200182a7SSimon Glass {
353200182a7SSimon Glass 	uint i;
354200182a7SSimon Glass 
355200182a7SSimon Glass 	memset(pgtable, '\0', PAGETABLE_SIZE);
356200182a7SSimon Glass 
357200182a7SSimon Glass 	/* Level 4 needs a single entry */
358200182a7SSimon Glass 	pgtable[0] = (uint32_t)&pgtable[1024] + 7;
359200182a7SSimon Glass 
360200182a7SSimon Glass 	/* Level 3 has one 64-bit entry for each GiB of memory */
361200182a7SSimon Glass 	for (i = 0; i < 4; i++) {
362200182a7SSimon Glass 		pgtable[1024 + i * 2] = (uint32_t)&pgtable[2048] +
363200182a7SSimon Glass 							0x1000 * i + 7;
364200182a7SSimon Glass 	}
365200182a7SSimon Glass 
366200182a7SSimon Glass 	/* Level 2 has 2048 64-bit entries, each repesenting 2MiB */
367200182a7SSimon Glass 	for (i = 0; i < 2048; i++)
368200182a7SSimon Glass 		pgtable[2048 + i * 2] = 0x183 + (i << 21UL);
369200182a7SSimon Glass }
370200182a7SSimon Glass 
371200182a7SSimon Glass int cpu_jump_to_64bit(ulong setup_base, ulong target)
372200182a7SSimon Glass {
373200182a7SSimon Glass 	uint32_t *pgtable;
374200182a7SSimon Glass 
375200182a7SSimon Glass 	pgtable = memalign(4096, PAGETABLE_SIZE);
376200182a7SSimon Glass 	if (!pgtable)
377200182a7SSimon Glass 		return -ENOMEM;
378200182a7SSimon Glass 
379200182a7SSimon Glass 	build_pagetable(pgtable);
380200182a7SSimon Glass 	cpu_call64((ulong)pgtable, setup_base, target);
381200182a7SSimon Glass 	free(pgtable);
382200182a7SSimon Glass 
383200182a7SSimon Glass 	return -EFAULT;
384200182a7SSimon Glass }
385