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