| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | /* |
| 3 | * QEMU LoongArch CPU |
| 4 | * |
| 5 | * Copyright (c) 2021 Loongson Technology Corporation Limited |
| 6 | */ |
| 7 | |
| 8 | #include "qemu/osdep.h" |
| 9 | #include "qemu/log.h" |
| 10 | #include "qemu/qemu-print.h" |
| 11 | #include "qapi/error.h" |
| 12 | #include "qemu/module.h" |
| 13 | #include "system/qtest.h" |
| 14 | #include "system/tcg.h" |
| 15 | #include "system/kvm.h" |
| 16 | #include "kvm/kvm_loongarch.h" |
| 17 | #include "hw/core/qdev-properties.h" |
| 18 | #include "exec/translation-block.h" |
| 19 | #include "cpu.h" |
| 20 | #include "cpu-mmu.h" |
| 21 | #include "internals.h" |
| 22 | #include "fpu/softfloat-helpers.h" |
| 23 | #include "csr.h" |
| 24 | #ifndef CONFIG_USER_ONLY |
| 25 | #include "system/reset.h" |
| 26 | #endif |
| 27 | #include "vec.h" |
| 28 | #ifdef CONFIG_KVM |
| 29 | #include <linux/kvm.h> |
| 30 | #endif |
| 31 | #include "tcg/tcg_loongarch.h" |
| 32 | #include "disas/capstone.h" |
| 33 | |
| 34 | const char * const regnames[32] = { |
| 35 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| 36 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
| 37 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", |
| 38 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", |
| 39 | }; |
| 40 | |
| 41 | const char * const fregnames[32] = { |
| 42 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
| 43 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", |
| 44 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", |
| 45 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", |
| 46 | }; |
| 47 | |
| 48 | static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) |
| 49 | { |
| 50 | set_pc(cpu_env(cs), value); |
| 51 | } |
| 52 | |
| 53 | static vaddr loongarch_cpu_get_pc(CPUState *cs) |
| 54 | { |
| 55 | return cpu_env(cs)->pc; |
| 56 | } |
| 57 | |
| 58 | #ifndef CONFIG_USER_ONLY |
| 59 | #include "hw/loongarch/virt.h" |
| 60 | |
| 61 | void loongarch_cpu_update_irq(LoongArchCPU *cpu, uint64_t old) |
| 62 | { |
| 63 | CPULoongArchState *env = &cpu->env; |
| 64 | CPUState *cs = CPU(cpu); |
| 65 | CPUSysState *sys = env_sys(env); |
| 66 | |
| 67 | if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) { |
| 68 | if (!FIELD_EX64(old, CSR_ESTAT, IS)) { |
| 69 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
| 70 | } |
| 71 | } else { |
| 72 | if (FIELD_EX64(old, CSR_ESTAT, IS)) { |
| 73 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | void loongarch_cpu_set_irq(void *opaque, int irq, int level) |
| 79 | { |
| 80 | LoongArchCPU *cpu = opaque; |
| 81 | CPULoongArchState *env = &cpu->env; |
| 82 | CPUSysState *sys = env_sys(env); |
| 83 | uint64_t old; |
| 84 | |
| 85 | if (irq < 0 || irq >= N_IRQS) { |
| 86 | return; |
| 87 | } |
| 88 | |
| 89 | if (kvm_enabled()) { |
| 90 | kvm_loongarch_set_interrupt(cpu, irq, level); |
| 91 | } else if (tcg_enabled()) { |
| 92 | old = sys->CSR_ESTAT; |
| 93 | sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0); |
| 94 | loongarch_cpu_update_irq(cpu, old); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | /* Check if there is pending and not masked out interrupt */ |
| 99 | bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) |
| 100 | { |
| 101 | uint32_t pending; |
| 102 | uint32_t status; |
| 103 | CPUSysState *sys = env_sys(env); |
| 104 | |
| 105 | pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS); |
| 106 | status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE); |
| 107 | |
| 108 | return (pending & status) != 0; |
| 109 | } |
| 110 | #endif |
| 111 | |
| 112 | #ifndef CONFIG_USER_ONLY |
| 113 | bool loongarch_cpu_has_work(CPUState *cs) |
| 114 | { |
| 115 | bool has_work = false; |
| 116 | |
| 117 | if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && |
| 118 | cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) { |
| 119 | has_work = true; |
| 120 | } |
| 121 | |
| 122 | return has_work; |
| 123 | } |
| 124 | #endif /* !CONFIG_USER_ONLY */ |
| 125 | |
| 126 | static void loongarch_la464_init_csr(DeviceState *dev) |
| 127 | { |
| 128 | #ifndef CONFIG_USER_ONLY |
| 129 | static bool initialized; |
| 130 | LoongArchCPU *cpu = LOONGARCH_CPU(dev); |
| 131 | CPULoongArchState *env = &cpu->env; |
| 132 | CPUSysState *sys = env_sys(env); |
| 133 | int i, num; |
| 134 | |
| 135 | if (!initialized) { |
| 136 | initialized = true; |
| 137 | num = FIELD_EX64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM); |
| 138 | for (i = num; i < 16; i++) { |
| 139 | set_csr_flag(LOONGARCH_CSR_SAVE(i), CSRFL_UNUSED); |
| 140 | } |
| 141 | set_csr_flag(LOONGARCH_CSR_IMPCTL1, CSRFL_UNUSED); |
| 142 | set_csr_flag(LOONGARCH_CSR_IMPCTL2, CSRFL_UNUSED); |
| 143 | set_csr_flag(LOONGARCH_CSR_MERRCTL, CSRFL_UNUSED); |
| 144 | set_csr_flag(LOONGARCH_CSR_MERRINFO1, CSRFL_UNUSED); |
| 145 | set_csr_flag(LOONGARCH_CSR_MERRINFO2, CSRFL_UNUSED); |
| 146 | set_csr_flag(LOONGARCH_CSR_MERRENTRY, CSRFL_UNUSED); |
| 147 | set_csr_flag(LOONGARCH_CSR_MERRERA, CSRFL_UNUSED); |
| 148 | set_csr_flag(LOONGARCH_CSR_MERRSAVE, CSRFL_UNUSED); |
| 149 | set_csr_flag(LOONGARCH_CSR_CTAG, CSRFL_UNUSED); |
| 150 | |
| 151 | for (i = env->perf_event_num; i < MAX_PERF_EVENTS; i++) { |
| 152 | set_csr_flag(LOONGARCH_CSR_PERFCTRL(i), CSRFL_UNUSED); |
| 153 | set_csr_flag(LOONGARCH_CSR_PERFCNTR(i), CSRFL_UNUSED); |
| 154 | } |
| 155 | } |
| 156 | #endif |
| 157 | } |
| 158 | |
| 159 | static bool loongarch_get_lsx(Object *obj, Error **errp) |
| 160 | { |
| 161 | return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; |
| 162 | } |
| 163 | |
| 164 | static void loongarch_set_lsx(Object *obj, bool value, Error **errp) |
| 165 | { |
| 166 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 167 | uint32_t val; |
| 168 | |
| 169 | cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; |
| 170 | if (cpu->lsx == ON_OFF_AUTO_OFF) { |
| 171 | cpu->lasx = ON_OFF_AUTO_OFF; |
| 172 | if (cpu->lasx == ON_OFF_AUTO_ON) { |
| 173 | error_setg(errp, "Failed to disable LSX since LASX is enabled"); |
| 174 | return; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | if (kvm_enabled()) { |
| 179 | /* kvm feature detection in function kvm_arch_init_vcpu */ |
| 180 | return; |
| 181 | } |
| 182 | |
| 183 | /* LSX feature detection in TCG mode */ |
| 184 | val = cpu->env.cpucfg[2]; |
| 185 | if (cpu->lsx == ON_OFF_AUTO_ON) { |
| 186 | if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { |
| 187 | error_setg(errp, "Failed to enable LSX in TCG mode"); |
| 188 | return; |
| 189 | } |
| 190 | } else { |
| 191 | cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); |
| 192 | val = cpu->env.cpucfg[2]; |
| 193 | } |
| 194 | |
| 195 | cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); |
| 196 | } |
| 197 | |
| 198 | static bool loongarch_get_lasx(Object *obj, Error **errp) |
| 199 | { |
| 200 | return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; |
| 201 | } |
| 202 | |
| 203 | static void loongarch_set_lasx(Object *obj, bool value, Error **errp) |
| 204 | { |
| 205 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 206 | uint32_t val; |
| 207 | |
| 208 | cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; |
| 209 | if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { |
| 210 | error_setg(errp, "Failed to enable LASX since lSX is disabled"); |
| 211 | return; |
| 212 | } |
| 213 | |
| 214 | if (kvm_enabled()) { |
| 215 | /* kvm feature detection in function kvm_arch_init_vcpu */ |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | /* LASX feature detection in TCG mode */ |
| 220 | val = cpu->env.cpucfg[2]; |
| 221 | if (cpu->lasx == ON_OFF_AUTO_ON) { |
| 222 | if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { |
| 223 | error_setg(errp, "Failed to enable LASX in TCG mode"); |
| 224 | return; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); |
| 229 | } |
| 230 | |
| 231 | static bool loongarch_get_msgint(Object *obj, Error **errp) |
| 232 | { |
| 233 | return LOONGARCH_CPU(obj)->msgint != ON_OFF_AUTO_OFF; |
| 234 | } |
| 235 | |
| 236 | static void loongarch_set_msgint(Object *obj, bool value, Error **errp) |
| 237 | { |
| 238 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 239 | |
| 240 | cpu->msgint = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; |
| 241 | |
| 242 | if (kvm_enabled()) { |
| 243 | /* kvm feature detection in function kvm_arch_init_vcpu */ |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, value); |
| 248 | } |
| 249 | |
| 250 | static bool loongarch_get_ptw(Object *obj, Error **errp) |
| 251 | { |
| 252 | return LOONGARCH_CPU(obj)->ptw != ON_OFF_AUTO_OFF; |
| 253 | } |
| 254 | |
| 255 | static void loongarch_set_ptw(Object *obj, bool value, Error **errp) |
| 256 | { |
| 257 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 258 | |
| 259 | cpu->ptw = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; |
| 260 | |
| 261 | if (kvm_enabled()) { |
| 262 | /* kvm feature detection in function kvm_arch_init_vcpu */ |
| 263 | return; |
| 264 | } |
| 265 | |
| 266 | cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, HPTW, value); |
| 267 | } |
| 268 | |
| 269 | static void loongarch_cpu_post_init(Object *obj) |
| 270 | { |
| 271 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 272 | |
| 273 | cpu->lbt = ON_OFF_AUTO_OFF; |
| 274 | cpu->pmu = ON_OFF_AUTO_OFF; |
| 275 | cpu->lsx = ON_OFF_AUTO_AUTO; |
| 276 | cpu->lasx = ON_OFF_AUTO_AUTO; |
| 277 | object_property_add_bool(obj, "lsx", loongarch_get_lsx, |
| 278 | loongarch_set_lsx); |
| 279 | object_property_add_bool(obj, "lasx", loongarch_get_lasx, |
| 280 | loongarch_set_lasx); |
| 281 | object_property_add_bool(obj, "msgint", loongarch_get_msgint, |
| 282 | loongarch_set_msgint); |
| 283 | object_property_add_bool(obj, "ptw", loongarch_get_ptw, |
| 284 | loongarch_set_ptw); |
| 285 | /* lbt is enabled only in kvm mode, not supported in tcg mode */ |
| 286 | |
| 287 | if (kvm_enabled()) { |
| 288 | kvm_loongarch_cpu_post_init(cpu); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | static void loongarch_la464_initfn(Object *obj) |
| 293 | { |
| 294 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 295 | CPULoongArchState *env = &cpu->env; |
| 296 | CPUSysState *sys; |
| 297 | uint32_t data = 0, field; |
| 298 | int i; |
| 299 | |
| 300 | set_sys_state(env, &env->sys_states[0]); |
| 301 | for (i = 0; i < 21; i++) { |
| 302 | env->cpucfg[i] = 0x0; |
| 303 | } |
| 304 | |
| 305 | cpu->dtb_compatible = "loongarch,Loongson-3A5000"; |
| 306 | data = FIELD_DP32(data, CPUCFG0, PRID, 0x10); |
| 307 | data = FIELD_DP32(data, CPUCFG0, SERID, PRID_SERIES_LA464); |
| 308 | data = FIELD_DP32(data, CPUCFG0, VENID, PRID_VENDOR_LOONGSON); |
| 309 | env->cpucfg[0] = data; |
| 310 | memccpy((void *)&env->vendor_id, CPU_VENDOR_LOONGSON, 0, 8); |
| 311 | memccpy((void *)&env->cpu_id, CPU_MODEL_3A5000, 0, 8); |
| 312 | |
| 313 | data = 0; |
| 314 | data = FIELD_DP32(data, CPUCFG1, ARCH, 2); |
| 315 | data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); |
| 316 | data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); |
| 317 | if (kvm_enabled()) { |
| 318 | /* GPA address width of VM is 47, field value is 47 - 1 */ |
| 319 | field = 0x2e; |
| 320 | } else { |
| 321 | field = 0x2f; /* 48 bit - 1 */ |
| 322 | } |
| 323 | data = FIELD_DP32(data, CPUCFG1, PALEN, field); |
| 324 | data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); |
| 325 | data = FIELD_DP32(data, CPUCFG1, UAL, 1); |
| 326 | data = FIELD_DP32(data, CPUCFG1, RI, 1); |
| 327 | data = FIELD_DP32(data, CPUCFG1, EP, 1); |
| 328 | data = FIELD_DP32(data, CPUCFG1, RPLV, 1); |
| 329 | data = FIELD_DP32(data, CPUCFG1, HP, 1); |
| 330 | data = FIELD_DP32(data, CPUCFG1, CRC, 1); |
| 331 | env->cpucfg[1] = data; |
| 332 | |
| 333 | data = 0; |
| 334 | data = FIELD_DP32(data, CPUCFG2, FP, 1); |
| 335 | data = FIELD_DP32(data, CPUCFG2, FP_SP, 1); |
| 336 | data = FIELD_DP32(data, CPUCFG2, FP_DP, 1); |
| 337 | data = FIELD_DP32(data, CPUCFG2, FP_VER, 1); |
| 338 | data = FIELD_DP32(data, CPUCFG2, LSX, 1), |
| 339 | data = FIELD_DP32(data, CPUCFG2, LASX, 1), |
| 340 | data = FIELD_DP32(data, CPUCFG2, LLFTP, 1); |
| 341 | data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1); |
| 342 | data = FIELD_DP32(data, CPUCFG2, LSPW, 1); |
| 343 | data = FIELD_DP32(data, CPUCFG2, LAM, 1); |
| 344 | env->cpucfg[2] = data; |
| 345 | |
| 346 | data = 0; |
| 347 | data = FIELD_DP32(data, CPUCFG3, CCDMA, 1); |
| 348 | data = FIELD_DP32(data, CPUCFG3, UCACC, 1); |
| 349 | data = FIELD_DP32(data, CPUCFG3, LLEXC, 1); |
| 350 | data = FIELD_DP32(data, CPUCFG3, SCDLY, 1); |
| 351 | data = FIELD_DP32(data, CPUCFG3, LLDBAR, 1); |
| 352 | data = FIELD_DP32(data, CPUCFG3, ITLBHMC, 1); |
| 353 | data = FIELD_DP32(data, CPUCFG3, ICHMC, 1); |
| 354 | data = FIELD_DP32(data, CPUCFG3, SPW_LVL, 4); |
| 355 | data = FIELD_DP32(data, CPUCFG3, SPW_HP_HF, 1); |
| 356 | if (kvm_enabled()) { |
| 357 | data = FIELD_DP32(data, CPUCFG3, RVA, 1); |
| 358 | data = FIELD_DP32(data, CPUCFG3, RVAMAX, 7); |
| 359 | } |
| 360 | env->cpucfg[3] = data; |
| 361 | |
| 362 | env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */ |
| 363 | |
| 364 | data = 0; |
| 365 | data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1); |
| 366 | data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1); |
| 367 | env->cpucfg[5] = data; |
| 368 | |
| 369 | data = 0; |
| 370 | data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1); |
| 371 | data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1); |
| 372 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1); |
| 373 | data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1); |
| 374 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1); |
| 375 | data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1); |
| 376 | data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1); |
| 377 | data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1); |
| 378 | env->cpucfg[16] = data; |
| 379 | |
| 380 | data = 0; |
| 381 | data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3); |
| 382 | data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8); |
| 383 | data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6); |
| 384 | env->cpucfg[17] = data; |
| 385 | |
| 386 | data = 0; |
| 387 | data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3); |
| 388 | data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8); |
| 389 | data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6); |
| 390 | env->cpucfg[18] = data; |
| 391 | |
| 392 | data = 0; |
| 393 | data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15); |
| 394 | data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8); |
| 395 | data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6); |
| 396 | env->cpucfg[19] = data; |
| 397 | |
| 398 | data = 0; |
| 399 | data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); |
| 400 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); |
| 401 | data = FIELD_DP32(data, CPUCFG20, L3IU_SIZE, 6); |
| 402 | env->cpucfg[20] = data; |
| 403 | |
| 404 | sys = env_sys(env); |
| 405 | sys->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); |
| 406 | |
| 407 | sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8); |
| 408 | sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f); |
| 409 | sys->CSR_PRCFG1 = FIELD_DP64(sys->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7); |
| 410 | |
| 411 | sys->CSR_PRCFG2 = 0x3ffff000; |
| 412 | |
| 413 | sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); |
| 414 | sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); |
| 415 | sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); |
| 416 | sys->CSR_PRCFG3 = FIELD_DP64(sys->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); |
| 417 | |
| 418 | cpu->msgint = ON_OFF_AUTO_OFF; |
| 419 | cpu->ptw = ON_OFF_AUTO_OFF; |
| 420 | loongarch_cpu_post_init(obj); |
| 421 | } |
| 422 | |
| 423 | static void loongarch_la132_initfn(Object *obj) |
| 424 | { |
| 425 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 426 | CPULoongArchState *env = &cpu->env; |
| 427 | uint32_t data = 0; |
| 428 | int i; |
| 429 | |
| 430 | set_sys_state(env, &env->sys_states[0]); |
| 431 | for (i = 0; i < 21; i++) { |
| 432 | env->cpucfg[i] = 0x0; |
| 433 | } |
| 434 | |
| 435 | cpu->dtb_compatible = "loongarch,Loongson-1C103"; |
| 436 | data = FIELD_DP32(data, CPUCFG0, PRID, 0x42); |
| 437 | data = FIELD_DP32(data, CPUCFG0, SERID, PRID_SERIES_LA132); |
| 438 | data = FIELD_DP32(data, CPUCFG0, VENID, PRID_VENDOR_LOONGSON); |
| 439 | env->cpucfg[0] = data; |
| 440 | memccpy((void *)&env->vendor_id, CPU_VENDOR_LOONGSON, 0, 8); |
| 441 | memccpy((void *)&env->cpu_id, CPU_MODEL_1C101, 0, 8); |
| 442 | |
| 443 | data = 0; |
| 444 | data = FIELD_DP32(data, CPUCFG1, ARCH, 1); /* LA32 */ |
| 445 | data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); |
| 446 | data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); |
| 447 | data = FIELD_DP32(data, CPUCFG1, PALEN, 0x1f); /* 32 bits */ |
| 448 | data = FIELD_DP32(data, CPUCFG1, VALEN, 0x1f); /* 32 bits */ |
| 449 | data = FIELD_DP32(data, CPUCFG1, UAL, 1); |
| 450 | data = FIELD_DP32(data, CPUCFG1, RI, 0); |
| 451 | data = FIELD_DP32(data, CPUCFG1, EP, 0); |
| 452 | data = FIELD_DP32(data, CPUCFG1, RPLV, 0); |
| 453 | data = FIELD_DP32(data, CPUCFG1, HP, 1); |
| 454 | data = FIELD_DP32(data, CPUCFG1, CRC, 1); |
| 455 | env->cpucfg[1] = data; |
| 456 | cpu->msgint = ON_OFF_AUTO_OFF; |
| 457 | cpu->ptw = ON_OFF_AUTO_OFF; |
| 458 | } |
| 459 | |
| 460 | static void loongarch_max_initfn(Object *obj) |
| 461 | { |
| 462 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 463 | /* '-cpu max': use it for max supported CPU features */ |
| 464 | loongarch_la464_initfn(obj); |
| 465 | |
| 466 | cpu->ptw = ON_OFF_AUTO_AUTO; |
| 467 | if (kvm_enabled()){ |
| 468 | cpu->msgint=ON_OFF_AUTO_OFF; |
| 469 | } |
| 470 | if (tcg_enabled()) { |
| 471 | uint32_t data = cpu->env.cpucfg[2]; |
| 472 | data = FIELD_DP32(data, CPUCFG2, HPTW, 1); |
| 473 | /* Enable LA v1.1 instructions */ |
| 474 | data = FIELD_DP32(data, CPUCFG2, FRECIPE, 1); |
| 475 | data = FIELD_DP32(data, CPUCFG2, LAM_BH, 1); |
| 476 | data = FIELD_DP32(data, CPUCFG2, LAMCAS, 1); |
| 477 | data = FIELD_DP32(data, CPUCFG2, LLACQ_SCREL, 1); |
| 478 | data = FIELD_DP32(data, CPUCFG2, SCQ, 1); |
| 479 | cpu->env.cpucfg[2] = data; |
| 480 | |
| 481 | data = cpu->env.cpucfg[3]; |
| 482 | data = FIELD_DP32(data, CPUCFG3, DBAR_HINTS, 1); |
| 483 | cpu->env.cpucfg[3] = data; |
| 484 | cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); |
| 485 | cpu->msgint = ON_OFF_AUTO_AUTO; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | #if defined(CONFIG_KVM) |
| 490 | static int read_cpuinfo(const char *field, char *value, int len) |
| 491 | { |
| 492 | FILE *f; |
| 493 | int ret = -1; |
| 494 | int field_len = strlen(field); |
| 495 | char line[512]; |
| 496 | |
| 497 | f = fopen("/proc/cpuinfo", "r"); |
| 498 | if (!f) { |
| 499 | return -1; |
| 500 | } |
| 501 | |
| 502 | do { |
| 503 | if (!fgets(line, sizeof(line), f)) { |
| 504 | break; |
| 505 | } |
| 506 | if (!strncmp(line, field, field_len)) { |
| 507 | strncpy(value, line, len); |
| 508 | ret = 0; |
| 509 | break; |
| 510 | } |
| 511 | } while (*line); |
| 512 | |
| 513 | fclose(f); |
| 514 | |
| 515 | return ret; |
| 516 | } |
| 517 | |
| 518 | static uint64_t get_host_cpu_model(void) |
| 519 | { |
| 520 | char line[512]; |
| 521 | char *ns; |
| 522 | static uint64_t cpuid; |
| 523 | |
| 524 | if (cpuid) { |
| 525 | return cpuid; |
| 526 | } |
| 527 | |
| 528 | if (read_cpuinfo("Model Name", line, sizeof(line))) { |
| 529 | return 0; |
| 530 | } |
| 531 | |
| 532 | ns = strchr(line, ':'); |
| 533 | if (!ns) { |
| 534 | return 0; |
| 535 | } |
| 536 | |
| 537 | ns = strstr(ns, "Loongson-"); |
| 538 | if (!ns) { |
| 539 | return 0; |
| 540 | } |
| 541 | |
| 542 | ns += strlen("Loongson-"); |
| 543 | memccpy((void *)&cpuid, ns, 0, 8); |
| 544 | return cpuid; |
| 545 | } |
| 546 | |
| 547 | static uint32_t get_host_cpucfg(int number) |
| 548 | { |
| 549 | unsigned int data = 0; |
| 550 | |
| 551 | #ifdef __loongarch__ |
| 552 | asm volatile("cpucfg %[val], %[reg]" |
| 553 | : [val] "=r" (data) |
| 554 | : [reg] "r" (number) |
| 555 | : "memory"); |
| 556 | #endif |
| 557 | |
| 558 | return data; |
| 559 | } |
| 560 | |
| 561 | static void loongarch_host_initfn(Object *obj) |
| 562 | { |
| 563 | uint32_t data, cpucfg, field; |
| 564 | uint64_t cpuid; |
| 565 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 566 | |
| 567 | loongarch_max_initfn(obj); |
| 568 | data = get_host_cpucfg(0); |
| 569 | if (data) { |
| 570 | cpu->env.cpucfg[0] = data; |
| 571 | } |
| 572 | |
| 573 | /* |
| 574 | * There is no exception in KVM hypervisor when these intructions are |
| 575 | * executed if HW support, KVM hypervisor cannot control this. |
| 576 | * |
| 577 | * Set cpucfg bits which cannot be controlled by KVM hypervisor. |
| 578 | */ |
| 579 | data = get_host_cpucfg(2); |
| 580 | cpucfg = cpu->env.cpucfg[2]; |
| 581 | field = FIELD_EX32(data, CPUCFG2, FRECIPE); |
| 582 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, FRECIPE, field); |
| 583 | field = FIELD_EX32(data, CPUCFG2, DIV32); |
| 584 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, DIV32, field); |
| 585 | field = FIELD_EX32(data, CPUCFG2, LAM_BH); |
| 586 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, LAM_BH, field); |
| 587 | field = FIELD_EX32(data, CPUCFG2, LAMCAS); |
| 588 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, LAMCAS, field); |
| 589 | field = FIELD_EX32(data, CPUCFG2, LLACQ_SCREL); |
| 590 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, LLACQ_SCREL, field); |
| 591 | field = FIELD_EX32(data, CPUCFG2, SCQ); |
| 592 | cpucfg = FIELD_DP32(cpucfg, CPUCFG2, SCQ, field); |
| 593 | cpu->env.cpucfg[2] = cpucfg; |
| 594 | |
| 595 | data = get_host_cpucfg(3); |
| 596 | cpucfg = cpu->env.cpucfg[3]; |
| 597 | field = FIELD_EX32(data, CPUCFG3, DBAR_HINTS); |
| 598 | cpucfg = FIELD_DP32(cpucfg, CPUCFG3, DBAR_HINTS, field); |
| 599 | field = FIELD_EX32(data, CPUCFG3, ALDORDER_STA); |
| 600 | cpucfg = FIELD_DP32(cpucfg, CPUCFG3, ALDORDER_STA, field); |
| 601 | field = FIELD_EX32(data, CPUCFG3, ASTORDER_STA); |
| 602 | cpucfg = FIELD_DP32(cpucfg, CPUCFG3, ASTORDER_STA, field); |
| 603 | field = FIELD_EX32(data, CPUCFG3, SLDORDER_STA); |
| 604 | cpucfg = FIELD_DP32(cpucfg, CPUCFG3, SLDORDER_STA, field); |
| 605 | cpu->env.cpucfg[3] = cpucfg; |
| 606 | |
| 607 | cpuid = get_host_cpu_model(); |
| 608 | if (cpuid) { |
| 609 | cpu->env.cpu_id = cpuid; |
| 610 | } |
| 611 | } |
| 612 | #endif |
| 613 | |
| 614 | static void loongarch_cpu_reset_hold(Object *obj, ResetType type) |
| 615 | { |
| 616 | uint8_t tlb_ps; |
| 617 | CPUState *cs = CPU(obj); |
| 618 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(obj); |
| 619 | CPULoongArchState *env = cpu_env(cs); |
| 620 | CPUSysState *sys = env_sys(env); |
| 621 | |
| 622 | if (lacc->parent_phases.hold) { |
| 623 | lacc->parent_phases.hold(obj, type); |
| 624 | } |
| 625 | |
| 626 | #ifdef CONFIG_TCG |
| 627 | env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; |
| 628 | |
| 629 | if (is_la64(env)) { |
| 630 | env->hw_pte_mask = MAKE_64BIT_MASK(0, 9) | |
| 631 | R_TLBENTRY_64_PPN_MASK | |
| 632 | R_TLBENTRY_64_NR_MASK | |
| 633 | R_TLBENTRY_64_NX_MASK | |
| 634 | R_TLBENTRY_64_RPLV_MASK; |
| 635 | } else { |
| 636 | env->hw_pte_mask = MAKE_64BIT_MASK(0, 9) | |
| 637 | R_TLBENTRY_32_PPN_MASK; |
| 638 | } |
| 639 | #endif |
| 640 | env->fcsr0 = 0x0; |
| 641 | |
| 642 | int n; |
| 643 | /* Set csr registers value after reset, see the manual 6.4. */ |
| 644 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PLV, 0); |
| 645 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, 0); |
| 646 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DA, 1); |
| 647 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PG, 0); |
| 648 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATF, 0); |
| 649 | sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DATM, 0); |
| 650 | |
| 651 | sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, FPE, 0); |
| 652 | sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, SXE, 0); |
| 653 | sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, ASXE, 0); |
| 654 | sys->CSR_EUEN = FIELD_DP64(sys->CSR_EUEN, CSR_EUEN, BTE, 0); |
| 655 | |
| 656 | sys->CSR_MISC = 0; |
| 657 | |
| 658 | sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, VS, 0); |
| 659 | sys->CSR_ECFG = FIELD_DP64(sys->CSR_ECFG, CSR_ECFG, LIE, 0); |
| 660 | |
| 661 | sys->CSR_ESTAT = sys->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); |
| 662 | sys->CSR_RVACFG = FIELD_DP64(sys->CSR_RVACFG, CSR_RVACFG, RBITS, 0); |
| 663 | sys->CSR_CPUID = cs->cpu_index; |
| 664 | sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0); |
| 665 | sys->CSR_LLBCTL = FIELD_DP64(sys->CSR_LLBCTL, CSR_LLBCTL, KLO, 0); |
| 666 | sys->CSR_TLBRERA = FIELD_DP64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); |
| 667 | sys->CSR_MERRCTL = FIELD_DP64(sys->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); |
| 668 | sys->CSR_TID = cs->cpu_index; |
| 669 | /* |
| 670 | * Workaround for edk2-stable202408, CSR PGD register is set only if |
| 671 | * its value is equal to zero for boot cpu, it causes reboot issue. |
| 672 | * |
| 673 | * Here clear CSR registers relative with TLB. |
| 674 | */ |
| 675 | sys->CSR_PGDH = 0; |
| 676 | sys->CSR_PGDL = 0; |
| 677 | sys->CSR_PWCH = 0; |
| 678 | sys->CSR_EENTRY = 0; |
| 679 | sys->CSR_TLBRENTRY = 0; |
| 680 | sys->CSR_MERRENTRY = 0; |
| 681 | /* set CSR_PWCL.PTBASE and CSR_STLBPS.PS bits from CSR_PRCFG2 */ |
| 682 | if (sys->CSR_PRCFG2 == 0) { |
| 683 | sys->CSR_PRCFG2 = 0x3fffff000; |
| 684 | } |
| 685 | tlb_ps = ctz32(sys->CSR_PRCFG2); |
| 686 | sys->CSR_STLBPS = FIELD_DP64(sys->CSR_STLBPS, CSR_STLBPS, PS, tlb_ps); |
| 687 | sys->CSR_PWCL = FIELD_DP64(sys->CSR_PWCL, CSR_PWCL, PTBASE, tlb_ps); |
| 688 | for (n = 0; n < 4; n++) { |
| 689 | sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV0, 0); |
| 690 | sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV1, 0); |
| 691 | sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV2, 0); |
| 692 | sys->CSR_DMW[n] = FIELD_DP64(sys->CSR_DMW[n], CSR_DMW, PLV3, 0); |
| 693 | } |
| 694 | |
| 695 | #ifndef CONFIG_USER_ONLY |
| 696 | env->pc = 0x1c000000; |
| 697 | #ifdef CONFIG_TCG |
| 698 | memset(env->tlb, 0, sizeof(env->tlb)); |
| 699 | #endif |
| 700 | if (kvm_enabled()) { |
| 701 | kvm_arch_reset_vcpu(cs); |
| 702 | } |
| 703 | #endif |
| 704 | |
| 705 | #ifdef CONFIG_TCG |
| 706 | restore_fp_status(env); |
| 707 | #endif |
| 708 | cs->exception_index = -1; |
| 709 | } |
| 710 | |
| 711 | static void loongarch_cpu_disas_set_info(const CPUState *cs, |
| 712 | disassemble_info *info) |
| 713 | { |
| 714 | CPULoongArchState *env = cpu_env((CPUState *)cs); |
| 715 | |
| 716 | info->endian = BFD_ENDIAN_LITTLE; |
| 717 | info->print_insn = print_insn_loongarch; |
| 718 | |
| 719 | info->cap_arch = CS_ARCH_LOONGARCH; |
| 720 | info->cap_insn_unit = 4; |
| 721 | info->cap_insn_split = 4; |
| 722 | info->cap_mode = is_la64(env) ? CS_MODE_LOONGARCH64 : CS_MODE_LOONGARCH32; |
| 723 | } |
| 724 | |
| 725 | static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) |
| 726 | { |
| 727 | CPUState *cs = CPU(dev); |
| 728 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); |
| 729 | Error *local_err = NULL; |
| 730 | |
| 731 | cpu_common_realize(cs, &local_err); |
| 732 | if (local_err != NULL) { |
| 733 | error_propagate(errp, local_err); |
| 734 | return; |
| 735 | } |
| 736 | |
| 737 | loongarch_cpu_register_gdb_regs_for_features(cs); |
| 738 | |
| 739 | qemu_init_vcpu(cs); |
| 740 | cpu_reset(cs); |
| 741 | loongarch_la464_init_csr(dev); |
| 742 | |
| 743 | lacc->parent_realize(dev, errp); |
| 744 | } |
| 745 | |
| 746 | static void loongarch_cpu_unrealizefn(DeviceState *dev) |
| 747 | { |
| 748 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); |
| 749 | |
| 750 | #ifndef CONFIG_USER_ONLY |
| 751 | cpu_remove_sync(CPU(dev)); |
| 752 | #endif |
| 753 | |
| 754 | lacc->parent_unrealize(dev); |
| 755 | } |
| 756 | |
| 757 | static void loongarch_cpu_init(Object *obj) |
| 758 | { |
| 759 | #ifndef CONFIG_USER_ONLY |
| 760 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); |
| 761 | |
| 762 | qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); |
| 763 | #ifdef CONFIG_TCG |
| 764 | timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, |
| 765 | &loongarch_constant_timer_cb, cpu); |
| 766 | #endif |
| 767 | #endif |
| 768 | } |
| 769 | |
| 770 | static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) |
| 771 | { |
| 772 | ObjectClass *oc; |
| 773 | |
| 774 | oc = object_class_by_name(cpu_model); |
| 775 | if (!oc) { |
| 776 | g_autofree char *typename |
| 777 | = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); |
| 778 | oc = object_class_by_name(typename); |
| 779 | } |
| 780 | |
| 781 | return oc; |
| 782 | } |
| 783 | |
| 784 | static void loongarch_cpu_dump_csr(CPUState *cs, FILE *f) |
| 785 | { |
| 786 | #ifndef CONFIG_USER_ONLY |
| 787 | CPULoongArchState *env = cpu_env(cs); |
| 788 | const CSRInfo *csr_info; |
| 789 | int64_t *addr; |
| 790 | int i, j, len, col = 0; |
| 791 | |
| 792 | qemu_fprintf(f, "\n"); |
| 793 | |
| 794 | /* Dump all generic CSR register */ |
| 795 | for (i = 0; i < LOONGARCH_CSR_DBG; i++) { |
| 796 | csr_info = get_csr(i); |
| 797 | if (!csr_info || (csr_info->flags & CSRFL_UNUSED)) { |
| 798 | if (i == (col + 3)) { |
| 799 | qemu_fprintf(f, "\n"); |
| 800 | } |
| 801 | |
| 802 | continue; |
| 803 | } |
| 804 | |
| 805 | if ((i > (col + 3)) || (i == col)) { |
| 806 | col = i & ~3; |
| 807 | qemu_fprintf(f, " CSR%03d:", col); |
| 808 | } |
| 809 | |
| 810 | addr = (void *)env + get_csr_offset(csr_info, 0); |
| 811 | qemu_fprintf(f, " %s ", csr_info->name); |
| 812 | len = strlen(csr_info->name); |
| 813 | for (; len < 6; len++) { |
| 814 | qemu_fprintf(f, " "); |
| 815 | } |
| 816 | |
| 817 | qemu_fprintf(f, "%" PRIx64, *addr); |
| 818 | j = find_last_bit((void *)addr, BITS_PER_LONG) & (BITS_PER_LONG - 1); |
| 819 | len += j / 4 + 1; |
| 820 | for (; len < 22; len++) { |
| 821 | qemu_fprintf(f, " "); |
| 822 | } |
| 823 | |
| 824 | if (i == (col + 3)) { |
| 825 | qemu_fprintf(f, "\n"); |
| 826 | } |
| 827 | } |
| 828 | qemu_fprintf(f, "\n"); |
| 829 | #endif |
| 830 | } |
| 831 | |
| 832 | static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) |
| 833 | { |
| 834 | CPULoongArchState *env = cpu_env(cs); |
| 835 | int i; |
| 836 | |
| 837 | qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); |
| 838 | qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0); |
| 839 | |
| 840 | /* gpr */ |
| 841 | for (i = 0; i < 32; i++) { |
| 842 | if ((i & 3) == 0) { |
| 843 | qemu_fprintf(f, " GPR%02d:", i); |
| 844 | } |
| 845 | qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]); |
| 846 | if ((i & 3) == 3) { |
| 847 | qemu_fprintf(f, "\n"); |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | /* csr */ |
| 852 | loongarch_cpu_dump_csr(cs, f); |
| 853 | |
| 854 | /* fpr */ |
| 855 | if (flags & CPU_DUMP_FPU) { |
| 856 | for (i = 0; i < 32; i++) { |
| 857 | qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i].vreg.D(0)); |
| 858 | if ((i & 3) == 3) { |
| 859 | qemu_fprintf(f, "\n"); |
| 860 | } |
| 861 | } |
| 862 | } |
| 863 | } |
| 864 | |
| 865 | #ifndef CONFIG_USER_ONLY |
| 866 | #include "hw/core/sysemu-cpu-ops.h" |
| 867 | |
| 868 | static const struct SysemuCPUOps loongarch_sysemu_ops = { |
| 869 | .has_work = loongarch_cpu_has_work, |
| 870 | .write_elf64_note = loongarch_cpu_write_elf64_note, |
| 871 | .get_phys_addr_debug = loongarch_cpu_get_phys_addr_debug, |
| 872 | }; |
| 873 | |
| 874 | static int64_t loongarch_cpu_get_arch_id(CPUState *cs) |
| 875 | { |
| 876 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); |
| 877 | |
| 878 | return cpu->phy_id; |
| 879 | } |
| 880 | #endif |
| 881 | |
| 882 | static const Property loongarch_cpu_properties[] = { |
| 883 | DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0), |
| 884 | DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0), |
| 885 | DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0), |
| 886 | DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), |
| 887 | }; |
| 888 | |
| 889 | static void loongarch_cpu_class_init(ObjectClass *c, const void *data) |
| 890 | { |
| 891 | LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); |
| 892 | CPUClass *cc = CPU_CLASS(c); |
| 893 | DeviceClass *dc = DEVICE_CLASS(c); |
| 894 | ResettableClass *rc = RESETTABLE_CLASS(c); |
| 895 | |
| 896 | device_class_set_props(dc, loongarch_cpu_properties); |
| 897 | device_class_set_parent_realize(dc, loongarch_cpu_realizefn, |
| 898 | &lacc->parent_realize); |
| 899 | device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn, |
| 900 | &lacc->parent_unrealize); |
| 901 | resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, |
| 902 | &lacc->parent_phases); |
| 903 | |
| 904 | cc->class_by_name = loongarch_cpu_class_by_name; |
| 905 | cc->dump_state = loongarch_cpu_dump_state; |
| 906 | cc->set_pc = loongarch_cpu_set_pc; |
| 907 | cc->get_pc = loongarch_cpu_get_pc; |
| 908 | #ifndef CONFIG_USER_ONLY |
| 909 | cc->get_arch_id = loongarch_cpu_get_arch_id; |
| 910 | dc->vmsd = &vmstate_loongarch_cpu; |
| 911 | cc->sysemu_ops = &loongarch_sysemu_ops; |
| 912 | #endif |
| 913 | cc->disas_set_info = loongarch_cpu_disas_set_info; |
| 914 | cc->gdb_read_register = loongarch_cpu_gdb_read_register; |
| 915 | cc->gdb_write_register = loongarch_cpu_gdb_write_register; |
| 916 | cc->gdb_stop_before_watchpoint = true; |
| 917 | |
| 918 | #ifdef CONFIG_TCG |
| 919 | cc->tcg_ops = &loongarch_tcg_ops; |
| 920 | #endif |
| 921 | dc->user_creatable = true; |
| 922 | } |
| 923 | |
| 924 | static const gchar *loongarch32_gdb_arch_name(CPUState *cs) |
| 925 | { |
| 926 | return "loongarch32"; |
| 927 | } |
| 928 | |
| 929 | static void loongarch32_cpu_class_init(ObjectClass *c, const void *data) |
| 930 | { |
| 931 | CPUClass *cc = CPU_CLASS(c); |
| 932 | |
| 933 | cc->gdb_core_xml_file = "loongarch-base32.xml"; |
| 934 | cc->gdb_arch_name = loongarch32_gdb_arch_name; |
| 935 | } |
| 936 | |
| 937 | static const gchar *loongarch64_gdb_arch_name(CPUState *cs) |
| 938 | { |
| 939 | return "loongarch64"; |
| 940 | } |
| 941 | |
| 942 | static void loongarch64_cpu_class_init(ObjectClass *c, const void *data) |
| 943 | { |
| 944 | CPUClass *cc = CPU_CLASS(c); |
| 945 | |
| 946 | cc->gdb_core_xml_file = "loongarch-base64.xml"; |
| 947 | cc->gdb_arch_name = loongarch64_gdb_arch_name; |
| 948 | } |
| 949 | |
| 950 | #define DEFINE_LOONGARCH_CPU_TYPE(size, model, initfn) \ |
| 951 | { \ |
| 952 | .parent = TYPE_LOONGARCH##size##_CPU, \ |
| 953 | .instance_init = initfn, \ |
| 954 | .name = LOONGARCH_CPU_TYPE_NAME(model), \ |
| 955 | } |
| 956 | |
| 957 | static const TypeInfo loongarch_cpu_type_infos[] = { |
| 958 | { |
| 959 | .name = TYPE_LOONGARCH_CPU, |
| 960 | .parent = TYPE_CPU, |
| 961 | .instance_size = sizeof(LoongArchCPU), |
| 962 | .instance_align = __alignof(LoongArchCPU), |
| 963 | .instance_init = loongarch_cpu_init, |
| 964 | |
| 965 | .abstract = true, |
| 966 | .class_size = sizeof(LoongArchCPUClass), |
| 967 | .class_init = loongarch_cpu_class_init, |
| 968 | }, |
| 969 | { |
| 970 | .name = TYPE_LOONGARCH32_CPU, |
| 971 | .parent = TYPE_LOONGARCH_CPU, |
| 972 | |
| 973 | .abstract = true, |
| 974 | .class_init = loongarch32_cpu_class_init, |
| 975 | }, |
| 976 | { |
| 977 | .name = TYPE_LOONGARCH64_CPU, |
| 978 | .parent = TYPE_LOONGARCH_CPU, |
| 979 | |
| 980 | .abstract = true, |
| 981 | .class_init = loongarch64_cpu_class_init, |
| 982 | }, |
| 983 | DEFINE_LOONGARCH_CPU_TYPE(64, "la464", loongarch_la464_initfn), |
| 984 | DEFINE_LOONGARCH_CPU_TYPE(32, "la132", loongarch_la132_initfn), |
| 985 | DEFINE_LOONGARCH_CPU_TYPE(64, "max", loongarch_max_initfn), |
| 986 | #if defined(CONFIG_KVM) |
| 987 | DEFINE_LOONGARCH_CPU_TYPE(64, "host", loongarch_host_initfn), |
| 988 | #endif |
| 989 | }; |
| 990 | |
| 991 | DEFINE_TYPES(loongarch_cpu_type_infos) |