xref: /kvmtool/x86/mptable.c (revision 0c7c14a747e9eb2c3cacef60fb74b0698c9d3adf)
1*0c7c14a7SCyrill Gorcunov #include "kvm/kvm.h"
2*0c7c14a7SCyrill Gorcunov #include "kvm/bios.h"
3*0c7c14a7SCyrill Gorcunov #include "kvm/apic.h"
4*0c7c14a7SCyrill Gorcunov #include "kvm/mptable.h"
5*0c7c14a7SCyrill Gorcunov #include "kvm/util.h"
6*0c7c14a7SCyrill Gorcunov 
7*0c7c14a7SCyrill Gorcunov #include <string.h>
8*0c7c14a7SCyrill Gorcunov 
9*0c7c14a7SCyrill Gorcunov #include <asm/mpspec_def.h>
10*0c7c14a7SCyrill Gorcunov 
11*0c7c14a7SCyrill Gorcunov /*
12*0c7c14a7SCyrill Gorcunov  * FIXME: please make sure the addresses borrowed
13*0c7c14a7SCyrill Gorcunov  * for apic/ioapic never overlaped! We need a global
14*0c7c14a7SCyrill Gorcunov  * tracker of system resources (including io, mmio,
15*0c7c14a7SCyrill Gorcunov  * and friends).
16*0c7c14a7SCyrill Gorcunov  */
17*0c7c14a7SCyrill Gorcunov 
18*0c7c14a7SCyrill Gorcunov static unsigned int mpf_checksum(unsigned char *mp, int len)
19*0c7c14a7SCyrill Gorcunov {
20*0c7c14a7SCyrill Gorcunov 	unsigned int sum = 0;
21*0c7c14a7SCyrill Gorcunov 
22*0c7c14a7SCyrill Gorcunov 	while (len--)
23*0c7c14a7SCyrill Gorcunov 		sum += *mp++;
24*0c7c14a7SCyrill Gorcunov 
25*0c7c14a7SCyrill Gorcunov 	return sum & 0xFF;
26*0c7c14a7SCyrill Gorcunov }
27*0c7c14a7SCyrill Gorcunov 
28*0c7c14a7SCyrill Gorcunov static unsigned int gen_cpu_flag(unsigned int cpu, unsigned int ncpu)
29*0c7c14a7SCyrill Gorcunov {
30*0c7c14a7SCyrill Gorcunov 	/* sets enabled/disabled | BSP/AP processor */
31*0c7c14a7SCyrill Gorcunov 	return ( (cpu < ncpu) ? CPU_ENABLED       : 0) |
32*0c7c14a7SCyrill Gorcunov 		((cpu == 0)   ? CPU_BOOTPROCESSOR : 0x00);
33*0c7c14a7SCyrill Gorcunov }
34*0c7c14a7SCyrill Gorcunov 
35*0c7c14a7SCyrill Gorcunov #define MPTABLE_SIG_FLOATING	"_MP_"
36*0c7c14a7SCyrill Gorcunov #define MPTABLE_OEM		"KVMCPU00"
37*0c7c14a7SCyrill Gorcunov #define MPTABLE_PRODUCTID	"0.1         "
38*0c7c14a7SCyrill Gorcunov #define MPTABLE_PCIBUSTYPE	"PCI   "
39*0c7c14a7SCyrill Gorcunov #define MPTABLE_ISABUSTYPE	"ISA   "
40*0c7c14a7SCyrill Gorcunov 
41*0c7c14a7SCyrill Gorcunov #define MPTABLE_STRNCPY(d, s)	memcpy(d, s, sizeof(d))
42*0c7c14a7SCyrill Gorcunov 
43*0c7c14a7SCyrill Gorcunov /* It should be more than enough */
44*0c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_SIZE	(32 << 20)
45*0c7c14a7SCyrill Gorcunov 
46*0c7c14a7SCyrill Gorcunov /*
47*0c7c14a7SCyrill Gorcunov  * Too many cpus will require x2apic mode
48*0c7c14a7SCyrill Gorcunov  * and rather ACPI support so we limit it
49*0c7c14a7SCyrill Gorcunov  * here for a while.
50*0c7c14a7SCyrill Gorcunov  */
51*0c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_CPUS	255
52*0c7c14a7SCyrill Gorcunov 
53*0c7c14a7SCyrill Gorcunov /**
54*0c7c14a7SCyrill Gorcunov  * mptable_setup - create mptable and fill guest memory with it
55*0c7c14a7SCyrill Gorcunov  */
56*0c7c14a7SCyrill Gorcunov void mptable_setup(struct kvm *kvm, unsigned int ncpus)
57*0c7c14a7SCyrill Gorcunov {
58*0c7c14a7SCyrill Gorcunov 	unsigned long real_mpc_table, size;
59*0c7c14a7SCyrill Gorcunov 	struct mpf_intel *mpf_intel;
60*0c7c14a7SCyrill Gorcunov 	struct mpc_table *mpc_table;
61*0c7c14a7SCyrill Gorcunov 	struct mpc_cpu *mpc_cpu;
62*0c7c14a7SCyrill Gorcunov 	struct mpc_bus *mpc_bus;
63*0c7c14a7SCyrill Gorcunov 	struct mpc_ioapic *mpc_ioapic;
64*0c7c14a7SCyrill Gorcunov 	struct mpc_intsrc *mpc_intsrc;
65*0c7c14a7SCyrill Gorcunov 
66*0c7c14a7SCyrill Gorcunov 	const int pcibusid = 0;
67*0c7c14a7SCyrill Gorcunov 	const int isabusid = 1;
68*0c7c14a7SCyrill Gorcunov 
69*0c7c14a7SCyrill Gorcunov 	unsigned int i, nentries = 0;
70*0c7c14a7SCyrill Gorcunov 	unsigned int ioapicid;
71*0c7c14a7SCyrill Gorcunov 	void *last_addr;
72*0c7c14a7SCyrill Gorcunov 
73*0c7c14a7SCyrill Gorcunov 	/* That is where MP table will be in guest memory */
74*0c7c14a7SCyrill Gorcunov 	real_mpc_table = ALIGN(MB_BIOS_BEGIN + bios_rom_size, 16);
75*0c7c14a7SCyrill Gorcunov 
76*0c7c14a7SCyrill Gorcunov 	if (ncpus > MPTABLE_MAX_CPUS) {
77*0c7c14a7SCyrill Gorcunov 		warning("Too many cpus: %d limited to %d",
78*0c7c14a7SCyrill Gorcunov 			ncpus, MPTABLE_MAX_CPUS);
79*0c7c14a7SCyrill Gorcunov 		ncpus = MPTABLE_MAX_CPUS;
80*0c7c14a7SCyrill Gorcunov 	}
81*0c7c14a7SCyrill Gorcunov 
82*0c7c14a7SCyrill Gorcunov 	mpc_table = calloc(1, MPTABLE_MAX_SIZE);
83*0c7c14a7SCyrill Gorcunov 	if (!mpc_table)
84*0c7c14a7SCyrill Gorcunov 		die("Out of memory");
85*0c7c14a7SCyrill Gorcunov 
86*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpc_table->signature,	MPC_SIGNATURE);
87*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpc_table->oem,		MPTABLE_OEM);
88*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpc_table->productid,	MPTABLE_PRODUCTID);
89*0c7c14a7SCyrill Gorcunov 
90*0c7c14a7SCyrill Gorcunov 	mpc_table->spec		= 4;
91*0c7c14a7SCyrill Gorcunov 	mpc_table->lapic	= APIC_ADDR(0);
92*0c7c14a7SCyrill Gorcunov 	mpc_table->oemcount	= ncpus; /* will be updated again at end */
93*0c7c14a7SCyrill Gorcunov 
94*0c7c14a7SCyrill Gorcunov 	/*
95*0c7c14a7SCyrill Gorcunov 	 * CPUs enumeration. Technically speaking we should
96*0c7c14a7SCyrill Gorcunov 	 * ask either host or HV for apic version supported
97*0c7c14a7SCyrill Gorcunov 	 * but for a while we simply put some random value
98*0c7c14a7SCyrill Gorcunov 	 * here.
99*0c7c14a7SCyrill Gorcunov 	 */
100*0c7c14a7SCyrill Gorcunov 	mpc_cpu = (void *)&mpc_table[1];
101*0c7c14a7SCyrill Gorcunov 	for (i = 0; i < ncpus; i++) {
102*0c7c14a7SCyrill Gorcunov 		mpc_cpu->type		= MP_PROCESSOR;
103*0c7c14a7SCyrill Gorcunov 		mpc_cpu->apicid		= i;
104*0c7c14a7SCyrill Gorcunov 		mpc_cpu->apicver	= KVM_APIC_VERSION;
105*0c7c14a7SCyrill Gorcunov 		mpc_cpu->cpuflag	= gen_cpu_flag(i, ncpus);
106*0c7c14a7SCyrill Gorcunov 		mpc_cpu->cpufeature	= 0x600; /* some default value */
107*0c7c14a7SCyrill Gorcunov 		mpc_cpu->featureflag	= 0x201; /* some default value */
108*0c7c14a7SCyrill Gorcunov 		mpc_cpu++;
109*0c7c14a7SCyrill Gorcunov 	}
110*0c7c14a7SCyrill Gorcunov 
111*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)mpc_cpu;
112*0c7c14a7SCyrill Gorcunov 	nentries += ncpus;
113*0c7c14a7SCyrill Gorcunov 
114*0c7c14a7SCyrill Gorcunov 	/*
115*0c7c14a7SCyrill Gorcunov 	 * PCI buses.
116*0c7c14a7SCyrill Gorcunov 	 * FIXME: Some callback here to obtain real number
117*0c7c14a7SCyrill Gorcunov 	 * of PCI buses present in system.
118*0c7c14a7SCyrill Gorcunov 	 */
119*0c7c14a7SCyrill Gorcunov 	mpc_bus		= last_addr;
120*0c7c14a7SCyrill Gorcunov 	mpc_bus->type	= MP_BUS;
121*0c7c14a7SCyrill Gorcunov 	mpc_bus->busid	= pcibusid;
122*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_PCIBUSTYPE);
123*0c7c14a7SCyrill Gorcunov 
124*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_bus[1];
125*0c7c14a7SCyrill Gorcunov 	nentries++;
126*0c7c14a7SCyrill Gorcunov 
127*0c7c14a7SCyrill Gorcunov 	/*
128*0c7c14a7SCyrill Gorcunov 	 * ISA bus.
129*0c7c14a7SCyrill Gorcunov 	 * FIXME: Same issue as for PCI bus.
130*0c7c14a7SCyrill Gorcunov 	 */
131*0c7c14a7SCyrill Gorcunov 	mpc_bus		= last_addr;
132*0c7c14a7SCyrill Gorcunov 	mpc_bus->type	= MP_BUS;
133*0c7c14a7SCyrill Gorcunov 	mpc_bus->busid	= isabusid;
134*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_ISABUSTYPE);
135*0c7c14a7SCyrill Gorcunov 
136*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_bus[1];
137*0c7c14a7SCyrill Gorcunov 	nentries++;
138*0c7c14a7SCyrill Gorcunov 
139*0c7c14a7SCyrill Gorcunov 	/*
140*0c7c14a7SCyrill Gorcunov 	 * IO-APIC chip.
141*0c7c14a7SCyrill Gorcunov 	 */
142*0c7c14a7SCyrill Gorcunov 	ioapicid		= ncpus + 1;
143*0c7c14a7SCyrill Gorcunov 	mpc_ioapic		= last_addr;
144*0c7c14a7SCyrill Gorcunov 	mpc_ioapic->type	= MP_IOAPIC;
145*0c7c14a7SCyrill Gorcunov 	mpc_ioapic->apicid	= ioapicid;
146*0c7c14a7SCyrill Gorcunov 	mpc_ioapic->apicver	= KVM_APIC_VERSION;
147*0c7c14a7SCyrill Gorcunov 	mpc_ioapic->flags	= MPC_APIC_USABLE;
148*0c7c14a7SCyrill Gorcunov 	mpc_ioapic->apicaddr	= IOAPIC_ADDR(0);
149*0c7c14a7SCyrill Gorcunov 
150*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_ioapic[1];
151*0c7c14a7SCyrill Gorcunov 	nentries++;
152*0c7c14a7SCyrill Gorcunov 
153*0c7c14a7SCyrill Gorcunov 	/*
154*0c7c14a7SCyrill Gorcunov 	 * IRQ sources.
155*0c7c14a7SCyrill Gorcunov 	 *
156*0c7c14a7SCyrill Gorcunov 	 * FIXME: Same issue as with buses. We definitely
157*0c7c14a7SCyrill Gorcunov 	 * need kind of collector routine which enumerate
158*0c7c14a7SCyrill Gorcunov 	 * resources used first and pass them here.
159*0c7c14a7SCyrill Gorcunov 	 * At moment we know we have only virtio block device
160*0c7c14a7SCyrill Gorcunov 	 * and virtio console but this is g00berfish.
161*0c7c14a7SCyrill Gorcunov 	 *
162*0c7c14a7SCyrill Gorcunov 	 * Also note we use PCI irqs here, no for ISA bus yet.
163*0c7c14a7SCyrill Gorcunov 	 */
164*0c7c14a7SCyrill Gorcunov 	mpc_intsrc		= last_addr;
165*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->type	= MP_INTSRC;
166*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_INT;
167*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqflag	= MP_IRQDIR_DEFAULT;
168*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbus	= pcibusid;
169*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbusirq	= 2; /* virtio console irq pin */
170*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstapic	= ioapicid;
171*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstirq	= 13; /* VIRTIO_CONSOLE_IRQ */
172*0c7c14a7SCyrill Gorcunov 
173*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_intsrc[1];
174*0c7c14a7SCyrill Gorcunov 	nentries++;
175*0c7c14a7SCyrill Gorcunov 
176*0c7c14a7SCyrill Gorcunov 	mpc_intsrc		= last_addr;
177*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->type	= MP_INTSRC;
178*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_INT;
179*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqflag	= MP_IRQDIR_DEFAULT;
180*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbus	= pcibusid;
181*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbusirq	= 1; /* virtio block irq pin */
182*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstapic	= ioapicid;
183*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstirq	= 15; /* VIRTIO_BLK_IRQ */
184*0c7c14a7SCyrill Gorcunov 
185*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_intsrc[1];
186*0c7c14a7SCyrill Gorcunov 	nentries++;
187*0c7c14a7SCyrill Gorcunov 
188*0c7c14a7SCyrill Gorcunov 	mpc_intsrc		= last_addr;
189*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->type	= MP_INTSRC;
190*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_INT;
191*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqflag	= MP_IRQDIR_DEFAULT;
192*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbus	= pcibusid;
193*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbusirq	= 3; /* virtio net irq pin */
194*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstapic	= ioapicid;
195*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstirq	= 14; /* VIRTIO_NET_IRQ */
196*0c7c14a7SCyrill Gorcunov 
197*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_intsrc[1];
198*0c7c14a7SCyrill Gorcunov 	nentries++;
199*0c7c14a7SCyrill Gorcunov 
200*0c7c14a7SCyrill Gorcunov 	/*
201*0c7c14a7SCyrill Gorcunov 	 * Local IRQs assignment (LINT0, LINT1)
202*0c7c14a7SCyrill Gorcunov 	 */
203*0c7c14a7SCyrill Gorcunov 	mpc_intsrc		= last_addr;
204*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->type	= MP_LINTSRC;
205*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_ExtINT;
206*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_INT;
207*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqflag	= MP_IRQDIR_DEFAULT;
208*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbus	= isabusid;
209*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbusirq	= 0;
210*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstapic	= 0; /* FIXME: BSP apic */
211*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstirq	= 0; /* LINT0 */
212*0c7c14a7SCyrill Gorcunov 
213*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_intsrc[1];
214*0c7c14a7SCyrill Gorcunov 	nentries++;
215*0c7c14a7SCyrill Gorcunov 
216*0c7c14a7SCyrill Gorcunov 	mpc_intsrc		= last_addr;
217*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->type	= MP_LINTSRC;
218*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqtype	= mp_NMI;
219*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->irqflag	= MP_IRQDIR_DEFAULT;
220*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbus	= isabusid;
221*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->srcbusirq	= 0;
222*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstapic	= 0; /* FIXME: BSP apic */
223*0c7c14a7SCyrill Gorcunov 	mpc_intsrc->dstirq	= 1; /* LINT1 */
224*0c7c14a7SCyrill Gorcunov 
225*0c7c14a7SCyrill Gorcunov 	last_addr = (void *)&mpc_intsrc[1];
226*0c7c14a7SCyrill Gorcunov 	nentries++;
227*0c7c14a7SCyrill Gorcunov 
228*0c7c14a7SCyrill Gorcunov 	/*
229*0c7c14a7SCyrill Gorcunov 	 * Floating MP table finally.
230*0c7c14a7SCyrill Gorcunov 	 */
231*0c7c14a7SCyrill Gorcunov 	mpf_intel = (void *)ALIGN((unsigned long)last_addr, 16);
232*0c7c14a7SCyrill Gorcunov 
233*0c7c14a7SCyrill Gorcunov 	MPTABLE_STRNCPY(mpf_intel->signature, MPTABLE_SIG_FLOATING);
234*0c7c14a7SCyrill Gorcunov 	mpf_intel->length	= 1;
235*0c7c14a7SCyrill Gorcunov 	mpf_intel->specification= 4;
236*0c7c14a7SCyrill Gorcunov 	mpf_intel->physptr	= (unsigned int)real_mpc_table;
237*0c7c14a7SCyrill Gorcunov 	mpf_intel->checksum	= -mpf_checksum((unsigned char *)mpf_intel, sizeof(*mpf_intel));
238*0c7c14a7SCyrill Gorcunov 
239*0c7c14a7SCyrill Gorcunov 	/*
240*0c7c14a7SCyrill Gorcunov 	 * No last_addr inclrement here please, we need last
241*0c7c14a7SCyrill Gorcunov 	 * active position here to compute table size.
242*0c7c14a7SCyrill Gorcunov 	 */
243*0c7c14a7SCyrill Gorcunov 
244*0c7c14a7SCyrill Gorcunov 	/*
245*0c7c14a7SCyrill Gorcunov 	 * Don't forget to update header in fixed table.
246*0c7c14a7SCyrill Gorcunov 	*/
247*0c7c14a7SCyrill Gorcunov 	mpc_table->oemcount	= nentries;
248*0c7c14a7SCyrill Gorcunov 	mpc_table->length	= last_addr - (void *)mpc_table;
249*0c7c14a7SCyrill Gorcunov 	mpc_table->checksum	= -mpf_checksum((unsigned char *)mpc_table, mpc_table->length);
250*0c7c14a7SCyrill Gorcunov 
251*0c7c14a7SCyrill Gorcunov 
252*0c7c14a7SCyrill Gorcunov 	/*
253*0c7c14a7SCyrill Gorcunov 	 * We will copy the whole table, no need to separate
254*0c7c14a7SCyrill Gorcunov 	 * floating structure and table itself.
255*0c7c14a7SCyrill Gorcunov 	 */
256*0c7c14a7SCyrill Gorcunov 	size = (unsigned long)mpf_intel + sizeof(*mpf_intel) - (unsigned long)mpc_table;
257*0c7c14a7SCyrill Gorcunov 
258*0c7c14a7SCyrill Gorcunov 	/*
259*0c7c14a7SCyrill Gorcunov 	 * The finial check -- never get out of system bios
260*0c7c14a7SCyrill Gorcunov 	 * area. Lets also check for allocated memory overrun,
261*0c7c14a7SCyrill Gorcunov 	 * in real it's late but still usefull.
262*0c7c14a7SCyrill Gorcunov 	 */
263*0c7c14a7SCyrill Gorcunov 
264*0c7c14a7SCyrill Gorcunov 	if (size > (unsigned long)(MB_BIOS_END - bios_rom_size) ||
265*0c7c14a7SCyrill Gorcunov 	    size > MPTABLE_MAX_SIZE)
266*0c7c14a7SCyrill Gorcunov 		die("MP table is too big");
267*0c7c14a7SCyrill Gorcunov 
268*0c7c14a7SCyrill Gorcunov 	/*
269*0c7c14a7SCyrill Gorcunov 	 * OK, it is time to move it to guest memory.
270*0c7c14a7SCyrill Gorcunov 	 */
271*0c7c14a7SCyrill Gorcunov 	memcpy(guest_flat_to_host(kvm, real_mpc_table), mpc_table, size);
272*0c7c14a7SCyrill Gorcunov 
273*0c7c14a7SCyrill Gorcunov 	free(mpc_table);
274*0c7c14a7SCyrill Gorcunov }
275