1/* $OpenBSD: cpu.c,v 1.83 2023/06/15 22:18:07 cheloha Exp $ */ 2 3/* 4 * Copyright (c) 1997-2004 Opsycon AB (www.opsycon.se) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/proc.h> 32#include <sys/atomic.h> 33#include <sys/device.h> 34#include <sys/malloc.h> 35 36#include <uvm/uvm_extern.h> 37 38#include <machine/cpu.h> 39#include <mips64/mips_cpu.h> 40#include <machine/autoconf.h> 41 42int cpumatch(struct device *, void *, void *); 43void cpuattach(struct device *, struct device *, void *); 44 45struct cpu_info cpu_info_primary; 46struct cpu_info *cpu_info_list = &cpu_info_primary; 47struct cpu_info *cpu_info_secondaries; 48 49extern void cpu_idle_cycle_nop(void); 50extern void cpu_idle_cycle_wait(void); 51void (*cpu_idle_cycle_func)(void) = cpu_idle_cycle_nop; 52 53vaddr_t cache_valias_mask; 54int cpu_has_synced_cp0_count; 55int cpu_has_userlocal; 56 57const struct cfattach cpu_ca = { 58 sizeof(struct device), cpumatch, cpuattach 59}; 60struct cfdriver cpu_cd = { 61 NULL, "cpu", DV_DULL, 62}; 63 64int 65cpumatch(struct device *parent, void *match, void *aux) 66{ 67 struct cpu_attach_args *caa = aux; 68 69 /* make sure that we're looking for a CPU. */ 70 if (strcmp(caa->caa_maa.maa_name, cpu_cd.cd_name) != 0) 71 return 0; 72 73 return 20; /* Make CPU probe first */ 74} 75 76void 77cpuattach(struct device *parent, struct device *dev, void *aux) 78{ 79 struct cpu_attach_args *caa = aux; 80 struct cpu_hwinfo *ch = caa->caa_hw; 81 struct cpu_info *ci; 82 int cpuno = dev->dv_unit; 83 int fptype, vers_maj, vers_min; 84 int displayver; 85 86#ifdef MULTIPROCESSOR 87 if (cpuno == 0) { 88 ci = &cpu_info_primary; 89 ci->ci_flags |= CPUF_RUNNING | CPUF_PRESENT | CPUF_PRIMARY; 90 if (ncpusfound > 1) { 91 cpu_info_secondaries = (struct cpu_info *) 92 alloc_contiguous_pages(sizeof(struct cpu_info) * 93 (ncpusfound - 1)); 94 if (cpu_info_secondaries == NULL) 95 panic("unable to allocate cpu_info"); 96 } 97 } else { 98 ci = &cpu_info_secondaries[cpuno - 1]; 99 ci->ci_next = cpu_info_list->ci_next; 100 cpu_info_list->ci_next = ci; 101 ci->ci_flags |= CPUF_PRESENT; 102 } 103#else 104 ci = &cpu_info_primary; 105#endif 106 ci->ci_self = ci; 107 ci->ci_cpuid = cpuno; 108 ci->ci_dev = dev; 109 bcopy(ch, &ci->ci_hw, sizeof(struct cpu_hwinfo)); 110#ifdef MULTIPROCESSOR 111 /* 112 * When attaching secondary processors, cache information is not 113 * available yet. Copy the cache information from the primary cpu 114 * instead. 115 * XXX The MP boot sequence needs to be reworked to avoid this. 116 */ 117 if (!CPU_IS_PRIMARY(ci)) { 118 ci->ci_l1inst = cpu_info_primary.ci_l1inst; 119 ci->ci_l1data = cpu_info_primary.ci_l1data; 120 ci->ci_l2 = cpu_info_primary.ci_l2; 121 ci->ci_l3 = cpu_info_primary.ci_l3; 122 } 123#endif 124 125 printf(": "); 126 127 displayver = 1; 128 fptype = (ch->c1prid >> 8) & 0xff; 129 vers_maj = (ch->c0prid >> 4) & 0x0f; 130 vers_min = ch->c0prid & 0x0f; 131 switch (ch->type) { 132 case MIPS_LOONGSON2: 133 switch (ch->c0prid & 0xff) { 134 case 0x00: 135 case 0x02: 136 case 0x03: 137 printf("STC Loongson2%c CPU", 'C' + vers_min); 138 break; 139 case 0x05: 140 printf("STC Loongson3%c CPU", 'A' + vers_min - 5); 141 break; 142 case 0x08: 143 printf("STC Loongson3A2000/3B2000 CPU"); 144 break; 145 default: 146 printf("Unknown STC Loongson CPU type (%02x)", 147 ch->c0prid & 0xff); 148 break; 149 } 150 displayver = 0; 151 break; 152 case MIPS_CN50XX: 153 printf("CN50xx CPU"); 154 fptype = MIPS_SOFT; 155 break; 156 case MIPS_CN61XX: 157 if (ci->ci_l2.size < 1024 * 1024) 158 printf("CN60xx CPU"); 159 else 160 printf("CN61xx CPU"); 161 fptype = MIPS_SOFT; 162 break; 163 case MIPS_CN63XX: 164 printf("CN62xx/CN63xx CPU"); 165 fptype = MIPS_SOFT; 166 break; 167 case MIPS_CN66XX: 168 printf("CN66xx CPU"); 169 fptype = MIPS_SOFT; 170 break; 171 case MIPS_CN68XX: 172 printf("CN68xx CPU"); 173 fptype = MIPS_SOFT; 174 break; 175 case MIPS_CN71XX: 176 printf("CN70xx/CN71xx CPU"); 177 break; 178 case MIPS_CN73XX: 179 printf("CN72xx/CN73xx CPU"); 180 break; 181 case MIPS_CN78XX: 182 printf("CN76xx/CN77xx/CN78xx CPU"); 183 break; 184 default: 185 printf("Unknown CPU type (0x%x)", ch->type); 186 break; 187 } 188 if (displayver != 0) 189 printf(" rev %d.%d", vers_maj, vers_min); 190 printf(" %d MHz, ", ch->clock / 1000000); 191 192 displayver = 1; 193 vers_maj = (ch->c1prid >> 4) & 0x0f; 194 vers_min = ch->c1prid & 0x0f; 195 switch (fptype) { 196 case MIPS_SOFT: 197#ifdef FPUEMUL 198 printf("Software FP emulation"); 199#else 200 printf("no FPU"); 201#endif 202 displayver = 0; 203 break; 204 case MIPS_LOONGSON2: 205 switch (ch->c1prid & 0xff) { 206 case 0x00: 207 case 0x02: 208 case 0x03: 209 printf("STC Loongson2%c FPU", 'C' + vers_min); 210 break; 211 case 0x05: 212 printf("STC Loongson3%c FPU", 'A' + vers_min - 5); 213 break; 214 case 0x08: 215 printf("STC Loongson3A2000/3B2000 FPU"); 216 break; 217 default: 218 printf("Unknown STC Loongson FPU type (%02x)", 219 ch->c1prid & 0xff); 220 break; 221 } 222 displayver = 0; 223 break; 224 case MIPS_CN71XX: 225 printf("CN70xx/CN71xx FPU"); 226 break; 227 case MIPS_CN73XX: 228 printf("CN72xx/CN73xx FPU"); 229 break; 230 case MIPS_CN78XX: 231 printf("CN76xx/CN77xx/CN78xx FPU"); 232 break; 233 default: 234 printf("Unknown FPU type (0x%x)", fptype); 235 break; 236 } 237 if (displayver != 0) 238 printf(" rev %d.%d", vers_maj, vers_min); 239 printf("\n"); 240 241 if (ci->ci_l1inst.sets == ci->ci_l1data.sets) { 242 printf("cpu%d: cache L1-I %dKB D %dKB ", cpuno, 243 ci->ci_l1inst.size / 1024, ci->ci_l1data.size / 1024); 244 if (ci->ci_l1inst.sets == 1) 245 printf("direct"); 246 else 247 printf("%d way", ci->ci_l1inst.sets); 248 } else { 249 printf("cpu%d: cache L1-I %dKB ", cpuno, 250 ci->ci_l1inst.size / 1024); 251 if (ci->ci_l1inst.sets == 1) 252 printf("direct"); 253 else 254 printf("%d way", ci->ci_l1inst.sets); 255 printf(" D %dKB ", ci->ci_l1data.size / 1024); 256 if (ci->ci_l1data.sets == 1) 257 printf("direct"); 258 else 259 printf("%d way", ci->ci_l1data.sets); 260 } 261 262 if (ci->ci_l2.size != 0) { 263 printf(", L2 %dKB ", ci->ci_l2.size / 1024); 264 if (ci->ci_l2.sets == 1) 265 printf("direct"); 266 else 267 printf("%d way", ci->ci_l2.sets); 268 } 269 if (ci->ci_l3.size != 0) { 270 printf(", L3 %dKB ", ci->ci_l3.size / 1024); 271 if (ci->ci_l3.sets == 1) 272 printf("direct"); 273 else 274 printf("%d way", ci->ci_l3.sets); 275 } 276 277 if (cpuno == 0) { 278 switch (ch->type) { 279 case MIPS_CN50XX: 280 case MIPS_CN61XX: 281 case MIPS_CN71XX: 282 case MIPS_CN73XX: 283 cpu_idle_cycle_func = cpu_idle_cycle_wait; 284 break; 285 } 286 } 287 288 printf("\n"); 289 290#ifdef DEBUG 291 printf("cpu%d: L1 set size %d:%d\n", cpuno, 292 ci->ci_l1inst.setsize, ci->ci_l1data.setsize); 293 printf("cpu%d: L1 line size %d:%d\n", cpuno, 294 ci->ci_l1inst.linesize, ci->ci_l1data.linesize); 295 printf("cpu%d: L2 line size %d\n", cpuno, ci->ci_l2.linesize); 296 printf("cpu%d: cache configuration %x\n", 297 cpuno, ci->ci_cacheconfiguration); 298 printf("cpu%d: virtual alias mask 0x%lx\n", cpuno, cache_valias_mask); 299 printf("cpu%d: config register %016lx, status register %016lx\n", 300 cpuno, cp0_get_config(), getsr()); 301#endif 302} 303 304void 305cpu_switchto(struct proc *oldproc, struct proc *newproc) 306{ 307#ifdef MULTIPROCESSOR 308 struct cpu_info *ci = curcpu(); 309 if (ci->ci_fpuproc) 310 save_fpu(); 311#endif 312 313 cpu_switchto_asm(oldproc, newproc); 314} 315 316void 317enable_fpu(struct proc *p) 318{ 319 struct cpu_info *ci = curcpu(); 320 321 if (!CPU_HAS_FPU(ci)) 322 return; 323 324 if (p->p_md.md_regs->sr & SR_FR_32) 325 MipsSwitchFPState(ci->ci_fpuproc, p->p_md.md_regs); 326 else 327 MipsSwitchFPState16(ci->ci_fpuproc, p->p_md.md_regs); 328 atomic_inc_int(&uvmexp.fpswtch); 329 330 ci->ci_fpuproc = p; 331 p->p_md.md_regs->sr |= SR_COP_1_BIT; 332 p->p_md.md_flags |= MDP_FPUSED; 333} 334 335void 336save_fpu(void) 337{ 338 struct cpu_info *ci = curcpu(); 339 struct proc *p; 340 341 if (!CPU_HAS_FPU(ci)) 342 return; 343 344 KASSERT(ci->ci_fpuproc); 345 p = ci->ci_fpuproc; 346 if (p->p_md.md_regs->sr & SR_FR_32) 347 MipsSaveCurFPState(p); 348 else 349 MipsSaveCurFPState16(p); 350} 351 352void 353need_resched(struct cpu_info *ci) 354{ 355 ci->ci_want_resched = 1; 356 357 if (ci->ci_curproc != NULL) { 358 /* 359 * Ensure that preceding stores are visible to other CPUs 360 * before setting the AST flag. 361 */ 362 membar_producer(); 363 364 aston(ci->ci_curproc); 365 cpu_unidle(ci); 366 } 367} 368 369#ifdef MULTIPROCESSOR 370struct cpu_info * 371get_cpu_info(int cpuno) 372{ 373 struct cpu_info *ci; 374 CPU_INFO_ITERATOR cii; 375 376 CPU_INFO_FOREACH(cii, ci) { 377 if (ci->ci_cpuid == cpuno) 378 return ci; 379 } 380 return NULL; 381} 382 383void 384cpu_boot_secondary_processors(void) 385{ 386 struct cpu_info *ci; 387 CPU_INFO_ITERATOR cii; 388 389 mips64_ipi_init(); 390 391 CPU_INFO_FOREACH(cii, ci) { 392 if ((ci->ci_flags & CPUF_PRESENT) == 0) 393 continue; 394 if (CPU_IS_PRIMARY(ci)) 395 continue; 396 397 ci->ci_randseed = (arc4random() & 0x7fffffff) + 1; 398 clockqueue_init(&ci->ci_queue); 399 sched_init_cpu(ci); 400 cpu_boot_secondary(ci); 401 } 402} 403 404void 405cpu_unidle(struct cpu_info *ci) 406{ 407 if (ci != curcpu()) 408 mips64_send_ipi(ci->ci_cpuid, MIPS64_IPI_NOP); 409} 410 411vaddr_t 412alloc_contiguous_pages(size_t size) 413{ 414 struct pglist mlist; 415 struct vm_page *m; 416 int error; 417 paddr_t pa; 418 419 TAILQ_INIT(&mlist); 420 error = uvm_pglistalloc(round_page(size), 0, (paddr_t)-1, 0, 0, 421 &mlist, 1, UVM_PLA_NOWAIT | UVM_PLA_ZERO); 422 if (error) 423 return 0; 424 m = TAILQ_FIRST(&mlist); 425 pa = VM_PAGE_TO_PHYS(m); 426 427 return PHYS_TO_XKPHYS(pa, CCA_CACHED); 428} 429#endif 430