Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 1 | // MPTable generation (on emulators) |
| 2 | // |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 3 | // Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 4 | // Copyright (C) 2006 Fabrice Bellard |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 7 | |
| 8 | #include "util.h" // dprintf |
Kevin O'Connor | 9521e26 | 2008-07-04 13:04:29 -0400 | [diff] [blame] | 9 | #include "config.h" // CONFIG_* |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 10 | #include "mptable.h" // MPTABLE_SIGNATURE |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 11 | #include "paravirt.h" // qemu_cfg_irq0_override |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 12 | #include "pci.h" |
| 13 | #include "pci_regs.h" |
Kevin O'Connor | c0ad0e8 | 2009-11-09 19:20:21 -0500 | [diff] [blame] | 14 | |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 15 | void |
| 16 | mptable_init(void) |
| 17 | { |
Kevin O'Connor | d381702 | 2008-07-12 12:46:07 -0400 | [diff] [blame] | 18 | if (! CONFIG_MPTABLE) |
| 19 | return; |
| 20 | |
Kevin O'Connor | 7b49cd9 | 2008-11-08 10:35:26 -0500 | [diff] [blame] | 21 | dprintf(3, "init MPTable\n"); |
| 22 | |
Kevin O'Connor | 8362699 | 2009-12-26 11:15:31 -0500 | [diff] [blame] | 23 | // Config structure in temp area. |
| 24 | struct mptable_config_s *config = malloc_tmphigh(32*1024); |
| 25 | if (!config) { |
| 26 | dprintf(1, "No space for temp mptable\n"); |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 27 | return; |
| 28 | } |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 29 | memset(config, 0, sizeof(*config)); |
| 30 | config->signature = MPCONFIG_SIGNATURE; |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 31 | config->spec = 4; |
| 32 | memcpy(config->oemid, CONFIG_CPUNAME8, sizeof(config->oemid)); |
| 33 | memcpy(config->productid, "0.1 ", sizeof(config->productid)); |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 34 | config->lapic = BUILD_APIC_ADDR; |
| 35 | |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 36 | // Detect cpu info |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 37 | u32 cpuid_signature, ebx, ecx, cpuid_features; |
| 38 | cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 39 | int pkgcpus = 1; |
| 40 | if (cpuid_features & (1 << 28)) { |
| 41 | /* Only populate the MPS tables with the first logical CPU in |
| 42 | each package */ |
| 43 | pkgcpus = (ebx >> 16) & 0xff; |
| 44 | pkgcpus = 1 << (__fls(pkgcpus - 1) + 1); /* round up to power of 2 */ |
| 45 | } |
Kevin O'Connor | c0ad0e8 | 2009-11-09 19:20:21 -0500 | [diff] [blame] | 46 | |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 47 | // CPU definitions. |
| 48 | struct mpt_cpu *cpus = (void*)&config[1], *cpu = cpus; |
| 49 | int i; |
| 50 | for (i = 0; i < MaxCountCPUs; i+=pkgcpus) { |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 51 | memset(cpu, 0, sizeof(*cpu)); |
| 52 | cpu->type = MPT_TYPE_CPU; |
| 53 | cpu->apicid = i; |
| 54 | cpu->apicver = 0x11; |
| 55 | /* cpu flags: enabled, bootstrap cpu */ |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 56 | cpu->cpuflag = (i < CountCPUs) | ((i == 0) << 1); |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 57 | if (cpuid_signature) { |
| 58 | cpu->cpusignature = cpuid_signature; |
| 59 | cpu->featureflag = cpuid_features; |
| 60 | } else { |
| 61 | cpu->cpusignature = 0x600; |
| 62 | cpu->featureflag = 0x201; |
| 63 | } |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 64 | cpu++; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 65 | } |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 66 | int entrycount = cpu - cpus; |
Kevin O'Connor | c0ad0e8 | 2009-11-09 19:20:21 -0500 | [diff] [blame] | 67 | |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 68 | /* isa bus */ |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 69 | struct mpt_bus *bus = (void*)cpu; |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 70 | memset(bus, 0, sizeof(*bus)); |
| 71 | bus->type = MPT_TYPE_BUS; |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 72 | bus->busid = 1; |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 73 | memcpy(bus->bustype, "ISA ", sizeof(bus->bustype)); |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 74 | entrycount++; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 75 | |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 76 | bus++; |
| 77 | memset(bus, 0, sizeof(*bus)); |
| 78 | bus->type = MPT_TYPE_BUS; |
| 79 | bus->busid = 0; |
| 80 | memcpy(bus->bustype, "PCI ", sizeof(bus->bustype)); |
| 81 | entrycount++; |
| 82 | |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 83 | /* ioapic */ |
Kevin O'Connor | a26df9b | 2009-10-09 09:42:11 -0400 | [diff] [blame] | 84 | u8 ioapic_id = CountCPUs; |
Kevin O'Connor | 3574644 | 2009-02-27 20:54:51 -0500 | [diff] [blame] | 85 | struct mpt_ioapic *ioapic = (void*)&bus[1]; |
| 86 | memset(ioapic, 0, sizeof(*ioapic)); |
| 87 | ioapic->type = MPT_TYPE_IOAPIC; |
| 88 | ioapic->apicid = ioapic_id; |
| 89 | ioapic->apicver = 0x11; |
| 90 | ioapic->flags = 1; // enable |
| 91 | ioapic->apicaddr = BUILD_IOAPIC_ADDR; |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 92 | entrycount++; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 93 | |
| 94 | /* irqs */ |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 95 | struct mpt_intsrc *intsrcs = (void*)&ioapic[1], *intsrc = intsrcs; |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 96 | int bdf, max, dev = -1; |
| 97 | unsigned short mask = 0, pinmask; |
| 98 | |
| 99 | foreachpci(bdf, max) { |
| 100 | int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); |
| 101 | int irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); |
| 102 | if (pin == 0) |
| 103 | continue; |
| 104 | if (dev != pci_bdf_to_dev(bdf)) { |
| 105 | dev = pci_bdf_to_dev(bdf); |
| 106 | pinmask = 0; |
| 107 | } |
| 108 | if (pinmask & (1 << pin)) /* pin was seen already */ |
| 109 | continue; |
| 110 | pinmask |= (1 << pin); |
| 111 | mask |= (1 << irq); |
Kevin O'Connor | b64db30 | 2009-07-29 19:20:03 -0400 | [diff] [blame] | 112 | memset(intsrc, 0, sizeof(*intsrc)); |
| 113 | intsrc->type = MPT_TYPE_INTSRC; |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 114 | intsrc->irqtype = 0; /* INT */ |
| 115 | intsrc->irqflag = 1; /* active high */ |
| 116 | intsrc->srcbus = 0; /* PCI bus */ |
| 117 | intsrc->srcbusirq = (dev << 2) | (pin - 1); |
| 118 | intsrc->dstapic = ioapic_id; |
| 119 | intsrc->dstirq = irq; |
| 120 | intsrc++; |
| 121 | } |
| 122 | |
| 123 | for (i = 0; i < 16; i++) { |
| 124 | memset(intsrc, 0, sizeof(*intsrc)); |
| 125 | if (mask & (1 << i)) |
| 126 | continue; |
| 127 | intsrc->type = MPT_TYPE_INTSRC; |
| 128 | intsrc->irqtype = 0; /* INT */ |
| 129 | intsrc->irqflag = 0; /* conform to bus spec */ |
| 130 | intsrc->srcbus = 1; /* ISA bus */ |
Kevin O'Connor | b64db30 | 2009-07-29 19:20:03 -0400 | [diff] [blame] | 131 | intsrc->srcbusirq = i; |
| 132 | intsrc->dstapic = ioapic_id; |
| 133 | intsrc->dstirq = i; |
Kevin O'Connor | 4d2b619 | 2009-10-08 21:37:21 -0400 | [diff] [blame] | 134 | if (qemu_cfg_irq0_override()) { |
Kevin O'Connor | b64db30 | 2009-07-29 19:20:03 -0400 | [diff] [blame] | 135 | /* Destination 2 is covered by irq0->inti2 override (i == |
| 136 | 0). Source IRQ 2 is unused */ |
| 137 | if (i == 0) |
| 138 | intsrc->dstirq = 2; |
| 139 | else if (i == 2) |
| 140 | intsrc--; |
| 141 | } |
| 142 | intsrc++; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 143 | } |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 144 | entrycount += intsrc - intsrcs; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 145 | |
Kevin O'Connor | b021a57 | 2009-11-14 13:49:06 -0500 | [diff] [blame] | 146 | /* Local interrupt assignment */ |
| 147 | intsrc->type = MPT_TYPE_LOCAL_INT; |
| 148 | intsrc->irqtype = 3; /* ExtINT */ |
| 149 | intsrc->irqflag = 0; /* PO, EL default */ |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 150 | intsrc->srcbus = 1; /* ISA */ |
Kevin O'Connor | b021a57 | 2009-11-14 13:49:06 -0500 | [diff] [blame] | 151 | intsrc->srcbusirq = 0; |
| 152 | intsrc->dstapic = 0; /* BSP == APIC #0 */ |
| 153 | intsrc->dstirq = 0; /* LINTIN0 */ |
| 154 | intsrc++; |
| 155 | entrycount++; |
| 156 | |
| 157 | intsrc->type = MPT_TYPE_LOCAL_INT; |
| 158 | intsrc->irqtype = 1; /* NMI */ |
| 159 | intsrc->irqflag = 0; /* PO, EL default */ |
Gleb Natapov | 928d4df | 2009-12-24 14:29:43 +0200 | [diff] [blame] | 160 | intsrc->srcbus = 1; /* ISA */ |
Kevin O'Connor | b021a57 | 2009-11-14 13:49:06 -0500 | [diff] [blame] | 161 | intsrc->srcbusirq = 0; |
| 162 | intsrc->dstapic = 0; /* BSP == APIC #0 */ |
| 163 | intsrc->dstirq = 1; /* LINTIN1 */ |
| 164 | intsrc++; |
| 165 | entrycount++; |
| 166 | |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 167 | // Finalize config structure. |
Kevin O'Connor | 8362699 | 2009-12-26 11:15:31 -0500 | [diff] [blame] | 168 | int length = (void*)intsrc - (void*)config; |
Kevin O'Connor | 86916ce | 2009-11-14 13:34:27 -0500 | [diff] [blame] | 169 | config->entrycount = entrycount; |
Kevin O'Connor | 8362699 | 2009-12-26 11:15:31 -0500 | [diff] [blame] | 170 | config->length = length; |
| 171 | config->checksum -= checksum(config, length); |
| 172 | |
| 173 | // Allocate final memory locations |
| 174 | struct mptable_config_s *finalconfig = malloc_fseg(length); |
| 175 | struct mptable_floating_s *floating = malloc_fseg(sizeof(*floating)); |
| 176 | if (!finalconfig || !floating) { |
| 177 | dprintf(1, "No room for MPTABLE!\n"); |
| 178 | free(config); |
| 179 | free(finalconfig); |
| 180 | free(floating); |
| 181 | return; |
| 182 | } |
| 183 | memcpy(finalconfig, config, length); |
| 184 | free(config); |
| 185 | |
| 186 | /* floating pointer structure */ |
| 187 | memset(floating, 0, sizeof(*floating)); |
| 188 | floating->signature = MPTABLE_SIGNATURE; |
| 189 | floating->physaddr = (u32)finalconfig; |
| 190 | floating->length = 1; |
| 191 | floating->spec_rev = 4; |
| 192 | floating->checksum -= checksum(floating, sizeof(*floating)); |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 193 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 194 | dprintf(1, "MP table addr=%p MPC table addr=%p size=%d\n", |
Kevin O'Connor | 8362699 | 2009-12-26 11:15:31 -0500 | [diff] [blame] | 195 | floating, finalconfig, length); |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 196 | } |