#include "kvm/kvm.h" #include "kvm/bios.h" #include "kvm/apic.h" #include "kvm/mptable.h" #include "kvm/util.h" #include "kvm/devices.h" #include "kvm/pci.h" #include #include #include #include /* * FIXME: please make sure the addresses borrowed * for apic/ioapic never overlaped! We need a global * tracker of system resources (including io, mmio, * and friends). */ static unsigned int mpf_checksum(unsigned char *mp, int len) { unsigned int sum = 0; while (len--) sum += *mp++; return sum & 0xFF; } static unsigned int gen_cpu_flag(unsigned int cpu, unsigned int ncpu) { /* sets enabled/disabled | BSP/AP processor */ return ( (cpu < ncpu) ? CPU_ENABLED : 0) | ((cpu == 0) ? CPU_BOOTPROCESSOR : 0x00); } #define MPTABLE_SIG_FLOATING "_MP_" #define MPTABLE_OEM "KVMCPU00" #define MPTABLE_PRODUCTID "0.1 " #define MPTABLE_PCIBUSTYPE "PCI " #define MPTABLE_ISABUSTYPE "ISA " #define MPTABLE_STRNCPY(d, s) memcpy(d, s, sizeof(d)) /* It should be more than enough */ #define MPTABLE_MAX_SIZE (32 << 20) /* * Too many cpus will require x2apic mode * and rather ACPI support so we limit it * here for a while. */ #define MPTABLE_MAX_CPUS 255 static void mptable_add_irq_src(struct mpc_intsrc *mpc_intsrc, u16 srcbusid, u16 srcbusirq, u16 dstapic, u16 dstirq) { *mpc_intsrc = (struct mpc_intsrc) { .type = MP_INTSRC, .irqtype = mp_INT, .irqflag = MP_IRQDIR_DEFAULT, .srcbus = srcbusid, .srcbusirq = srcbusirq, .dstapic = dstapic, .dstirq = dstirq }; } /** * mptable_setup - create mptable and fill guest memory with it */ int mptable__init(struct kvm *kvm) { unsigned long real_mpc_table, real_mpf_intel, size; struct mpf_intel *mpf_intel; struct mpc_table *mpc_table; struct mpc_cpu *mpc_cpu; struct mpc_bus *mpc_bus; struct mpc_ioapic *mpc_ioapic; struct mpc_intsrc *mpc_intsrc; struct device_header *dev_hdr; const int pcibusid = 0; const int isabusid = 1; unsigned int i, nentries = 0, ncpus = kvm->nrcpus; unsigned int ioapicid; void *last_addr; /* That is where MP table will be in guest memory */ real_mpc_table = ALIGN(MB_BIOS_BEGIN + bios_rom_size, 16); if (ncpus > MPTABLE_MAX_CPUS) { pr_warning("Too many cpus: %d limited to %d", ncpus, MPTABLE_MAX_CPUS); ncpus = MPTABLE_MAX_CPUS; } mpc_table = calloc(1, MPTABLE_MAX_SIZE); if (!mpc_table) return -ENOMEM; MPTABLE_STRNCPY(mpc_table->signature, MPC_SIGNATURE); MPTABLE_STRNCPY(mpc_table->oem, MPTABLE_OEM); MPTABLE_STRNCPY(mpc_table->productid, MPTABLE_PRODUCTID); mpc_table->spec = 4; mpc_table->lapic = APIC_ADDR(0); mpc_table->oemcount = ncpus; /* will be updated again at end */ /* * CPUs enumeration. Technically speaking we should * ask either host or HV for apic version supported * but for a while we simply put some random value * here. */ mpc_cpu = (void *)&mpc_table[1]; for (i = 0; i < ncpus; i++) { mpc_cpu->type = MP_PROCESSOR; mpc_cpu->apicid = i; mpc_cpu->apicver = KVM_APIC_VERSION; mpc_cpu->cpuflag = gen_cpu_flag(i, ncpus); mpc_cpu->cpufeature = 0x600; /* some default value */ mpc_cpu->featureflag = 0x201; /* some default value */ mpc_cpu++; } last_addr = (void *)mpc_cpu; nentries += ncpus; /* * PCI buses. * FIXME: Some callback here to obtain real number * of PCI buses present in system. */ mpc_bus = last_addr; mpc_bus->type = MP_BUS; mpc_bus->busid = pcibusid; MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_PCIBUSTYPE); last_addr = (void *)&mpc_bus[1]; nentries++; /* * ISA bus. * FIXME: Same issue as for PCI bus. */ mpc_bus = last_addr; mpc_bus->type = MP_BUS; mpc_bus->busid = isabusid; MPTABLE_STRNCPY(mpc_bus->bustype, MPTABLE_ISABUSTYPE); last_addr = (void *)&mpc_bus[1]; nentries++; /* * IO-APIC chip. */ ioapicid = ncpus + 1; mpc_ioapic = last_addr; mpc_ioapic->type = MP_IOAPIC; mpc_ioapic->apicid = ioapicid; mpc_ioapic->apicver = KVM_APIC_VERSION; mpc_ioapic->flags = MPC_APIC_USABLE; mpc_ioapic->apicaddr = IOAPIC_ADDR(0); last_addr = (void *)&mpc_ioapic[1]; nentries++; /* * IRQ sources. * Also note we use PCI irqs here, no for ISA bus yet. */ dev_hdr = device__first_dev(DEVICE_BUS_PCI); while (dev_hdr) { unsigned char srcbusirq; struct pci_device_header *pci_hdr = dev_hdr->data; srcbusirq = (pci_hdr->subsys_id << 2) | (pci_hdr->irq_pin - 1); mpc_intsrc = last_addr; mptable_add_irq_src(mpc_intsrc, pcibusid, srcbusirq, ioapicid, pci_hdr->irq_line); last_addr = (void *)&mpc_intsrc[1]; nentries++; dev_hdr = device__next_dev(dev_hdr); } /* * Local IRQs assignment (LINT0, LINT1) */ mpc_intsrc = last_addr; mpc_intsrc->type = MP_LINTSRC; mpc_intsrc->irqtype = mp_ExtINT; mpc_intsrc->irqtype = mp_INT; mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT; mpc_intsrc->srcbus = isabusid; mpc_intsrc->srcbusirq = 0; mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */ mpc_intsrc->dstirq = 0; /* LINT0 */ last_addr = (void *)&mpc_intsrc[1]; nentries++; mpc_intsrc = last_addr; mpc_intsrc->type = MP_LINTSRC; mpc_intsrc->irqtype = mp_NMI; mpc_intsrc->irqflag = MP_IRQDIR_DEFAULT; mpc_intsrc->srcbus = isabusid; mpc_intsrc->srcbusirq = 0; mpc_intsrc->dstapic = 0; /* FIXME: BSP apic */ mpc_intsrc->dstirq = 1; /* LINT1 */ last_addr = (void *)&mpc_intsrc[1]; nentries++; /* * Floating MP table finally. */ real_mpf_intel = ALIGN((unsigned long)last_addr - (unsigned long)mpc_table, 16); mpf_intel = (void *)((unsigned long)mpc_table + real_mpf_intel); MPTABLE_STRNCPY(mpf_intel->signature, MPTABLE_SIG_FLOATING); mpf_intel->length = 1; mpf_intel->specification= 4; mpf_intel->physptr = (unsigned int)real_mpc_table; mpf_intel->checksum = -mpf_checksum((unsigned char *)mpf_intel, sizeof(*mpf_intel)); /* * No last_addr inclrement here please, we need last * active position here to compute table size. */ /* * Don't forget to update header in fixed table. */ mpc_table->oemcount = nentries; mpc_table->length = last_addr - (void *)mpc_table; mpc_table->checksum = -mpf_checksum((unsigned char *)mpc_table, mpc_table->length); /* * We will copy the whole table, no need to separate * floating structure and table itkvm. */ size = (unsigned long)mpf_intel + sizeof(*mpf_intel) - (unsigned long)mpc_table; /* * The finial check -- never get out of system bios * area. Lets also check for allocated memory overrun, * in real it's late but still usefull. */ if (size > (unsigned long)(MB_BIOS_END - bios_rom_size) || size > MPTABLE_MAX_SIZE) { free(mpc_table); pr_err("MP table is too big"); return -E2BIG; } /* * OK, it is time to move it to guest memory. */ memcpy(guest_flat_to_host(kvm, real_mpc_table), mpc_table, size); free(mpc_table); return 0; } firmware_init(mptable__init); int mptable__exit(struct kvm *kvm) { return 0; } firmware_exit(mptable__exit);