aboutsummaryrefslogtreecommitdiff
path: root/mptable.c
blob: 6d62cef72963b52d4ed13e2d55a31f54986e3016 (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
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
#include "include/string.h"
#include "bios.h"
#include "fw_cfg.h"
#include "include/mpspec_def.h"

#define MPTABLE_START    0x9fc00
#define APIC_VERSION     0x14
#define MPC_SPEC         0x4

#define MP_IRQDIR_DEFAULT 0
#define MP_IRQDIR_HIGH    1
#define MP_IRQDIR_LOW     3

static const char MPC_OEM[]        = "QBOOT   ";
static const char MPC_PRODUCT_ID[] = "000000000000";
static const char BUS_TYPE_ISA[]   = "ISA   ";

#define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000
#define APIC_DEFAULT_PHYS_BASE    0xfee00000
#define APIC_VERSION              0x14

static int mptable_checksum(char *buf, int size)
{
	int i;
	int sum = 0;

	for (i = 0; i < size; i++) {
		sum += buf[i];
	}

	return sum;
}

static void mptable_get_cpuid(int *signature, int *features)
{
	int ebx, ecx;

	asm("cpuid"
	    : "=a" (*signature), "=b" (ebx), "=c" (ecx), "=d" (*features)
	    : "0" (1));
}

void setup_mptable(void)
{
	struct mpf_intel *mpf;
	struct mpc_table *table;
	struct mpc_cpu *cpu;
	struct mpc_bus *bus;
	struct mpc_ioapic *ioapic;
	struct mpc_intsrc *intsrc;
	struct mpc_lintsrc *lintsrc;
	const char mpc_signature[] = MPC_SIGNATURE;
	const char smp_magic_ident[] = "_MP_";
	int cpuid_stepping, cpuid_features;
	int irq0_override = 0;
	int checksum = 0;
	int offset = 0;
	int num_cpus;
	int ssize;
	int i;

	ssize = sizeof(struct mpf_intel);

	mpf = (struct mpf_intel *) MPTABLE_START;
	memset(mpf, 0, ssize);
	memcpy(mpf->signature, smp_magic_ident, sizeof(smp_magic_ident) - 1);
	mpf->length = 1;
	mpf->specification = 4;
	mpf->physptr = MPTABLE_START + ssize;
	mpf->checksum -= mptable_checksum((char *) mpf, ssize);

	offset += ssize;
	ssize = sizeof(struct mpc_table);

	table = (struct mpc_table *) (MPTABLE_START + offset);
	memset(table, 0, ssize);
	memcpy(table->signature, mpc_signature, sizeof(mpc_signature) - 1);
	table->spec = MPC_SPEC;
	memcpy(table->oem, MPC_OEM, sizeof(MPC_OEM) - 1);
	memcpy(table->productid, MPC_PRODUCT_ID, sizeof(MPC_PRODUCT_ID) - 1);
	table->lapic = APIC_DEFAULT_PHYS_BASE;

	offset += ssize;
	ssize = sizeof(struct mpc_cpu);

	fw_cfg_select(FW_CFG_NB_CPUS);
	num_cpus = fw_cfg_readl_le();
	mptable_get_cpuid(&cpuid_stepping, &cpuid_features);

	for (i = 0; i < num_cpus; i++) {
		cpu = (struct mpc_cpu *) (MPTABLE_START + offset);
		memset(cpu, 0, ssize);
		cpu->type = MP_PROCESSOR;
		cpu->apicid = i;
		cpu->apicver = APIC_VERSION;
		cpu->cpuflag = CPU_ENABLED;
		if (i == 0) {
			cpu->cpuflag |= CPU_BOOTPROCESSOR;
		}
		cpu->cpufeature = cpuid_stepping;
		cpu->featureflag = cpuid_features;
		checksum += mptable_checksum((char *) cpu, ssize);
		offset += ssize;
	}

	ssize = sizeof(struct mpc_bus);

	bus = (struct mpc_bus *) (MPTABLE_START + offset);
	memset(bus, 0, ssize);
	bus->type = MP_BUS;
	bus->busid = 0;
	memcpy(bus->bustype, BUS_TYPE_ISA, sizeof(BUS_TYPE_ISA) - 1);
	checksum += mptable_checksum((char *) bus, ssize);

	offset += ssize;
	ssize = sizeof(struct mpc_ioapic);

	ioapic = (struct mpc_ioapic *) (MPTABLE_START + offset);
	memset(ioapic, 0, ssize);
	ioapic->type = MP_IOAPIC;
	ioapic->apicid = num_cpus + 1;
	ioapic->apicver = APIC_VERSION;
	ioapic->flags = MPC_APIC_USABLE;
	ioapic->apicaddr = IO_APIC_DEFAULT_PHYS_BASE;
	checksum += mptable_checksum((char *) ioapic, ssize);

	offset += ssize;
	ssize = sizeof(struct mpc_intsrc);

	fw_cfg_select(FW_CFG_IRQ0_OVERRIDE);
	irq0_override = fw_cfg_readl_le();

	for (i = 0; i < 16; i++) {
		intsrc = (struct mpc_intsrc *) (MPTABLE_START + offset);
		memset(intsrc, 0, ssize);
		intsrc->type = MP_INTSRC;
		intsrc->irqtype = mp_INT;
		intsrc->irqflag = MP_IRQDIR_DEFAULT;
		intsrc->srcbus = 0;
		intsrc->srcbusirq = i;
		intsrc->dstapic = num_cpus + 1;
		intsrc->dstirq = i;
		if (irq0_override) {
			if (i == 0) {
				intsrc->dstirq = 2;
			} else if (i == 2) {
				// Don't update offset nor checksum
				continue;
			}
		}
		checksum += mptable_checksum((char *) intsrc, ssize);
		offset += ssize;
	}

	ssize = sizeof(struct mpc_lintsrc);

	lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset);
	memset(lintsrc, 0, ssize);
	lintsrc->type = MP_LINTSRC;
	lintsrc->irqtype = mp_ExtINT;
	lintsrc->irqflag = MP_IRQDIR_DEFAULT;
	lintsrc->srcbusid = 0;
	lintsrc->srcbusirq = 0;
	lintsrc->destapic = 0;
	lintsrc->destapiclint = 0;
	checksum += mptable_checksum((char *) lintsrc, ssize);

	offset += ssize;

	lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset);
	lintsrc->type = MP_LINTSRC;
	lintsrc->irqtype = mp_NMI;
	lintsrc->irqflag = MP_IRQDIR_DEFAULT;
	lintsrc->srcbusid = 0;
	lintsrc->srcbusirq = 0;
	lintsrc->destapic = 0xFF;
	lintsrc->destapiclint = 1;
	checksum += mptable_checksum((char *) lintsrc, ssize);

	offset += ssize;
	ssize = sizeof(struct mpc_table);

	table->length = offset - sizeof(struct mpf_intel);
	checksum += mptable_checksum((char *) table, ssize);
	table->checksum -= checksum;
}