forked from woai3c/MIT6.828
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmpconfig.c
225 lines (200 loc) · 6.18 KB
/
mpconfig.c
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Search for and parse the multiprocessor configuration table
// See http://developer.intel.com/design/pentium/datashts/24201606.pdf
#include <inc/types.h>
#include <inc/string.h>
#include <inc/memlayout.h>
#include <inc/x86.h>
#include <inc/mmu.h>
#include <inc/env.h>
#include <kern/cpu.h>
#include <kern/pmap.h>
struct CpuInfo cpus[NCPU];
struct CpuInfo *bootcpu;
int ismp;
int ncpu;
// Per-CPU kernel stacks
unsigned char percpu_kstacks[NCPU][KSTKSIZE]
__attribute__ ((aligned(PGSIZE)));
// See MultiProcessor Specification Version 1.[14]
struct mp { // floating pointer [MP 4.1]
uint8_t signature[4]; // "_MP_"
physaddr_t physaddr; // phys addr of MP config table
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t type; // MP system config type
uint8_t imcrp;
uint8_t reserved[3];
} __attribute__((__packed__));
struct mpconf { // configuration table header [MP 4.2]
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
physaddr_t oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
physaddr_t lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
uint8_t reserved;
uint8_t entries[0]; // table entries
} __attribute__((__packed__));
struct mpproc { // processor table entry [MP 4.3.1]
uint8_t type; // entry type (0)
uint8_t apicid; // local APIC id
uint8_t version; // local APIC version
uint8_t flags; // CPU flags
uint8_t signature[4]; // CPU signature
uint32_t feature; // feature flags from CPUID instruction
uint8_t reserved[8];
} __attribute__((__packed__));
// mpproc flags
#define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor
// Table entry types
#define MPPROC 0x00 // One per processor
#define MPBUS 0x01 // One per bus
#define MPIOAPIC 0x02 // One per I/O APIC
#define MPIOINTR 0x03 // One per bus interrupt source
#define MPLINTR 0x04 // One per system interrupt source
static uint8_t
sum(void *addr, int len)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++)
sum += ((uint8_t *)addr)[i];
return sum;
}
// Look for an MP structure in the len bytes at physical address addr.
static struct mp *
mpsearch1(physaddr_t a, int len)
{
struct mp *mp = KADDR(a), *end = KADDR(a + len);
for (; mp < end; mp++)
if (memcmp(mp->signature, "_MP_", 4) == 0 &&
sum(mp, sizeof(*mp)) == 0)
return mp;
return NULL;
}
// Search for the MP Floating Pointer Structure, which according to
// [MP 4] is in one of the following three locations:
// 1) in the first KB of the EBDA;
// 2) if there is no EBDA, in the last KB of system base memory;
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
static struct mp *
mpsearch(void)
{
uint8_t *bda;
uint32_t p;
struct mp *mp;
static_assert(sizeof(*mp) == 16);
// The BIOS data area lives in 16-bit segment 0x40.
bda = (uint8_t *) KADDR(0x40 << 4);
// [MP 4] The 16-bit segment of the EBDA is in the two bytes
// starting at byte 0x0E of the BDA. 0 if not present.
if ((p = *(uint16_t *) (bda + 0x0E))) {
p <<= 4; // Translate from segment to PA
if ((mp = mpsearch1(p, 1024)))
return mp;
} else {
// The size of base memory, in KB is in the two bytes
// starting at 0x13 of the BDA.
p = *(uint16_t *) (bda + 0x13) * 1024;
if ((mp = mpsearch1(p - 1024, 1024)))
return mp;
}
return mpsearch1(0xF0000, 0x10000);
}
// Search for an MP configuration table. For now, don't accept the
// default configurations (physaddr == 0).
// Check for the correct signature, checksum, and version.
static struct mpconf *
mpconfig(struct mp **pmp)
{
struct mpconf *conf;
struct mp *mp;
if ((mp = mpsearch()) == 0)
return NULL;
if (mp->physaddr == 0 || mp->type != 0) {
cprintf("SMP: Default configurations not implemented\n");
return NULL;
}
conf = (struct mpconf *) KADDR(mp->physaddr);
if (memcmp(conf, "PCMP", 4) != 0) {
cprintf("SMP: Incorrect MP configuration table signature\n");
return NULL;
}
if (sum(conf, conf->length) != 0) {
cprintf("SMP: Bad MP configuration checksum\n");
return NULL;
}
if (conf->version != 1 && conf->version != 4) {
cprintf("SMP: Unsupported MP version %d\n", conf->version);
return NULL;
}
if ((sum((uint8_t *)conf + conf->length, conf->xlength) + conf->xchecksum) & 0xff) {
cprintf("SMP: Bad MP configuration extended checksum\n");
return NULL;
}
*pmp = mp;
return conf;
}
void
mp_init(void)
{
struct mp *mp;
struct mpconf *conf;
struct mpproc *proc;
uint8_t *p;
unsigned int i;
bootcpu = &cpus[0];
if ((conf = mpconfig(&mp)) == 0)
return;
ismp = 1;
lapicaddr = conf->lapicaddr;
for (p = conf->entries, i = 0; i < conf->entry; i++) {
switch (*p) {
case MPPROC:
proc = (struct mpproc *)p;
if (proc->flags & MPPROC_BOOT)
bootcpu = &cpus[ncpu];
if (ncpu < NCPU) {
cpus[ncpu].cpu_id = ncpu;
ncpu++;
} else {
cprintf("SMP: too many CPUs, CPU %d disabled\n",
proc->apicid);
}
p += sizeof(struct mpproc);
continue;
case MPBUS:
case MPIOAPIC:
case MPIOINTR:
case MPLINTR:
p += 8;
continue;
default:
cprintf("mpinit: unknown config type %x\n", *p);
ismp = 0;
i = conf->entry;
}
}
bootcpu->cpu_status = CPU_STARTED;
if (!ismp) {
// Didn't like what we found; fall back to no MP.
ncpu = 1;
lapicaddr = 0;
cprintf("SMP: configuration not found, SMP disabled\n");
return;
}
cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu);
if (mp->imcrp) {
// [MP 3.2.6.1] If the hardware implements PIC mode,
// switch to getting interrupts from the LAPIC.
cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n");
outb(0x22, 0x70); // Select IMCR
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
}
}