10c7c14a7SCyrill Gorcunov #include "kvm/kvm.h"
20c7c14a7SCyrill Gorcunov #include "kvm/bios.h"
30c7c14a7SCyrill Gorcunov #include "kvm/apic.h"
40c7c14a7SCyrill Gorcunov #include "kvm/mptable.h"
50c7c14a7SCyrill Gorcunov #include "kvm/util.h"
6f83cd161SWill Deacon #include "kvm/devices.h"
7f83cd161SWill Deacon #include "kvm/pci.h"
80c7c14a7SCyrill Gorcunov
9ab9e4f1cSSasha Levin #include <linux/kernel.h>
100c7c14a7SCyrill Gorcunov #include <string.h>
110c7c14a7SCyrill Gorcunov
120c7c14a7SCyrill Gorcunov #include <asm/mpspec_def.h>
134749e795SSasha Levin #include <linux/types.h>
140c7c14a7SCyrill Gorcunov
150c7c14a7SCyrill Gorcunov /*
160c7c14a7SCyrill Gorcunov * FIXME: please make sure the addresses borrowed
170c7c14a7SCyrill Gorcunov * for apic/ioapic never overlaped! We need a global
180c7c14a7SCyrill Gorcunov * tracker of system resources (including io, mmio,
190c7c14a7SCyrill Gorcunov * and friends).
200c7c14a7SCyrill Gorcunov */
210c7c14a7SCyrill Gorcunov
mpf_checksum(unsigned char * mp,int len)220c7c14a7SCyrill Gorcunov static unsigned int mpf_checksum(unsigned char *mp, int len)
230c7c14a7SCyrill Gorcunov {
240c7c14a7SCyrill Gorcunov unsigned int sum = 0;
250c7c14a7SCyrill Gorcunov
260c7c14a7SCyrill Gorcunov while (len--)
270c7c14a7SCyrill Gorcunov sum += *mp++;
280c7c14a7SCyrill Gorcunov
290c7c14a7SCyrill Gorcunov return sum & 0xFF;
300c7c14a7SCyrill Gorcunov }
310c7c14a7SCyrill Gorcunov
gen_cpu_flag(unsigned int cpu,unsigned int ncpu)320c7c14a7SCyrill Gorcunov static unsigned int gen_cpu_flag(unsigned int cpu, unsigned int ncpu)
330c7c14a7SCyrill Gorcunov {
340c7c14a7SCyrill Gorcunov /* sets enabled/disabled | BSP/AP processor */
350c7c14a7SCyrill Gorcunov return ( (cpu < ncpu) ? CPU_ENABLED : 0) |
360c7c14a7SCyrill Gorcunov ((cpu == 0) ? CPU_BOOTPROCESSOR : 0x00);
370c7c14a7SCyrill Gorcunov }
380c7c14a7SCyrill Gorcunov
390c7c14a7SCyrill Gorcunov #define MPTABLE_SIG_FLOATING "_MP_"
400c7c14a7SCyrill Gorcunov #define MPTABLE_OEM "KVMCPU00"
410c7c14a7SCyrill Gorcunov #define MPTABLE_PRODUCTID "0.1 "
420c7c14a7SCyrill Gorcunov #define MPTABLE_PCIBUSTYPE "PCI "
430c7c14a7SCyrill Gorcunov #define MPTABLE_ISABUSTYPE "ISA "
440c7c14a7SCyrill Gorcunov
450c7c14a7SCyrill Gorcunov #define MPTABLE_STRNCPY(d, s) memcpy(d, s, sizeof(d))
460c7c14a7SCyrill Gorcunov
470c7c14a7SCyrill Gorcunov /* It should be more than enough */
480c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_SIZE (32 << 20)
490c7c14a7SCyrill Gorcunov
500c7c14a7SCyrill Gorcunov /*
510c7c14a7SCyrill Gorcunov * Too many cpus will require x2apic mode
520c7c14a7SCyrill Gorcunov * and rather ACPI support so we limit it
530c7c14a7SCyrill Gorcunov * here for a while.
540c7c14a7SCyrill Gorcunov */
550c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_CPUS 255
560c7c14a7SCyrill Gorcunov
mptable_add_irq_src(struct mpc_intsrc * mpc_intsrc,u16 srcbusid,u16 srcbusirq,u16 dstapic,u16 dstirq)574749e795SSasha Levin static void mptable_add_irq_src(struct mpc_intsrc *mpc_intsrc,
584749e795SSasha Levin u16 srcbusid, u16 srcbusirq,
594749e795SSasha Levin u16 dstapic, u16 dstirq)
604749e795SSasha Levin {
614749e795SSasha Levin *mpc_intsrc = (struct mpc_intsrc) {
624749e795SSasha Levin .type = MP_INTSRC,
634749e795SSasha Levin .irqtype = mp_INT,
644749e795SSasha Levin .irqflag = MP_IRQDIR_DEFAULT,
654749e795SSasha Levin .srcbus = srcbusid,
664749e795SSasha Levin .srcbusirq = srcbusirq,
674749e795SSasha Levin .dstapic = dstapic,
684749e795SSasha Levin .dstirq = dstirq
694749e795SSasha Levin };
704749e795SSasha Levin }
714749e795SSasha Levin
720c7c14a7SCyrill Gorcunov /**
730c7c14a7SCyrill Gorcunov * mptable_setup - create mptable and fill guest memory with it
740c7c14a7SCyrill Gorcunov */
mptable__init(struct kvm * kvm)751add9f73SSasha Levin int mptable__init(struct kvm *kvm)
760c7c14a7SCyrill Gorcunov {
77c13efdd6SCyrill Gorcunov unsigned long real_mpc_table, real_mpf_intel, size;
780c7c14a7SCyrill Gorcunov struct mpf_intel *mpf_intel;
790c7c14a7SCyrill Gorcunov struct mpc_table *mpc_table;
800c7c14a7SCyrill Gorcunov struct mpc_cpu *mpc_cpu;
810c7c14a7SCyrill Gorcunov struct mpc_bus *mpc_bus;
820c7c14a7SCyrill Gorcunov struct mpc_ioapic *mpc_ioapic;
830c7c14a7SCyrill Gorcunov struct mpc_intsrc *mpc_intsrc;
84f83cd161SWill Deacon struct device_header *dev_hdr;
850c7c14a7SCyrill Gorcunov
860c7c14a7SCyrill Gorcunov const int pcibusid = 0;
870c7c14a7SCyrill Gorcunov const int isabusid = 1;
880c7c14a7SCyrill Gorcunov
891add9f73SSasha Levin unsigned int i, nentries = 0, ncpus = kvm->nrcpus;
900c7c14a7SCyrill Gorcunov unsigned int ioapicid;
910c7c14a7SCyrill Gorcunov void *last_addr;
920c7c14a7SCyrill Gorcunov
930c7c14a7SCyrill Gorcunov /* That is where MP table will be in guest memory */
940c7c14a7SCyrill Gorcunov real_mpc_table = ALIGN(MB_BIOS_BEGIN + bios_rom_size, 16);
950c7c14a7SCyrill Gorcunov
960c7c14a7SCyrill Gorcunov if (ncpus > MPTABLE_MAX_CPUS) {
974542f276SCyrill Gorcunov pr_warning("Too many cpus: %d limited to %d",
980c7c14a7SCyrill Gorcunov ncpus, MPTABLE_MAX_CPUS);
990c7c14a7SCyrill Gorcunov ncpus = MPTABLE_MAX_CPUS;
1000c7c14a7SCyrill Gorcunov }
1010c7c14a7SCyrill Gorcunov
1020c7c14a7SCyrill Gorcunov mpc_table = calloc(1, MPTABLE_MAX_SIZE);
1030c7c14a7SCyrill Gorcunov if (!mpc_table)
1041add9f73SSasha Levin return -ENOMEM;
1050c7c14a7SCyrill Gorcunov
1060c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->signature, MPC_SIGNATURE);
1070c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->oem, MPTABLE_OEM);
1080c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->productid, MPTABLE_PRODUCTID);
1090c7c14a7SCyrill Gorcunov
1100c7c14a7SCyrill Gorcunov mpc_table->spec = 4;
1110c7c14a7SCyrill Gorcunov mpc_table->lapic = APIC_ADDR(0);
1120c7c14a7SCyrill Gorcunov mpc_table->oemcount = ncpus; /* will be updated again at end */
1130c7c14a7SCyrill Gorcunov
1140c7c14a7SCyrill Gorcunov /*
1150c7c14a7SCyrill Gorcunov * CPUs enumeration. Technically speaking we should
1160c7c14a7SCyrill Gorcunov * ask either host or HV for apic version supported
1170c7c14a7SCyrill Gorcunov * but for a while we simply put some random value
1180c7c14a7SCyrill Gorcunov * here.
1190c7c14a7SCyrill Gorcunov */
1200c7c14a7SCyrill Gorcunov mpc_cpu = (void *)&mpc_table[1];
1210c7c14a7SCyrill Gorcunov for (i = 0; i < ncpus; i++) {
1220c7c14a7SCyrill Gorcunov mpc_cpu->type = MP_PROCESSOR;
1230c7c14a7SCyrill Gorcunov mpc_cpu->apicid = i;
1240c7c14a7SCyrill Gorcunov mpc_cpu->apicver = KVM_APIC_VERSION;
1250c7c14a7SCyrill Gorcunov mpc_cpu->cpuflag = gen_cpu_flag(i, ncpus);
1260c7c14a7SCyrill Gorcunov mpc_cpu->cpufeature = 0x600; /* some default value */
1270c7c14a7SCyrill Gorcunov mpc_cpu->featureflag = 0x201; /* some default value */
1280c7c14a7SCyrill Gorcunov mpc_cpu++;
1290c7c14a7SCyrill Gorcunov }
1300c7c14a7SCyrill Gorcunov
1310c7c14a7SCyrill Gorcunov last_addr = (void *)mpc_cpu;
1320c7c14a7SCyrill Gorcunov nentries += ncpus;
1330c7c14a7SCyrill Gorcunov
1340c7c14a7SCyrill Gorcunov /*
1350c7c14a7SCyrill Gorcunov * PCI buses.
1360c7c14a7SCyrill Gorcunov * FIXME: Some callback here to obtain real number
1370c7c14a7SCyrill Gorcunov * of PCI buses present in system.
1380c7c14a7SCyrill Gorcunov */
1390c7c14a7SCyrill Gorcunov mpc_bus = last_addr;
1400c7c14a7SCyrill Gorcunov mpc_bus->type = MP_BUS;
1410c7c14a7SCyrill Gorcunov mpc_bus->busid = pcibusid;
1420c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_PCIBUSTYPE);
1430c7c14a7SCyrill Gorcunov
1440c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_bus[1];
1450c7c14a7SCyrill Gorcunov nentries++;
1460c7c14a7SCyrill Gorcunov
1470c7c14a7SCyrill Gorcunov /*
1480c7c14a7SCyrill Gorcunov * ISA bus.
1490c7c14a7SCyrill Gorcunov * FIXME: Same issue as for PCI bus.
1500c7c14a7SCyrill Gorcunov */
1510c7c14a7SCyrill Gorcunov mpc_bus = last_addr;
1520c7c14a7SCyrill Gorcunov mpc_bus->type = MP_BUS;
1530c7c14a7SCyrill Gorcunov mpc_bus->busid = isabusid;
1540c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_ISABUSTYPE);
1550c7c14a7SCyrill Gorcunov
1560c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_bus[1];
1570c7c14a7SCyrill Gorcunov nentries++;
1580c7c14a7SCyrill Gorcunov
1590c7c14a7SCyrill Gorcunov /*
1600c7c14a7SCyrill Gorcunov * IO-APIC chip.
1610c7c14a7SCyrill Gorcunov */
1620c7c14a7SCyrill Gorcunov ioapicid = ncpus + 1;
1630c7c14a7SCyrill Gorcunov mpc_ioapic = last_addr;
1640c7c14a7SCyrill Gorcunov mpc_ioapic->type = MP_IOAPIC;
1650c7c14a7SCyrill Gorcunov mpc_ioapic->apicid = ioapicid;
1660c7c14a7SCyrill Gorcunov mpc_ioapic->apicver = KVM_APIC_VERSION;
1670c7c14a7SCyrill Gorcunov mpc_ioapic->flags = MPC_APIC_USABLE;
1680c7c14a7SCyrill Gorcunov mpc_ioapic->apicaddr = IOAPIC_ADDR(0);
1690c7c14a7SCyrill Gorcunov
1700c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_ioapic[1];
1710c7c14a7SCyrill Gorcunov nentries++;
1720c7c14a7SCyrill Gorcunov
1730c7c14a7SCyrill Gorcunov /*
1740c7c14a7SCyrill Gorcunov * IRQ sources.
1750c7c14a7SCyrill Gorcunov * Also note we use PCI irqs here, no for ISA bus yet.
1760c7c14a7SCyrill Gorcunov */
1778ec7e042SSasha Levin
178f83cd161SWill Deacon dev_hdr = device__first_dev(DEVICE_BUS_PCI);
179f83cd161SWill Deacon while (dev_hdr) {
18068791d77SCyrill Gorcunov unsigned char srcbusirq;
181f83cd161SWill Deacon struct pci_device_header *pci_hdr = dev_hdr->data;
18268791d77SCyrill Gorcunov
183f83cd161SWill Deacon srcbusirq = (pci_hdr->subsys_id << 2) | (pci_hdr->irq_pin - 1);
1840c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr;
185f83cd161SWill Deacon mptable_add_irq_src(mpc_intsrc, pcibusid, srcbusirq, ioapicid, pci_hdr->irq_line);
1860c7c14a7SCyrill Gorcunov
187*d4d6f153SMuchun Song last_addr = (void *)&mpc_intsrc[1];
1880c7c14a7SCyrill Gorcunov nentries++;
189f83cd161SWill Deacon dev_hdr = device__next_dev(dev_hdr);
1908ec7e042SSasha Levin }
1910c7c14a7SCyrill Gorcunov
1920c7c14a7SCyrill Gorcunov /*
1930c7c14a7SCyrill Gorcunov * Local IRQs assignment (LINT0, LINT1)
1940c7c14a7SCyrill Gorcunov */
1950c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr;
1960c7c14a7SCyrill Gorcunov mpc_intsrc->type = MP_LINTSRC;
1970c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_ExtINT;
1980c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_INT;
1990c7c14a7SCyrill Gorcunov mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT;
2000c7c14a7SCyrill Gorcunov mpc_intsrc->srcbus = isabusid;
2010c7c14a7SCyrill Gorcunov mpc_intsrc->srcbusirq = 0;
2020c7c14a7SCyrill Gorcunov mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */
2030c7c14a7SCyrill Gorcunov mpc_intsrc->dstirq = 0; /* LINT0 */
2040c7c14a7SCyrill Gorcunov
2050c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_intsrc[1];
2060c7c14a7SCyrill Gorcunov nentries++;
2070c7c14a7SCyrill Gorcunov
2080c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr;
2090c7c14a7SCyrill Gorcunov mpc_intsrc->type = MP_LINTSRC;
2100c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_NMI;
2110c7c14a7SCyrill Gorcunov mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT;
2120c7c14a7SCyrill Gorcunov mpc_intsrc->srcbus = isabusid;
2130c7c14a7SCyrill Gorcunov mpc_intsrc->srcbusirq = 0;
2140c7c14a7SCyrill Gorcunov mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */
2150c7c14a7SCyrill Gorcunov mpc_intsrc->dstirq = 1; /* LINT1 */
2160c7c14a7SCyrill Gorcunov
2170c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_intsrc[1];
2180c7c14a7SCyrill Gorcunov nentries++;
2190c7c14a7SCyrill Gorcunov
2200c7c14a7SCyrill Gorcunov /*
2210c7c14a7SCyrill Gorcunov * Floating MP table finally.
2220c7c14a7SCyrill Gorcunov */
223c13efdd6SCyrill Gorcunov real_mpf_intel = ALIGN((unsigned long)last_addr - (unsigned long)mpc_table, 16);
224c13efdd6SCyrill Gorcunov mpf_intel = (void *)((unsigned long)mpc_table + real_mpf_intel);
2250c7c14a7SCyrill Gorcunov
2260c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpf_intel->signature, MPTABLE_SIG_FLOATING);
2270c7c14a7SCyrill Gorcunov mpf_intel->length = 1;
2280c7c14a7SCyrill Gorcunov mpf_intel->specification= 4;
2290c7c14a7SCyrill Gorcunov mpf_intel->physptr = (unsigned int)real_mpc_table;
2300c7c14a7SCyrill Gorcunov mpf_intel->checksum = -mpf_checksum((unsigned char *)mpf_intel, sizeof(*mpf_intel));
2310c7c14a7SCyrill Gorcunov
2320c7c14a7SCyrill Gorcunov /*
2330c7c14a7SCyrill Gorcunov * No last_addr inclrement here please, we need last
2340c7c14a7SCyrill Gorcunov * active position here to compute table size.
2350c7c14a7SCyrill Gorcunov */
2360c7c14a7SCyrill Gorcunov
2370c7c14a7SCyrill Gorcunov /*
2380c7c14a7SCyrill Gorcunov * Don't forget to update header in fixed table.
2390c7c14a7SCyrill Gorcunov */
2400c7c14a7SCyrill Gorcunov mpc_table->oemcount = nentries;
2410c7c14a7SCyrill Gorcunov mpc_table->length = last_addr - (void *)mpc_table;
2420c7c14a7SCyrill Gorcunov mpc_table->checksum = -mpf_checksum((unsigned char *)mpc_table, mpc_table->length);
2430c7c14a7SCyrill Gorcunov
2440c7c14a7SCyrill Gorcunov
2450c7c14a7SCyrill Gorcunov /*
2460c7c14a7SCyrill Gorcunov * We will copy the whole table, no need to separate
24743835ac9SSasha Levin * floating structure and table itkvm.
2480c7c14a7SCyrill Gorcunov */
2490c7c14a7SCyrill Gorcunov size = (unsigned long)mpf_intel + sizeof(*mpf_intel) - (unsigned long)mpc_table;
2500c7c14a7SCyrill Gorcunov
2510c7c14a7SCyrill Gorcunov /*
2520c7c14a7SCyrill Gorcunov * The finial check -- never get out of system bios
2530c7c14a7SCyrill Gorcunov * area. Lets also check for allocated memory overrun,
2540c7c14a7SCyrill Gorcunov * in real it's late but still usefull.
2550c7c14a7SCyrill Gorcunov */
2560c7c14a7SCyrill Gorcunov
2570c7c14a7SCyrill Gorcunov if (size > (unsigned long)(MB_BIOS_END - bios_rom_size) ||
258f7f9d02bSCyrill Gorcunov size > MPTABLE_MAX_SIZE) {
259f7f9d02bSCyrill Gorcunov free(mpc_table);
260f7f9d02bSCyrill Gorcunov pr_err("MP table is too big");
2611add9f73SSasha Levin
2621add9f73SSasha Levin return -E2BIG;
263f7f9d02bSCyrill Gorcunov }
2640c7c14a7SCyrill Gorcunov
2650c7c14a7SCyrill Gorcunov /*
2660c7c14a7SCyrill Gorcunov * OK, it is time to move it to guest memory.
2670c7c14a7SCyrill Gorcunov */
2680c7c14a7SCyrill Gorcunov memcpy(guest_flat_to_host(kvm, real_mpc_table), mpc_table, size);
2690c7c14a7SCyrill Gorcunov
2700c7c14a7SCyrill Gorcunov free(mpc_table);
2711add9f73SSasha Levin
2721add9f73SSasha Levin return 0;
2731add9f73SSasha Levin }
2743d34111eSSasha Levin firmware_init(mptable__init);
2751add9f73SSasha Levin
mptable__exit(struct kvm * kvm)2761add9f73SSasha Levin int mptable__exit(struct kvm *kvm)
2771add9f73SSasha Levin {
278f7f9d02bSCyrill Gorcunov return 0;
2790c7c14a7SCyrill Gorcunov }
2803d34111eSSasha Levin firmware_exit(mptable__exit);
281