aboutsummaryrefslogtreecommitdiff
path: root/src/arch/i386/lib/cpu.c
blob: 3e27e7acdcfde5a9a76a8c919f6199a442aea77f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <console/console.h>
#include <cpu/cpu.h>
#include <mem.h>
#include <arch/io.h>
#include <string.h>
#include <cpu/cpufixup.h>
#include <smp/start_stop.h>
#include <cpu/cpufixup.h>
#include <cpu/p6/mtrr.h>
#include <cpu/p6/msr.h>
#include <cpu/p6/apic.h>
#include <cpu/p5/cpuid.h>
#if 0
#include <cpu/l2_cache.h>
#endif

#if CONFIG_SMP || CONFIG_IOAPIC
#define APIC 1
#endif

static void cache_on(struct mem_range *mem)
{
	post_code(0x60);
	printk_info("Enabling cache...");


	/* we need an #ifdef i586 here at some point ... */
	__asm__ __volatile__("mov %cr0, %eax\n\t"
			     "and $0x9fffffff,%eax\n\t"
			     "mov %eax, %cr0\n\t");
	/* turns out cache isn't really on until you set MTRR registers on 
	 * 686 and later. 
	 * NOTHING FANCY. Linux does a much better job anyway. 
	 * so absolute minimum needed to get it going. 
	 */
	/* OK, linux it turns out does nothing. We have to do it ... */
#if defined(i686) 
	// totalram here is in linux sizing, i.e. units of KB. 
	// set_mtrr is responsible for getting it into the right units!
	setup_mtrrs(mem);
#endif

	post_code(0x6A);
	printk_info("done.\n");
}

static void interrupts_on()
{
	/* this is so interrupts work. This is very limited scope -- 
	 * linux will do better later, we hope ...
	 */
	/* this is the first way we learned to do it. It fails on real SMP 
	 * stuff. So we have to do things differently ... 
	 * see the Intel mp1.4 spec, page A-3
	 */

#if defined(APIC)
	/* Only Pentium Pro and later have those MSR stuff */
	unsigned long low, high;

	printk_info("Setting up local apic...");

	/* Enable the local apic */
	rdmsr(APIC_BASE_MSR, low, high);
	low |= APIC_BASE_MSR_ENABLE;
	low &= ~APIC_BASE_MSR_ADDR_MASK;
	low |= APIC_DEFAULT_BASE;
	wrmsr(APIC_BASE_MSR, low, high);


	/* Put the local apic in virtual wire mode */
	apic_write_around(APIC_SPIV, 
		(apic_read_around(APIC_SPIV) & ~(APIC_VECTOR_MASK))
		| APIC_SPIV_ENABLE);
	apic_write_around(APIC_LVT0, 
		(apic_read_around(APIC_LVT0) & 
			~(APIC_LVT_MASKED | APIC_LVT_LEVEL_TRIGGER | 
				APIC_LVT_REMOTE_IRR | APIC_INPUT_POLARITY | 
				APIC_SEND_PENDING |APIC_LVT_RESERVED_1 |
				APIC_DELIVERY_MODE_MASK))
		| (APIC_LVT_REMOTE_IRR |APIC_SEND_PENDING | 
			APIC_DELIVERY_MODE_EXTINT)
		);
	apic_write_around(APIC_LVT1, 
		(apic_read_around(APIC_LVT1) & 
			~(APIC_LVT_MASKED | APIC_LVT_LEVEL_TRIGGER | 
				APIC_LVT_REMOTE_IRR | APIC_INPUT_POLARITY | 
				APIC_SEND_PENDING |APIC_LVT_RESERVED_1 |
				APIC_DELIVERY_MODE_MASK))
		| (APIC_LVT_REMOTE_IRR |APIC_SEND_PENDING | 
			APIC_DELIVERY_MODE_NMI)
		);
#else /* APIC */
#ifdef i686
	/* Only Pentium Pro and later have those MSR stuff */
	unsigned long low, high;

	printk_info("Disabling local apic...");

	rdmsr(APIC_BASE_MSR, low, high);
	low &= ~APIC_BASE_MSR_ENABLE;
	wrmsr(APIC_BASE_MSR, low, high);
#endif /* i686 */
#endif /* APIC */
	printk_info("done.\n");
	post_code(0x9b);
}

unsigned long cpu_initialize(struct mem_range *mem)
{
	/* Because we busy wait at the printk spinlock.
	 * It is important to keep the number of printed messages
	 * from secondary cpus to a minimum, when debugging is
	 * disabled.
	 */
	unsigned long processor_id = this_processors_id();
	printk_notice("Initializing CPU #%d\n", processor_id);
	
	/* some cpus need a fixup done. This is the hook for doing that. */
	cpufixup(mem);

	/* Turn on caching if we haven't already */
	cache_on(mem);

	display_cpuid();
	mtrr_check();

#if 0
	/* now that everything is really up, enable the l2 cache if desired. 
	 * The enable can wait until this point, because linuxbios and it's
	 * data areas are tiny, easily fitting into the L1 cache. 
	 */
	configure_l2_cache();
#endif
	interrupts_on();
	printk_info("CPU #%d Initialized\n", processor_id);
	return processor_id;
}