1 /* 2 * minimal acpi support for multiprocessors. 3 * 4 * avoids AML but that's only enough to discover 5 * the processors, not the interrupt routing details. 6 */ 7 #include "u.h" 8 #include "../port/lib.h" 9 #include "mem.h" 10 #include "dat.h" 11 #include "fns.h" 12 #include "io.h" 13 #include "mp.h" 14 #include "mpacpi.h" 15 16 /* 8c says: out of fixed registers */ 17 #define L64GET(p) ((uvlong)L32GET((p)+4) << 32 | L32GET(p)) 18 19 enum { 20 /* apic types */ 21 Apiclproc, 22 Apicio, 23 Apicintrsrcoverride, 24 Apicnmisrc, 25 Apiclnmi, 26 Apicladdroverride, 27 Apicios, 28 Apicls, 29 Apicintrsrc, 30 Apiclx2, 31 Apiclx2nmi, 32 33 PcmpUsed = 1ul<<31, /* Apic->flags addition */ 34 35 Lapicbase = 0x1b, /* msr */ 36 37 Lapicae = 1<<11, /* apic enable in Lapicbase */ 38 }; 39 40 #define dprint(...) if(mpdebug) print(__VA_ARGS__); else USED(mpdebug) 41 42 /* from mp.c */ 43 int mpdebug; 44 int mpmachno; 45 Apic mpapic[MaxAPICNO+1]; 46 int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */ 47 48 Apic *bootapic; 49 50 static int nprocid; 51 52 static uvlong 53 l64get(uchar *p) 54 { 55 return L64GET(p); 56 } 57 58 int 59 apicset(Apic *apic, int type, int apicno, int f) 60 { 61 if(apicno > MaxAPICNO) 62 return -1; 63 apic->type = type; 64 apic->apicno = apicno; 65 apic->flags = f | PcmpEN | PcmpUsed; 66 return 0; 67 } 68 69 int 70 mpnewproc(Apic *apic, int apicno, int f) 71 { 72 if(apic->flags & PcmpUsed) { 73 print("mpnewproc: apic already enabled\n"); 74 return -1; 75 } 76 if (apicset(apic, PcmpPROCESSOR, apicno, f) < 0) 77 return -1; 78 apic->lintr[1] = apic->lintr[0] = ApicIMASK; 79 /* botch! just enumerate */ 80 if(apic->flags & PcmpBP) 81 apic->machno = 0; 82 else 83 apic->machno = ++mpmachno; 84 machno2apicno[apic->machno] = apicno; 85 return 0; 86 } 87 88 static int 89 mpacpiproc(uchar *p, ulong laddr) 90 { 91 int id, f; 92 ulong *vladdr; 93 vlong base; 94 char *already; 95 Apic *apic; 96 97 /* p bytes: type (0), len (8), cpuid, cpu_lapic id, flags[4] */ 98 id = p[3]; 99 /* cpu unusable flag or id out of range? */ 100 if((L32GET(p+4) & 1) == 0 || id > MaxAPICNO) 101 return -1; 102 103 vladdr = nil; 104 already = ""; 105 f = 0; 106 apic = &mpapic[id]; 107 dprint("\tmpacpiproc: apic %#p\n", apic); 108 apic->paddr = laddr; 109 if (nprocid++ == 0) { 110 f = PcmpBP; 111 vladdr = vmap(apic->paddr, 1024); 112 if(apic->addr == nil){ 113 print("proc apic %d: failed to map %#p\n", id, 114 apic->paddr); 115 already = "(fail)"; 116 } 117 bootapic = apic; 118 } 119 apic->addr = vladdr; 120 121 if(apic->flags & PcmpUsed) 122 already = "(on)"; 123 else 124 mpnewproc(apic, id, f); 125 126 if (0) 127 dprint("\tapic proc %d/%d apicid %d flags%s%s %s\n", nprocid-1, 128 apic->machno, id, f & PcmpBP? " boot": "", 129 f & PcmpEN? " enabled": "", already); 130 USED(already); 131 132 rdmsr(Lapicbase, &base); 133 if (!(base & Lapicae)) { 134 dprint("mpacpiproc: enabling lapic\n"); 135 wrmsr(Lapicbase, base | Lapicae); 136 } 137 return 0; 138 } 139 140 static void 141 mpacpicpus(Madt *madt) 142 { 143 int i, n; 144 ulong laddr; 145 uchar *p; 146 147 laddr = L32GET(madt->addr); 148 dprint("APIC mpacpicpus(%#p) lapic addr %#lux, flags %#ux\n", 149 madt, laddr, L32GET(madt->flags)); 150 151 n = L32GET(&madt->sdthdr[4]); 152 p = madt->structures; 153 dprint("\t%d structures at %#p\n",n, p); 154 /* byte 0 is assumed to be type, 1 is assumed to be length */ 155 for(i = offsetof(Madt, structures[0]); i < n; i += p[1], p += p[1]) 156 switch(p[0]){ 157 case Apiclproc: 158 mpacpiproc(p, laddr); 159 break; 160 } 161 } 162 163 /* returns nil iff checksum is bad */ 164 static void * 165 mpacpirsdchecksum(void* addr, int length) 166 { 167 uchar *p, sum; 168 169 sum = 0; 170 for(p = addr; length-- > 0; p++) 171 sum += *p; 172 return sum == 0? addr: nil; 173 } 174 175 /* call func for each acpi table found */ 176 static void 177 mpacpiscan(void (*func)(uchar *)) 178 { 179 int asize, i, tbllen, sdtlen; 180 uintptr dhpa, sdtpa; 181 uchar *tbl, *sdt; 182 Rsd *rsd; 183 184 dprint("ACPI..."); 185 if((rsd = sigsearch("RSD PTR ")) == nil) { 186 dprint("none\n"); 187 return; 188 } 189 190 dprint("rsd %#p physaddr %#ux length %ud %#llux rev %d oem %.6s\n", 191 rsd, L32GET(rsd->raddr), L32GET(rsd->length), 192 l64get(rsd->xaddr), rsd->revision, (char*)rsd->oemid); 193 194 if(rsd->revision == 2){ 195 if(mpacpirsdchecksum(rsd, 36) == nil) 196 return; 197 asize = 8; 198 sdtpa = l64get(rsd->xaddr); 199 } else { 200 if(mpacpirsdchecksum(rsd, 20) == nil) 201 return; 202 asize = 4; 203 sdtpa = L32GET(rsd->raddr); 204 } 205 206 if((sdt = vmap(sdtpa, 8)) == nil) 207 return; 208 if((sdt[0] != 'R' && sdt[0] != 'X') || memcmp(sdt+1, "SDT", 3) != 0){ 209 vunmap(sdt, 8); 210 return; 211 } 212 sdtlen = L32GET(sdt + 4); 213 vunmap(sdt, 8); 214 215 if((sdt = vmap(sdtpa, sdtlen)) == nil) 216 return; 217 if(mpacpirsdchecksum(sdt, sdtlen) != nil) 218 for(i = 36; i < sdtlen; i += asize){ 219 if(asize == 8) 220 dhpa = l64get(sdt+i); 221 else 222 dhpa = L32GET(sdt+i); 223 224 if((tbl = vmap(dhpa, 8)) == nil) 225 continue; 226 tbllen = L32GET(tbl + 4); 227 vunmap(tbl, 8); 228 229 if((tbl = vmap(dhpa, tbllen)) == nil) 230 continue; 231 if(mpacpirsdchecksum(tbl, tbllen) != nil) 232 (*func)(tbl); 233 vunmap(tbl, tbllen); 234 } 235 vunmap(sdt, sdtlen); 236 } 237 238 static void 239 mpacpitbl(uchar *p) 240 { 241 /* for now, just activate any idle cpus */ 242 if (memcmp(p, "APIC", 4) == 0) 243 mpacpicpus((Madt *)p); 244 } 245 246 static void 247 mpacpi(void) 248 { 249 mpdebug = getconf("*debugmp") != nil; 250 mpacpiscan(mpacpitbl); 251 } 252 253 void (*mpacpifunc)(void) = mpacpi; 254