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" 68ec7e042SSasha Levin #include "kvm/irq.h" 70c7c14a7SCyrill Gorcunov 8ab9e4f1cSSasha Levin #include <linux/kernel.h> 90c7c14a7SCyrill Gorcunov #include <string.h> 100c7c14a7SCyrill Gorcunov 110c7c14a7SCyrill Gorcunov #include <asm/mpspec_def.h> 124749e795SSasha Levin #include <linux/types.h> 130c7c14a7SCyrill Gorcunov 140c7c14a7SCyrill Gorcunov /* 150c7c14a7SCyrill Gorcunov * FIXME: please make sure the addresses borrowed 160c7c14a7SCyrill Gorcunov * for apic/ioapic never overlaped! We need a global 170c7c14a7SCyrill Gorcunov * tracker of system resources (including io, mmio, 180c7c14a7SCyrill Gorcunov * and friends). 190c7c14a7SCyrill Gorcunov */ 200c7c14a7SCyrill Gorcunov 210c7c14a7SCyrill Gorcunov static unsigned int mpf_checksum(unsigned char *mp, int len) 220c7c14a7SCyrill Gorcunov { 230c7c14a7SCyrill Gorcunov unsigned int sum = 0; 240c7c14a7SCyrill Gorcunov 250c7c14a7SCyrill Gorcunov while (len--) 260c7c14a7SCyrill Gorcunov sum += *mp++; 270c7c14a7SCyrill Gorcunov 280c7c14a7SCyrill Gorcunov return sum & 0xFF; 290c7c14a7SCyrill Gorcunov } 300c7c14a7SCyrill Gorcunov 310c7c14a7SCyrill Gorcunov static unsigned int gen_cpu_flag(unsigned int cpu, unsigned int ncpu) 320c7c14a7SCyrill Gorcunov { 330c7c14a7SCyrill Gorcunov /* sets enabled/disabled | BSP/AP processor */ 340c7c14a7SCyrill Gorcunov return ( (cpu < ncpu) ? CPU_ENABLED : 0) | 350c7c14a7SCyrill Gorcunov ((cpu == 0) ? CPU_BOOTPROCESSOR : 0x00); 360c7c14a7SCyrill Gorcunov } 370c7c14a7SCyrill Gorcunov 380c7c14a7SCyrill Gorcunov #define MPTABLE_SIG_FLOATING "_MP_" 390c7c14a7SCyrill Gorcunov #define MPTABLE_OEM "KVMCPU00" 400c7c14a7SCyrill Gorcunov #define MPTABLE_PRODUCTID "0.1 " 410c7c14a7SCyrill Gorcunov #define MPTABLE_PCIBUSTYPE "PCI " 420c7c14a7SCyrill Gorcunov #define MPTABLE_ISABUSTYPE "ISA " 430c7c14a7SCyrill Gorcunov 440c7c14a7SCyrill Gorcunov #define MPTABLE_STRNCPY(d, s) memcpy(d, s, sizeof(d)) 450c7c14a7SCyrill Gorcunov 460c7c14a7SCyrill Gorcunov /* It should be more than enough */ 470c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_SIZE (32 << 20) 480c7c14a7SCyrill Gorcunov 490c7c14a7SCyrill Gorcunov /* 500c7c14a7SCyrill Gorcunov * Too many cpus will require x2apic mode 510c7c14a7SCyrill Gorcunov * and rather ACPI support so we limit it 520c7c14a7SCyrill Gorcunov * here for a while. 530c7c14a7SCyrill Gorcunov */ 540c7c14a7SCyrill Gorcunov #define MPTABLE_MAX_CPUS 255 550c7c14a7SCyrill Gorcunov 564749e795SSasha Levin static void mptable_add_irq_src(struct mpc_intsrc *mpc_intsrc, 574749e795SSasha Levin u16 srcbusid, u16 srcbusirq, 584749e795SSasha Levin u16 dstapic, u16 dstirq) 594749e795SSasha Levin { 604749e795SSasha Levin *mpc_intsrc = (struct mpc_intsrc) { 614749e795SSasha Levin .type = MP_INTSRC, 624749e795SSasha Levin .irqtype = mp_INT, 634749e795SSasha Levin .irqflag = MP_IRQDIR_DEFAULT, 644749e795SSasha Levin .srcbus = srcbusid, 654749e795SSasha Levin .srcbusirq = srcbusirq, 664749e795SSasha Levin .dstapic = dstapic, 674749e795SSasha Levin .dstirq = dstirq 684749e795SSasha Levin }; 694749e795SSasha Levin } 704749e795SSasha Levin 710c7c14a7SCyrill Gorcunov /** 720c7c14a7SCyrill Gorcunov * mptable_setup - create mptable and fill guest memory with it 730c7c14a7SCyrill Gorcunov */ 741add9f73SSasha Levin int mptable__init(struct kvm *kvm) 750c7c14a7SCyrill Gorcunov { 76c13efdd6SCyrill Gorcunov unsigned long real_mpc_table, real_mpf_intel, size; 770c7c14a7SCyrill Gorcunov struct mpf_intel *mpf_intel; 780c7c14a7SCyrill Gorcunov struct mpc_table *mpc_table; 790c7c14a7SCyrill Gorcunov struct mpc_cpu *mpc_cpu; 800c7c14a7SCyrill Gorcunov struct mpc_bus *mpc_bus; 810c7c14a7SCyrill Gorcunov struct mpc_ioapic *mpc_ioapic; 820c7c14a7SCyrill Gorcunov struct mpc_intsrc *mpc_intsrc; 838ec7e042SSasha Levin struct rb_node *pci_tree; 840c7c14a7SCyrill Gorcunov 850c7c14a7SCyrill Gorcunov const int pcibusid = 0; 860c7c14a7SCyrill Gorcunov const int isabusid = 1; 870c7c14a7SCyrill Gorcunov 881add9f73SSasha Levin unsigned int i, nentries = 0, ncpus = kvm->nrcpus; 890c7c14a7SCyrill Gorcunov unsigned int ioapicid; 900c7c14a7SCyrill Gorcunov void *last_addr; 910c7c14a7SCyrill Gorcunov 920c7c14a7SCyrill Gorcunov /* That is where MP table will be in guest memory */ 930c7c14a7SCyrill Gorcunov real_mpc_table = ALIGN(MB_BIOS_BEGIN + bios_rom_size, 16); 940c7c14a7SCyrill Gorcunov 950c7c14a7SCyrill Gorcunov if (ncpus > MPTABLE_MAX_CPUS) { 964542f276SCyrill Gorcunov pr_warning("Too many cpus: %d limited to %d", 970c7c14a7SCyrill Gorcunov ncpus, MPTABLE_MAX_CPUS); 980c7c14a7SCyrill Gorcunov ncpus = MPTABLE_MAX_CPUS; 990c7c14a7SCyrill Gorcunov } 1000c7c14a7SCyrill Gorcunov 1010c7c14a7SCyrill Gorcunov mpc_table = calloc(1, MPTABLE_MAX_SIZE); 1020c7c14a7SCyrill Gorcunov if (!mpc_table) 1031add9f73SSasha Levin return -ENOMEM; 1040c7c14a7SCyrill Gorcunov 1050c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->signature, MPC_SIGNATURE); 1060c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->oem, MPTABLE_OEM); 1070c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_table->productid, MPTABLE_PRODUCTID); 1080c7c14a7SCyrill Gorcunov 1090c7c14a7SCyrill Gorcunov mpc_table->spec = 4; 1100c7c14a7SCyrill Gorcunov mpc_table->lapic = APIC_ADDR(0); 1110c7c14a7SCyrill Gorcunov mpc_table->oemcount = ncpus; /* will be updated again at end */ 1120c7c14a7SCyrill Gorcunov 1130c7c14a7SCyrill Gorcunov /* 1140c7c14a7SCyrill Gorcunov * CPUs enumeration. Technically speaking we should 1150c7c14a7SCyrill Gorcunov * ask either host or HV for apic version supported 1160c7c14a7SCyrill Gorcunov * but for a while we simply put some random value 1170c7c14a7SCyrill Gorcunov * here. 1180c7c14a7SCyrill Gorcunov */ 1190c7c14a7SCyrill Gorcunov mpc_cpu = (void *)&mpc_table[1]; 1200c7c14a7SCyrill Gorcunov for (i = 0; i < ncpus; i++) { 1210c7c14a7SCyrill Gorcunov mpc_cpu->type = MP_PROCESSOR; 1220c7c14a7SCyrill Gorcunov mpc_cpu->apicid = i; 1230c7c14a7SCyrill Gorcunov mpc_cpu->apicver = KVM_APIC_VERSION; 1240c7c14a7SCyrill Gorcunov mpc_cpu->cpuflag = gen_cpu_flag(i, ncpus); 1250c7c14a7SCyrill Gorcunov mpc_cpu->cpufeature = 0x600; /* some default value */ 1260c7c14a7SCyrill Gorcunov mpc_cpu->featureflag = 0x201; /* some default value */ 1270c7c14a7SCyrill Gorcunov mpc_cpu++; 1280c7c14a7SCyrill Gorcunov } 1290c7c14a7SCyrill Gorcunov 1300c7c14a7SCyrill Gorcunov last_addr = (void *)mpc_cpu; 1310c7c14a7SCyrill Gorcunov nentries += ncpus; 1320c7c14a7SCyrill Gorcunov 1330c7c14a7SCyrill Gorcunov /* 1340c7c14a7SCyrill Gorcunov * PCI buses. 1350c7c14a7SCyrill Gorcunov * FIXME: Some callback here to obtain real number 1360c7c14a7SCyrill Gorcunov * of PCI buses present in system. 1370c7c14a7SCyrill Gorcunov */ 1380c7c14a7SCyrill Gorcunov mpc_bus = last_addr; 1390c7c14a7SCyrill Gorcunov mpc_bus->type = MP_BUS; 1400c7c14a7SCyrill Gorcunov mpc_bus->busid = pcibusid; 1410c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_PCIBUSTYPE); 1420c7c14a7SCyrill Gorcunov 1430c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_bus[1]; 1440c7c14a7SCyrill Gorcunov nentries++; 1450c7c14a7SCyrill Gorcunov 1460c7c14a7SCyrill Gorcunov /* 1470c7c14a7SCyrill Gorcunov * ISA bus. 1480c7c14a7SCyrill Gorcunov * FIXME: Same issue as for PCI bus. 1490c7c14a7SCyrill Gorcunov */ 1500c7c14a7SCyrill Gorcunov mpc_bus = last_addr; 1510c7c14a7SCyrill Gorcunov mpc_bus->type = MP_BUS; 1520c7c14a7SCyrill Gorcunov mpc_bus->busid = isabusid; 1530c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_ISABUSTYPE); 1540c7c14a7SCyrill Gorcunov 1550c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_bus[1]; 1560c7c14a7SCyrill Gorcunov nentries++; 1570c7c14a7SCyrill Gorcunov 1580c7c14a7SCyrill Gorcunov /* 1590c7c14a7SCyrill Gorcunov * IO-APIC chip. 1600c7c14a7SCyrill Gorcunov */ 1610c7c14a7SCyrill Gorcunov ioapicid = ncpus + 1; 1620c7c14a7SCyrill Gorcunov mpc_ioapic = last_addr; 1630c7c14a7SCyrill Gorcunov mpc_ioapic->type = MP_IOAPIC; 1640c7c14a7SCyrill Gorcunov mpc_ioapic->apicid = ioapicid; 1650c7c14a7SCyrill Gorcunov mpc_ioapic->apicver = KVM_APIC_VERSION; 1660c7c14a7SCyrill Gorcunov mpc_ioapic->flags = MPC_APIC_USABLE; 1670c7c14a7SCyrill Gorcunov mpc_ioapic->apicaddr = IOAPIC_ADDR(0); 1680c7c14a7SCyrill Gorcunov 1690c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_ioapic[1]; 1700c7c14a7SCyrill Gorcunov nentries++; 1710c7c14a7SCyrill Gorcunov 1720c7c14a7SCyrill Gorcunov /* 1730c7c14a7SCyrill Gorcunov * IRQ sources. 1740c7c14a7SCyrill Gorcunov * 1750c7c14a7SCyrill Gorcunov * FIXME: Same issue as with buses. We definitely 1760c7c14a7SCyrill Gorcunov * need kind of collector routine which enumerate 1770c7c14a7SCyrill Gorcunov * resources used first and pass them here. 1780c7c14a7SCyrill Gorcunov * At moment we know we have only virtio block device 1790c7c14a7SCyrill Gorcunov * and virtio console but this is g00berfish. 1800c7c14a7SCyrill Gorcunov * 1810c7c14a7SCyrill Gorcunov * Also note we use PCI irqs here, no for ISA bus yet. 1820c7c14a7SCyrill Gorcunov */ 1838ec7e042SSasha Levin 1848ec7e042SSasha Levin for (pci_tree = irq__get_pci_tree(); pci_tree; pci_tree = rb_next(pci_tree)) { 1858ec7e042SSasha Levin struct pci_dev *dev = rb_entry(pci_tree, struct pci_dev, node); 18668791d77SCyrill Gorcunov struct irq_line *irq_line; 1878ec7e042SSasha Levin 18868791d77SCyrill Gorcunov list_for_each_entry(irq_line, &dev->lines, node) { 18968791d77SCyrill Gorcunov unsigned char srcbusirq; 19068791d77SCyrill Gorcunov 19168791d77SCyrill Gorcunov srcbusirq = (dev->id << 2) | (dev->pin - 1); 19268791d77SCyrill Gorcunov 1930c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr; 1940c7c14a7SCyrill Gorcunov 19568791d77SCyrill Gorcunov mptable_add_irq_src(mpc_intsrc, pcibusid, srcbusirq, ioapicid, irq_line->line); 1960c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_intsrc[1]; 1970c7c14a7SCyrill Gorcunov nentries++; 1984749e795SSasha Levin } 1998ec7e042SSasha Levin } 2000c7c14a7SCyrill Gorcunov 2010c7c14a7SCyrill Gorcunov /* 2020c7c14a7SCyrill Gorcunov * Local IRQs assignment (LINT0, LINT1) 2030c7c14a7SCyrill Gorcunov */ 2040c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr; 2050c7c14a7SCyrill Gorcunov mpc_intsrc->type = MP_LINTSRC; 2060c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_ExtINT; 2070c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_INT; 2080c7c14a7SCyrill Gorcunov mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT; 2090c7c14a7SCyrill Gorcunov mpc_intsrc->srcbus = isabusid; 2100c7c14a7SCyrill Gorcunov mpc_intsrc->srcbusirq = 0; 2110c7c14a7SCyrill Gorcunov mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */ 2120c7c14a7SCyrill Gorcunov mpc_intsrc->dstirq = 0; /* LINT0 */ 2130c7c14a7SCyrill Gorcunov 2140c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_intsrc[1]; 2150c7c14a7SCyrill Gorcunov nentries++; 2160c7c14a7SCyrill Gorcunov 2170c7c14a7SCyrill Gorcunov mpc_intsrc = last_addr; 2180c7c14a7SCyrill Gorcunov mpc_intsrc->type = MP_LINTSRC; 2190c7c14a7SCyrill Gorcunov mpc_intsrc->irqtype = mp_NMI; 2200c7c14a7SCyrill Gorcunov mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT; 2210c7c14a7SCyrill Gorcunov mpc_intsrc->srcbus = isabusid; 2220c7c14a7SCyrill Gorcunov mpc_intsrc->srcbusirq = 0; 2230c7c14a7SCyrill Gorcunov mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */ 2240c7c14a7SCyrill Gorcunov mpc_intsrc->dstirq = 1; /* LINT1 */ 2250c7c14a7SCyrill Gorcunov 2260c7c14a7SCyrill Gorcunov last_addr = (void *)&mpc_intsrc[1]; 2270c7c14a7SCyrill Gorcunov nentries++; 2280c7c14a7SCyrill Gorcunov 2290c7c14a7SCyrill Gorcunov /* 2300c7c14a7SCyrill Gorcunov * Floating MP table finally. 2310c7c14a7SCyrill Gorcunov */ 232c13efdd6SCyrill Gorcunov real_mpf_intel = ALIGN((unsigned long)last_addr - (unsigned long)mpc_table, 16); 233c13efdd6SCyrill Gorcunov mpf_intel = (void *)((unsigned long)mpc_table + real_mpf_intel); 2340c7c14a7SCyrill Gorcunov 2350c7c14a7SCyrill Gorcunov MPTABLE_STRNCPY(mpf_intel->signature, MPTABLE_SIG_FLOATING); 2360c7c14a7SCyrill Gorcunov mpf_intel->length = 1; 2370c7c14a7SCyrill Gorcunov mpf_intel->specification= 4; 2380c7c14a7SCyrill Gorcunov mpf_intel->physptr = (unsigned int)real_mpc_table; 2390c7c14a7SCyrill Gorcunov mpf_intel->checksum = -mpf_checksum((unsigned char *)mpf_intel, sizeof(*mpf_intel)); 2400c7c14a7SCyrill Gorcunov 2410c7c14a7SCyrill Gorcunov /* 2420c7c14a7SCyrill Gorcunov * No last_addr inclrement here please, we need last 2430c7c14a7SCyrill Gorcunov * active position here to compute table size. 2440c7c14a7SCyrill Gorcunov */ 2450c7c14a7SCyrill Gorcunov 2460c7c14a7SCyrill Gorcunov /* 2470c7c14a7SCyrill Gorcunov * Don't forget to update header in fixed table. 2480c7c14a7SCyrill Gorcunov */ 2490c7c14a7SCyrill Gorcunov mpc_table->oemcount = nentries; 2500c7c14a7SCyrill Gorcunov mpc_table->length = last_addr - (void *)mpc_table; 2510c7c14a7SCyrill Gorcunov mpc_table->checksum = -mpf_checksum((unsigned char *)mpc_table, mpc_table->length); 2520c7c14a7SCyrill Gorcunov 2530c7c14a7SCyrill Gorcunov 2540c7c14a7SCyrill Gorcunov /* 2550c7c14a7SCyrill Gorcunov * We will copy the whole table, no need to separate 25643835ac9SSasha Levin * floating structure and table itkvm. 2570c7c14a7SCyrill Gorcunov */ 2580c7c14a7SCyrill Gorcunov size = (unsigned long)mpf_intel + sizeof(*mpf_intel) - (unsigned long)mpc_table; 2590c7c14a7SCyrill Gorcunov 2600c7c14a7SCyrill Gorcunov /* 2610c7c14a7SCyrill Gorcunov * The finial check -- never get out of system bios 2620c7c14a7SCyrill Gorcunov * area. Lets also check for allocated memory overrun, 2630c7c14a7SCyrill Gorcunov * in real it's late but still usefull. 2640c7c14a7SCyrill Gorcunov */ 2650c7c14a7SCyrill Gorcunov 2660c7c14a7SCyrill Gorcunov if (size > (unsigned long)(MB_BIOS_END - bios_rom_size) || 267f7f9d02bSCyrill Gorcunov size > MPTABLE_MAX_SIZE) { 268f7f9d02bSCyrill Gorcunov free(mpc_table); 269f7f9d02bSCyrill Gorcunov pr_err("MP table is too big"); 2701add9f73SSasha Levin 2711add9f73SSasha Levin return -E2BIG; 272f7f9d02bSCyrill Gorcunov } 2730c7c14a7SCyrill Gorcunov 2740c7c14a7SCyrill Gorcunov /* 2750c7c14a7SCyrill Gorcunov * OK, it is time to move it to guest memory. 2760c7c14a7SCyrill Gorcunov */ 2770c7c14a7SCyrill Gorcunov memcpy(guest_flat_to_host(kvm, real_mpc_table), mpc_table, size); 2780c7c14a7SCyrill Gorcunov 2790c7c14a7SCyrill Gorcunov free(mpc_table); 2801add9f73SSasha Levin 2811add9f73SSasha Levin return 0; 2821add9f73SSasha Levin } 283*3d34111eSSasha Levin firmware_init(mptable__init); 2841add9f73SSasha Levin 2851add9f73SSasha Levin int mptable__exit(struct kvm *kvm) 2861add9f73SSasha Levin { 287f7f9d02bSCyrill Gorcunov return 0; 2880c7c14a7SCyrill Gorcunov } 289*3d34111eSSasha Levin firmware_exit(mptable__exit); 290