github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/executor/common_kvm_arm64_syzos.h (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 #ifndef EXECUTOR_COMMON_KVM_ARM64_SYZOS_H 5 #define EXECUTOR_COMMON_KVM_ARM64_SYZOS_H 6 7 // This file provides guest code running inside the ARM64 KVM. 8 9 #include "common_kvm_syzos.h" 10 #include "kvm.h" 11 #include <linux/kvm.h> 12 #include <stdbool.h> 13 14 // Compilers will eagerly try to transform the switch statement in guest_main() 15 // into a jump table, unless the cases are sparse enough. 16 // We use prime numbers multiplied by 10 to prevent this behavior. 17 // Remember these constants must match those in sys/linux/dev_kvm_arm64.txt. 18 typedef enum { 19 SYZOS_API_UEXIT = 0, 20 SYZOS_API_CODE = 10, 21 SYZOS_API_MSR = 20, 22 SYZOS_API_SMC = 30, 23 SYZOS_API_HVC = 50, 24 SYZOS_API_IRQ_SETUP = 70, 25 SYZOS_API_MEMWRITE = 110, 26 SYZOS_API_ITS_SETUP = 130, 27 SYZOS_API_ITS_SEND_CMD = 170, 28 SYZOS_API_MRS = 190, 29 SYZOS_API_ERET = 230, 30 SYZOS_API_SVC = 290, 31 SYZOS_API_STOP, // Must be the last one 32 } syzos_api_id; 33 34 struct api_call_header { 35 uint64 call; 36 uint64 size; 37 }; 38 39 struct api_call_uexit { 40 struct api_call_header header; 41 uint64 exit_code; 42 }; 43 44 struct api_call_1 { 45 struct api_call_header header; 46 uint64 arg; 47 }; 48 49 struct api_call_2 { 50 struct api_call_header header; 51 uint64 args[2]; 52 }; 53 54 struct api_call_3 { 55 struct api_call_header header; 56 uint64 args[3]; 57 }; 58 59 struct api_call_code { 60 struct api_call_header header; 61 uint32 insns[]; 62 }; 63 64 struct api_call_smccc { 65 struct api_call_header header; 66 uint32 func_id; 67 uint64 params[5]; 68 }; 69 70 struct api_call_irq_setup { 71 struct api_call_header header; 72 uint32 nr_cpus; 73 uint32 nr_spis; 74 }; 75 76 struct api_call_memwrite { 77 struct api_call_header header; 78 uint64 base_addr; 79 uint64 offset; 80 uint64 value; 81 uint64 len; 82 }; 83 84 struct api_call_its_send_cmd { 85 struct api_call_header header; 86 uint8 type; 87 uint8 valid; 88 uint32 cpuid; 89 uint32 devid; 90 uint32 eventid; 91 uint32 intid; 92 uint32 cpuid2; 93 }; 94 95 GUEST_CODE static void guest_uexit(uint64 exit_code); 96 GUEST_CODE static void guest_execute_code(uint32* insns, uint64 size); 97 GUEST_CODE static void guest_handle_mrs(uint64 reg); 98 GUEST_CODE static void guest_handle_msr(uint64 reg, uint64 val); 99 GUEST_CODE static void guest_handle_smc(struct api_call_smccc* cmd); 100 GUEST_CODE static void guest_handle_hvc(struct api_call_smccc* cmd); 101 GUEST_CODE static void guest_handle_svc(struct api_call_smccc* cmd); 102 GUEST_CODE static void guest_handle_eret(uint64 unused); 103 GUEST_CODE static void guest_handle_irq_setup(struct api_call_irq_setup* cmd); 104 GUEST_CODE static void guest_handle_memwrite(struct api_call_memwrite* cmd); 105 GUEST_CODE static void guest_handle_its_setup(struct api_call_3* cmd); 106 GUEST_CODE static void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd); 107 108 typedef enum { 109 UEXIT_END = (uint64)-1, 110 UEXIT_IRQ = (uint64)-2, 111 UEXIT_ASSERT = (uint64)-3, 112 } uexit_code; 113 114 // Main guest function that performs necessary setup and passes the control to the user-provided 115 // payload. 116 __attribute__((used)) 117 GUEST_CODE static void 118 guest_main(uint64 size, uint64 cpu) 119 { 120 uint64 addr = ARM64_ADDR_USER_CODE + cpu * 0x1000; 121 122 while (size >= sizeof(struct api_call_header)) { 123 struct api_call_header* cmd = (struct api_call_header*)addr; 124 if (cmd->call >= SYZOS_API_STOP) 125 return; 126 if (cmd->size > size) 127 return; 128 switch (cmd->call) { 129 case SYZOS_API_UEXIT: { 130 struct api_call_uexit* ucmd = (struct api_call_uexit*)cmd; 131 guest_uexit(ucmd->exit_code); 132 break; 133 } 134 case SYZOS_API_CODE: { 135 struct api_call_code* ccmd = (struct api_call_code*)cmd; 136 guest_execute_code(ccmd->insns, cmd->size - sizeof(struct api_call_header)); 137 break; 138 } 139 case SYZOS_API_MRS: { 140 struct api_call_1* ccmd = (struct api_call_1*)cmd; 141 guest_handle_mrs(ccmd->arg); 142 break; 143 } 144 case SYZOS_API_MSR: { 145 struct api_call_2* ccmd = (struct api_call_2*)cmd; 146 guest_handle_msr(ccmd->args[0], ccmd->args[1]); 147 break; 148 } 149 case SYZOS_API_ERET: { 150 struct api_call_1* ccmd = (struct api_call_1*)cmd; 151 guest_handle_eret(ccmd->arg); 152 break; 153 } 154 case SYZOS_API_SMC: { 155 guest_handle_smc((struct api_call_smccc*)cmd); 156 break; 157 } 158 case SYZOS_API_HVC: { 159 guest_handle_hvc((struct api_call_smccc*)cmd); 160 break; 161 } 162 case SYZOS_API_SVC: { 163 guest_handle_svc((struct api_call_smccc*)cmd); 164 break; 165 } 166 case SYZOS_API_IRQ_SETUP: { 167 guest_handle_irq_setup((struct api_call_irq_setup*)cmd); 168 break; 169 } 170 case SYZOS_API_MEMWRITE: { 171 guest_handle_memwrite((struct api_call_memwrite*)cmd); 172 break; 173 } 174 case SYZOS_API_ITS_SETUP: { 175 guest_handle_its_setup((struct api_call_3*)cmd); 176 break; 177 } 178 case SYZOS_API_ITS_SEND_CMD: { 179 guest_handle_its_send_cmd((struct api_call_its_send_cmd*)cmd); 180 break; 181 } 182 } 183 addr += cmd->size; 184 size -= cmd->size; 185 }; 186 guest_uexit((uint64)-1); 187 } 188 189 // Some ARM chips use 128-byte cache lines. Pick 256 to be on the safe side. 190 #define MAX_CACHE_LINE_SIZE 256 191 192 GUEST_CODE static noinline void 193 flush_cache_range(void* addr, uint64 size) 194 { 195 uint64 start = (uint64)addr; 196 uint64 end = start + size; 197 198 // For self-modifying code, we must clean the D-cache and invalidate the 199 // I-cache for the memory range that was modified. This is the sequence 200 // mandated by the ARMv8-A architecture. 201 202 // 1. Clean D-cache over the whole range to the Point of Unification. 203 for (uint64 i = start; i < end; i += MAX_CACHE_LINE_SIZE) 204 asm volatile("dc cvau, %[addr]" : : [addr] "r"(i) : "memory"); 205 // 2. Wait for the D-cache clean to complete. 206 asm volatile("dsb sy" : : : "memory"); 207 208 // 3. Invalidate I-cache over the whole range. 209 for (uint64 i = start; i < end; i += MAX_CACHE_LINE_SIZE) 210 asm volatile("ic ivau, %[addr]" : : [addr] "r"(i) : "memory"); 211 // 4. Wait for the I-cache invalidate to complete. 212 asm volatile("dsb sy" : : : "memory"); 213 214 // 5. Flush pipeline to force re-fetch of new instruction. 215 asm volatile("isb" : : : "memory"); 216 } 217 218 GUEST_CODE static noinline void guest_execute_code(uint32* insns, uint64 size) 219 { 220 flush_cache_range(insns, size); 221 volatile void (*fn)() = (volatile void (*)())insns; 222 fn(); 223 } 224 225 // Perform a userspace exit that can be handled by the host. 226 // The host returns from ioctl(KVM_RUN) with kvm_run.exit_reason=KVM_EXIT_MMIO, 227 // and can handle the call depending on the data passed as exit code. 228 GUEST_CODE static noinline void guest_uexit(uint64 exit_code) 229 { 230 volatile uint64* ptr = (volatile uint64*)ARM64_ADDR_UEXIT; 231 *ptr = exit_code; 232 } 233 234 #define MSR_REG_OPCODE 0xd5100000 235 #define MRS_REG_OPCODE 0xd5300000 236 237 // Generate an `MSR register, x0` instruction based on the register ID. 238 // Luckily for us, the five operands, Op0, Op1, CRn, CRm, and Op2 are laid out sequentially in 239 // both the register ID and the MSR instruction encoding (see 240 // https://developer.arm.com/documentation/ddi0602/2024-06/Base-Instructions/MSR--register---Move-general-purpose-register-to-System-register-), 241 // so we can just extract the lower 16 bits and put them into the opcode. 242 GUEST_CODE static uint32 reg_to_msr(uint64 reg) 243 { 244 return MSR_REG_OPCODE | ((reg & 0xffff) << 5); 245 } 246 247 // Generate an `MRS register, x0` instruction based on the register ID. 248 GUEST_CODE static uint32 reg_to_mrs(uint64 reg) 249 { 250 return MRS_REG_OPCODE | ((reg & 0xffff) << 5); 251 } 252 253 // Host sets TPIDR_EL1 to contain the virtual CPU id. 254 GUEST_CODE static uint32 get_cpu_id() 255 { 256 uint64 val = 0; // Suppress lint warning. 257 asm volatile("mrs %0, tpidr_el1" 258 : "=r"(val)); 259 return (uint32)val; 260 } 261 262 // Read the value from a system register using an MRS instruction. 263 GUEST_CODE static noinline void 264 guest_handle_mrs(uint64 reg) 265 { 266 uint32 mrs = reg_to_mrs(reg); 267 uint32 cpu_id = get_cpu_id(); 268 // Make sure CPUs use different cache lines for scratch code. 269 uint32* insn = (uint32*)((uint64)ARM64_ADDR_SCRATCH_CODE + cpu_id * MAX_CACHE_LINE_SIZE); 270 insn[0] = mrs; 271 insn[1] = 0xd65f03c0; // RET 272 flush_cache_range(insn, 8); 273 // Make a call to the generated MSR instruction and clobber x0. 274 asm("blr %[pc]\n" 275 : 276 : [pc] "r"(insn) 277 : "x0", "x30"); 278 } 279 280 GUEST_CODE static noinline void 281 guest_handle_eret(uint64 unused) 282 { 283 asm("eret\n" : : : "memory"); 284 } 285 286 // Write value to a system register using an MSR instruction. 287 // The word "MSR" here has nothing to do with the x86 MSR registers. 288 GUEST_CODE static noinline void 289 guest_handle_msr(uint64 reg, uint64 val) 290 { 291 uint32 msr = reg_to_msr(reg); 292 uint32 cpu_id = get_cpu_id(); 293 // Make sure CPUs use different cache lines for scratch code. 294 uint32* insn = (uint32*)((uint64)ARM64_ADDR_SCRATCH_CODE + cpu_id * MAX_CACHE_LINE_SIZE); 295 insn[0] = msr; 296 insn[1] = 0xd65f03c0; // RET 297 flush_cache_range(insn, 8); 298 // Put `val` into x0 and make a call to the generated MSR instruction. 299 asm("mov x0, %[val]\nblr %[pc]\n" 300 : 301 : [val] "r"(val), [pc] "r"(insn) 302 : "x0", "x30", "memory"); 303 } 304 305 // See "SMC Calling Convention", https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6 306 GUEST_CODE static noinline void guest_handle_smc(struct api_call_smccc* cmd) 307 { 308 asm volatile( 309 "mov x0, %[func_id]\n" 310 "mov x1, %[arg1]\n" 311 "mov x2, %[arg2]\n" 312 "mov x3, %[arg3]\n" 313 "mov x4, %[arg4]\n" 314 "mov x5, %[arg5]\n" 315 // TODO(glider): it could be interesting to pass other immediate values here, although 316 // they are ignored as per the calling convention. 317 "smc #0\n" 318 : // Ignore the outputs for now 319 : [func_id] "r"((uint64)cmd->func_id), 320 [arg1] "r"(cmd->params[0]), [arg2] "r"(cmd->params[1]), 321 [arg3] "r"(cmd->params[2]), [arg4] "r"(cmd->params[3]), 322 [arg5] "r"(cmd->params[4]) 323 : "x0", "x1", "x2", "x3", "x4", "x5", 324 // These registers are not used above, but may be clobbered by the SMC call. 325 "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", 326 "memory"); 327 } 328 329 GUEST_CODE static noinline void guest_handle_hvc(struct api_call_smccc* cmd) 330 { 331 asm volatile( 332 "mov x0, %[func_id]\n" 333 "mov x1, %[arg1]\n" 334 "mov x2, %[arg2]\n" 335 "mov x3, %[arg3]\n" 336 "mov x4, %[arg4]\n" 337 "mov x5, %[arg5]\n" 338 // TODO(glider): nonzero immediate values are designated for use by hypervisor vendors. 339 "hvc #0\n" 340 : // Ignore the outputs for now 341 : [func_id] "r"((uint64)cmd->func_id), 342 [arg1] "r"(cmd->params[0]), [arg2] "r"(cmd->params[1]), 343 [arg3] "r"(cmd->params[2]), [arg4] "r"(cmd->params[3]), 344 [arg5] "r"(cmd->params[4]) 345 : "x0", "x1", "x2", "x3", "x4", "x5", 346 // These registers are not used above, but may be clobbered by the HVC call. 347 "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", 348 "memory"); 349 } 350 351 GUEST_CODE static noinline void guest_handle_svc(struct api_call_smccc* cmd) 352 { 353 asm volatile( 354 "mov x0, %[func_id]\n" 355 "mov x1, %[arg1]\n" 356 "mov x2, %[arg2]\n" 357 "mov x3, %[arg3]\n" 358 "mov x4, %[arg4]\n" 359 "mov x5, %[arg5]\n" 360 // TODO(glider): nonzero immediate values are designated for use by hypervisor vendors. 361 "svc #0\n" 362 : // Ignore the outputs for now 363 : [func_id] "r"((uint64)cmd->func_id), 364 [arg1] "r"(cmd->params[0]), [arg2] "r"(cmd->params[1]), 365 [arg3] "r"(cmd->params[2]), [arg4] "r"(cmd->params[3]), 366 [arg5] "r"(cmd->params[4]) 367 : "x0", "x1", "x2", "x3", "x4", "x5", 368 // These registers are not used above, but may be clobbered by the HVC call. 369 "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", 370 "memory"); 371 } 372 373 // VGICv3 setup and IRQ handling code below. 374 // This code is based on the "Arm Generic Interrupt Controller (GIC) Architecture Specification. 375 // GIC architecture version 3 and version 4" doc (https://developer.arm.com/documentation/ihi0069/latest/) 376 // and KVM selftests in the Linux kernel. 377 378 // GICv3 Distributor registers. 379 #define GICD_CTLR 0x0000 380 #define GICD_IGROUPR 0x0080 381 #define GICD_ISENABLER 0x0100 382 #define GICD_ICENABLER 0x0180 383 #define GICD_ICACTIVER 0x0380 384 #define GICD_IPRIORITYR 0x0400 385 386 #define GICD_INT_DEF_PRI_X4 0xa0a0a0a0 387 #define GICD_CTLR_ARE_NS (1U << 4) 388 #define GICD_CTLR_ENABLE_G1A (1U << 1) 389 #define GICD_CTLR_ENABLE_G1 (1U << 0) 390 391 #define GICD_CTLR_RWP (1U << 31) 392 393 // GICv3 Redistributor registers. 394 #define GICR_CTLR GICD_CTLR 395 #define GICR_WAKER 0x0014 396 #define GICR_PROPBASER 0x0070 397 #define GICR_PENDBASER 0x0078 398 399 #define GICR_CTLR_ENABLE_LPIS (1UL << 0) 400 #define GICR_CTLR_RWP (1UL << 3) 401 402 #define GICR_IGROUPR0 GICD_IGROUPR 403 #define GICR_ICENABLER0 GICD_ICENABLER 404 #define GICR_ICACTIVER0 GICD_ICACTIVER 405 #define GICR_IPRIORITYR0 GICD_IPRIORITYR 406 407 #define ICC_SRE_EL1_SRE (1U << 0) 408 #define ICC_PMR_DEF_PRIO 0xff 409 #define ICC_IGRPEN1_EL1_ENABLE (1U << 0) 410 411 #define GICR_WAKER_ProcessorSleep (1U << 1) 412 #define GICR_WAKER_ChildrenAsleep (1U << 2) 413 414 // When building with tools/syz-old-env, GCC doesn't recognize the names of ICC registers. 415 // Replace them with generic S3_* names until we get a newer toolchain. 416 #define ICC_SRE_EL1 "S3_0_C12_C12_5" 417 #define ICC_PMR_EL1 "S3_0_C4_C6_0" 418 #define ICC_IGRPEN1_EL1 "S3_0_C12_C12_7" 419 #define ICC_IAR0_EL1 "S3_0_C12_C8_0" 420 #define ICC_IAR1_EL1 "S3_0_C12_C12_0" 421 #define ICC_EOIR0_EL1 "S3_0_C12_C8_1" 422 #define ICC_EOIR1_EL1 "S3_0_C12_C12_1" 423 #define ICC_DIR_EL1 "S3_0_C12_C11_1" 424 425 GUEST_CODE static __always_inline void __raw_writel(uint32 val, uint64 addr) 426 { 427 asm volatile("str %w0, [%1]" 428 : 429 : "rZ"(val), "r"(addr)); 430 } 431 432 GUEST_CODE static __always_inline void __raw_writeq(uint64 val, uint64 addr) 433 { 434 asm volatile("str %x0, [%1]" 435 : 436 : "rZ"(val), "r"(addr)); 437 } 438 439 GUEST_CODE static __always_inline uint32 __raw_readl(uint64 addr) 440 { 441 uint32 val; 442 asm volatile("ldr %w0, [%1]" 443 : "=r"(val) 444 : "r"(addr)); 445 return val; 446 } 447 448 GUEST_CODE static __always_inline uint64 __raw_readq(uint64 addr) 449 { 450 uint64 val; 451 asm volatile("ldr %x0, [%1]" 452 : "=r"(val) 453 : "r"(addr)); 454 return val; 455 } 456 457 #define dmb() asm volatile("dmb sy" \ 458 : \ 459 : \ 460 : "memory") 461 462 #define writel(v, c) ({ dmb(); __raw_writel(v, c); }) 463 #define readl(c) ({ uint32 __v = __raw_readl(c); dmb(); __v; }) 464 #define writeq(v, c) ({ dmb(); __raw_writeq(v, c); }) 465 #define readq(c) ({ uint64 __v = __raw_readq(c); dmb(); __v; }) 466 467 // TODO(glider): may want to return extra data to the host. 468 #define GUEST_ASSERT(val) \ 469 do { \ 470 if (!(val)) \ 471 guest_uexit(UEXIT_ASSERT); \ 472 } while (0) 473 474 // Helper to implement guest_udelay(). 475 GUEST_CODE static uint64 read_cntvct(void) 476 { 477 uint64 val; 478 asm volatile("mrs %0, cntvct_el0" 479 : "=r"(val)); 480 return val; 481 } 482 483 // Wait for roughly @us microseconds. 484 GUEST_CODE static void guest_udelay(uint32 us) 485 { 486 uint64 ticks_per_second = 0; 487 // Have to read the frequency every time, since we don't have static storage. 488 asm volatile("mrs %0, cntfrq_el0" 489 : "=r"(ticks_per_second)); 490 491 uint64 start = read_cntvct(); 492 493 // Target counter value for the desired delay. 494 uint64 target = start + (us * ticks_per_second) / 1000000; 495 496 while (read_cntvct() < target) { 497 } 498 } 499 500 // Spin for at most one second as long as the register value has bits from mask. 501 GUEST_CODE static void spin_while_readl(uint64 reg, uint32 mask) 502 { 503 volatile unsigned int count = 100000; 504 while (readl(reg) & mask) { 505 GUEST_ASSERT(count--); 506 guest_udelay(10); 507 } 508 } 509 510 // Wait for the Register Write Pending bit on GICD_CTLR. 511 GUEST_CODE static void gicd_wait_for_rwp() 512 { 513 spin_while_readl(ARM64_ADDR_GICD_BASE + GICD_CTLR, GICD_CTLR_RWP); 514 } 515 516 GUEST_CODE static uint64 gicr_base_cpu(uint32 cpu) 517 { 518 return ARM64_ADDR_GICR_BASE + cpu * SZ_64K * 2; 519 } 520 521 GUEST_CODE static uint64 sgi_base_cpu(uint32 cpu) 522 { 523 return gicr_base_cpu(cpu) + SZ_64K; 524 } 525 526 // Wait for the Register Write Pending bit on GICR_CTLR. 527 GUEST_CODE static void gicr_wait_for_rwp(uint32 cpu) 528 { 529 spin_while_readl(gicr_base_cpu(cpu) + GICR_CTLR, GICR_CTLR_RWP); 530 } 531 532 // Set up the distributor part. 533 GUEST_CODE static void gicv3_dist_init(int nr_spis) 534 { 535 // Disable the distributor. 536 writel(0, ARM64_ADDR_GICD_BASE + GICD_CTLR); 537 gicd_wait_for_rwp(); 538 539 // Mark all the SPI interrupts as non-secure Group-1. Also, deactivate and disable them. 540 for (int i = 32; i < nr_spis + 32; i += 32) { 541 writel(~0, ARM64_ADDR_GICD_BASE + GICD_IGROUPR + i / 8); 542 writel(~0, ARM64_ADDR_GICD_BASE + GICD_ICACTIVER + i / 8); 543 writel(~0, ARM64_ADDR_GICD_BASE + GICD_ICENABLER + i / 8); 544 } 545 546 // Set a default priority for all the SPIs. 547 for (int i = 32; i < nr_spis + 32; i += 4) { 548 writel(GICD_INT_DEF_PRI_X4, 549 ARM64_ADDR_GICD_BASE + GICD_IPRIORITYR + i); 550 } 551 552 // Wait for the settings to sync-in. 553 gicd_wait_for_rwp(); 554 555 // Finally, enable the distributor globally with Affinity Routing Enable, Non-Secure. 556 writel(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1, ARM64_ADDR_GICD_BASE + GICD_CTLR); 557 gicd_wait_for_rwp(); 558 } 559 560 // https://developer.arm.com/documentation/198123/0302/Configuring-the-Arm-GIC 561 GUEST_CODE static void gicv3_enable_redist(uint32 cpu) 562 { 563 uint64 redist_base_cpu = gicr_base_cpu(cpu); 564 uint32 val = readl(redist_base_cpu + GICR_WAKER); 565 566 val &= ~GICR_WAKER_ProcessorSleep; 567 writel(val, ARM64_ADDR_GICR_BASE + GICR_WAKER); 568 // Wait until the processor is 'active'. 569 spin_while_readl(ARM64_ADDR_GICR_BASE + GICR_WAKER, GICR_WAKER_ChildrenAsleep); 570 } 571 572 GUEST_CODE static void gicv3_cpu_init(uint32 cpu) 573 { 574 uint64 sgi_base = sgi_base_cpu(cpu); 575 576 // It is important that software performs these steps before configuring 577 // the CPU interface, otherwise behavior can be UNPREDICTABLE. 578 gicv3_enable_redist(cpu); 579 580 // Mark all the SGI and PPI interrupts as non-secure Group-1. Also, deactivate and disable them. 581 writel(~0, sgi_base + GICR_IGROUPR0); 582 writel(~0, sgi_base + GICR_ICACTIVER0); 583 writel(~0, sgi_base + GICR_ICENABLER0); 584 585 // Set a default priority for all the SGIs and PPIs. 586 for (int i = 0; i < 32; i += 4) { 587 writel(GICD_INT_DEF_PRI_X4, 588 sgi_base + GICR_IPRIORITYR0 + i); 589 } 590 591 gicr_wait_for_rwp(cpu); 592 593 // Enable the GIC system register (ICC_*) access. 594 uint64 icc_sre_el1 = 0; 595 asm volatile("mrs %0, " ICC_SRE_EL1 596 : "=r"(icc_sre_el1)); 597 icc_sre_el1 |= ICC_SRE_EL1_SRE; 598 asm volatile("msr " ICC_SRE_EL1 ", %0" 599 : 600 : "r"(icc_sre_el1)); 601 602 // Set a default priority threshold. 603 uint64 value = ICC_PMR_DEF_PRIO; 604 asm volatile("msr " ICC_PMR_EL1 ", %0" 605 : 606 : "r"(value)); 607 608 // Enable non-secure Group-1 interrupts. 609 value = ICC_IGRPEN1_EL1_ENABLE; 610 asm volatile("msr " ICC_IGRPEN1_EL1 ", %0" 611 : 612 : "r"(value)); 613 } 614 615 // GICv3 reserves interrupts 32-1019 for SPI. 616 #define VGICV3_MIN_SPI 32 617 #define VGICV3_MAX_SPI 1019 618 619 // https://developer.arm.com/documentation/ihi0048/b/Programmers--Model/Distributor-register-descriptions/Interrupt-Set-Enable-Registers--GICD-ISENABLERn 620 GUEST_CODE static void gicv3_irq_enable(uint32 intid) 621 { 622 uint32 cpu = get_cpu_id(); 623 624 writel(1 << (intid % 32), ARM64_ADDR_GICD_BASE + GICD_ISENABLER + (intid / 32) * 4); 625 if ((intid >= VGICV3_MIN_SPI) && (intid <= VGICV3_MAX_SPI)) 626 gicd_wait_for_rwp(); 627 else 628 gicr_wait_for_rwp(cpu); 629 } 630 631 GUEST_CODE static noinline void guest_handle_irq_setup(struct api_call_irq_setup* cmd) 632 { 633 int nr_spis = cmd->nr_spis; 634 if ((nr_spis > VGICV3_MAX_SPI - VGICV3_MIN_SPI) || (nr_spis < 0)) 635 nr_spis = 32; 636 int nr_cpus = cmd->nr_cpus; 637 638 gicv3_dist_init(nr_spis); 639 for (int i = 0; i < nr_cpus; i++) 640 gicv3_cpu_init(i); 641 for (int i = 0; i < nr_spis; i++) 642 gicv3_irq_enable(VGICV3_MIN_SPI + i); 643 // Set up the vector table. 644 asm(R"( 645 adr x1, guest_vector_table 646 msr vbar_el1, x1 647 msr daifclr, #0b1111 648 )" 649 : 650 : 651 : "x1"); 652 } 653 654 GUEST_CODE static noinline void guest_handle_memwrite(struct api_call_memwrite* cmd) 655 { 656 uint64 dest = cmd->base_addr + cmd->offset; 657 switch (cmd->len) { 658 case 1: { 659 volatile uint8* p = (uint8*)dest; 660 *p = (uint8)cmd->value; 661 break; 662 } 663 664 case 2: { 665 volatile uint16* p = (uint16*)dest; 666 *p = (uint16)cmd->value; 667 break; 668 } 669 case 4: { 670 volatile uint32* p = (uint32*)dest; 671 *p = (uint32)cmd->value; 672 break; 673 } 674 case 8: 675 default: { 676 volatile uint64* p = (uint64*)dest; 677 *p = (uint64)cmd->value; 678 break; 679 } 680 } 681 } 682 683 GUEST_CODE static void guest_prepare_its(int nr_cpus, int nr_devices, int nr_events); 684 685 GUEST_CODE static noinline void guest_handle_its_setup(struct api_call_3* cmd) 686 { 687 guest_prepare_its(cmd->args[0], cmd->args[1], cmd->args[2]); 688 } 689 690 // Registers saved by one_irq_handler() and received by guest_irq_handler(). 691 struct ex_regs { 692 uint64 regs[31]; 693 uint64 sp; 694 uint64 pc; 695 uint64 pstate; 696 }; 697 698 // Placeholder function to declare one_irq_handler() inside the assembly blob. We cannot put it 699 // into a separate .S file, because syzkaller requires a standalone header for reproducers. 700 __attribute__((used)) 701 GUEST_CODE static void 702 one_irq_handler_fn() 703 { 704 asm volatile( 705 R"(.global one_irq_handler 706 one_irq_handler: 707 # Allocate 34 * uint64 for struct ex_regs. 708 add sp, sp, #-16 * 17 709 # Store registers x0-x29 on the stack. 710 stp x0, x1, [sp, #16 * 0] 711 stp x2, x3, [sp, #16 * 1] 712 stp x4, x5, [sp, #16 * 2] 713 stp x6, x7, [sp, #16 * 3] 714 stp x8, x9, [sp, #16 * 4] 715 stp x10, x11, [sp, #16 * 5] 716 stp x12, x13, [sp, #16 * 6] 717 stp x14, x15, [sp, #16 * 7] 718 stp x16, x17, [sp, #16 * 8] 719 stp x18, x19, [sp, #16 * 9] 720 stp x20, x21, [sp, #16 * 10] 721 stp x22, x23, [sp, #16 * 11] 722 stp x24, x25, [sp, #16 * 12] 723 stp x26, x27, [sp, #16 * 13] 724 stp x28, x29, [sp, #16 * 14] 725 726 add x1, sp, #16 * 17 727 # Store x30 and SP (before allocating ex_regs). 728 stp x30, x1, [sp, #16 * 15] 729 730 # ELR_EL1 holds the PC to return to. 731 mrs x1, elr_el1 732 # SPSR_EL1 is the saved PSTATE. 733 mrs x2, spsr_el1 734 # Also store them to ex_regs. 735 stp x1, x2, [sp, #16 * 16] 736 737 # Call guest_irq_handler(ex_regs). 738 mov x0, sp 739 bl guest_irq_handler 740 741 # Restore ELR_EL1 and SPSR_EL1. 742 ldp x1, x2, [sp, #16 * 16] 743 msr elr_el1, x1 744 msr spsr_el1, x2 745 746 # Restore the GP registers x0-x30 (ignoring SP). 747 ldp x30, xzr, [sp, #16 * 15] 748 ldp x28, x29, [sp, #16 * 14] 749 ldp x26, x27, [sp, #16 * 13] 750 ldp x24, x25, [sp, #16 * 12] 751 ldp x22, x23, [sp, #16 * 11] 752 ldp x20, x21, [sp, #16 * 10] 753 ldp x18, x19, [sp, #16 * 9] 754 ldp x16, x17, [sp, #16 * 8] 755 ldp x14, x15, [sp, #16 * 7] 756 ldp x12, x13, [sp, #16 * 6] 757 ldp x10, x11, [sp, #16 * 5] 758 ldp x8, x9, [sp, #16 * 4] 759 ldp x6, x7, [sp, #16 * 3] 760 ldp x4, x5, [sp, #16 * 2] 761 ldp x2, x3, [sp, #16 * 1] 762 ldp x0, x1, [sp, #16 * 0] 763 764 add sp, sp, #16 * 17 765 766 # Use ERET to exit from an exception. 767 eret)" 768 : 769 : 770 : "memory"); 771 } 772 773 #ifdef __cplusplus 774 extern "C" { 775 #endif 776 __attribute__((used)) 777 GUEST_CODE static void 778 guest_irq_handler(struct ex_regs* regs) 779 { 780 uint64 iar0, iar1, irq_num = 0; 781 bool is_group0 = false; 782 // Acknowledge the interrupt by reading the IAR. 783 // Depending on the particular interrupt's Group (0 or 1), its number will appear in either ICC_IAR0_EL1, or ICC_IAR1_EL1. 784 // The other register will contain a special interrupt number between 1020 and 1023. 785 // Numbers below 1020 are SGIs, PPIs and SPIs, numbers above 1023 are reserved interrupts and LPIs. 786 asm volatile("mrs %0, " ICC_IAR0_EL1 787 : "=r"(iar0)); 788 asm volatile("mrs %0, " ICC_IAR1_EL1 789 : "=r"(iar1)); 790 if ((iar0 < 1020) || (iar0 > 1023)) { 791 irq_num = iar0; 792 is_group0 = true; 793 } else if ((iar1 < 1020) || (iar1 > 1023)) { 794 irq_num = iar1; 795 } else { 796 return; 797 } 798 799 // Handle the interrupt by doing a uexit. 800 // TODO(glider): do something more interesting here. 801 guest_uexit(UEXIT_IRQ); 802 803 // Signal End of Interrupt (EOI) by writing back to the EOIR. 804 if (is_group0) { 805 asm volatile("msr " ICC_EOIR0_EL1 ", %0" 806 : 807 : "r"(irq_num)); 808 } else { 809 asm volatile("msr " ICC_EOIR1_EL1 ", %0" 810 : 811 : "r"(irq_num)); 812 } 813 // Deactivate the interrupt. 814 asm volatile("msr " ICC_DIR_EL1 ", %0" 815 : 816 : "r"(irq_num)); 817 } 818 #ifdef __cplusplus 819 } 820 #endif 821 822 // Default IRQ handler. 823 #define IRQ_ENTRY \ 824 ".balign 0x80\n" \ 825 "b one_irq_handler\n" 826 827 // Unused IRQ entry. 828 #define IRQ_ENTRY_DUMMY \ 829 ".balign 0x80\n" \ 830 "eret\n" 831 832 // clang-format off 833 // guest_vector_table_fn() is never used, it is just needed to declare guest_vector_table() 834 // inside the assembly blob. 835 __attribute__((used)) 836 GUEST_CODE static void guest_vector_table_fn() 837 { 838 // Exception vector table as explained at 839 // https://developer.arm.com/documentation/100933/0100/AArch64-exception-vector-table. 840 asm volatile( 841 ".global guest_vector_table\n" 842 ".balign 2048\n" 843 "guest_vector_table:\n" 844 // Exception handlers for current EL with SP0. 845 IRQ_ENTRY_DUMMY 846 IRQ_ENTRY_DUMMY 847 IRQ_ENTRY_DUMMY 848 IRQ_ENTRY_DUMMY 849 850 // Exception handlers for current EL with SPx. 851 IRQ_ENTRY_DUMMY 852 // Only handle IRQ/vIRQ for now. 853 IRQ_ENTRY 854 IRQ_ENTRY_DUMMY 855 IRQ_ENTRY_DUMMY 856 857 // Exception handlers for lower EL using AArch64. 858 IRQ_ENTRY_DUMMY 859 IRQ_ENTRY_DUMMY 860 IRQ_ENTRY_DUMMY 861 IRQ_ENTRY_DUMMY 862 863 // Exception handlers for lower EL using AArch32. 864 IRQ_ENTRY_DUMMY 865 IRQ_ENTRY_DUMMY 866 IRQ_ENTRY_DUMMY 867 IRQ_ENTRY_DUMMY); 868 } 869 // clang-format on 870 871 // ITS setup below. 872 #define GITS_CTLR 0x0000 873 #define GITS_CBASER 0x0080 874 #define GITS_CWRITER 0x0088 875 #define GITS_CREADR 0x0090 876 #define GITS_BASER 0x0100 877 878 #define GITS_CTLR_ENABLE (1U << 0) 879 880 #define GIC_BASER_InnerShareable 1ULL 881 882 #define GIC_PAGE_SIZE_64K 2ULL 883 #define GITS_BASER_PAGE_SIZE_SHIFT (8) 884 #define __GITS_BASER_PSZ(sz) (GIC_PAGE_SIZE_##sz << GITS_BASER_PAGE_SIZE_SHIFT) 885 #define GITS_BASER_PAGE_SIZE_64K __GITS_BASER_PSZ(64K) 886 887 #define GIC_BASER_CACHE_RaWaWb 7ULL 888 #define GITS_BASER_INNER_CACHEABILITY_SHIFT (59) 889 #define GITS_BASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWb) 890 891 #define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) 892 #define GITS_CBASER_RaWaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWb) 893 894 #define GICR_PROPBASER_SHAREABILITY_SHIFT (10) 895 #define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT (7) 896 #define GICR_PROPBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWb) 897 #define GICR_PROPBASER_IDBITS_MASK (0x1f) 898 899 #define GIC_BASER_CACHEABILITY(reg, inner_outer, type) \ 900 (GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) 901 902 #define GITS_BASER_SHAREABILITY_SHIFT (10) 903 #define GITS_CBASER_SHAREABILITY_SHIFT (10) 904 #define GIC_BASER_SHAREABILITY(reg, type) \ 905 (GIC_BASER_##type << reg##_SHAREABILITY_SHIFT) 906 #define GITS_BASER_InnerShareable \ 907 GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable) 908 909 #define GITS_CBASER_InnerShareable \ 910 GIC_BASER_SHAREABILITY(GITS_CBASER, InnerShareable) 911 912 #define GICR_PROPBASER_InnerShareable \ 913 GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable) 914 915 #define GICR_PENDBASER_InnerShareable \ 916 GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable) 917 918 #define GICR_PENDBASER_SHAREABILITY_SHIFT (10) 919 #define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT (7) 920 #define GICR_PENDBASER_RaWaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWb) 921 922 #define GITS_BASER_TYPE_NONE 0 923 #define GITS_BASER_TYPE_DEVICE 1 924 #define GITS_BASER_TYPE_VCPU 2 925 #define GITS_BASER_TYPE_RESERVED3 3 926 #define GITS_BASER_TYPE_COLLECTION 4 927 #define GITS_BASER_TYPE_RESERVED5 5 928 #define GITS_BASER_TYPE_RESERVED6 6 929 #define GITS_BASER_TYPE_RESERVED7 7 930 931 #define GITS_BASER_TYPE_SHIFT (56) 932 #define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) 933 934 #define GITS_BASER_NR_REGS 8 935 #define GITS_BASER_VALID (1ULL << 63) 936 937 #define GITS_CBASER_VALID (1ULL << 63) 938 939 GUEST_CODE static uint64 its_read_u64(unsigned long offset) 940 { 941 return readq(ARM64_ADDR_GITS_BASE + offset); 942 } 943 944 GUEST_CODE static void its_write_u64(unsigned long offset, uint64 val) 945 { 946 writeq(val, ARM64_ADDR_GITS_BASE + offset); 947 } 948 949 GUEST_CODE static uint32 its_read_u32(unsigned long offset) 950 { 951 return readl(ARM64_ADDR_GITS_BASE + offset); 952 } 953 954 GUEST_CODE static void its_write_u32(unsigned long offset, uint32 val) 955 { 956 writel(val, ARM64_ADDR_GITS_BASE + offset); 957 } 958 959 struct its_cmd_block { 960 // Kernel defines this struct as a union, but we don't need raw_cmd_le for now. 961 uint64 raw_cmd[4]; 962 }; 963 964 // Guest memcpy implementation is using volatile accesses to prevent the compiler from optimizing it 965 // into a memcpy() call. 966 GUEST_CODE static noinline void guest_memcpy(void* dst, void* src, size_t size) 967 { 968 volatile char* pdst = (char*)dst; 969 volatile char* psrc = (char*)src; 970 for (size_t i = 0; i < size; i++) 971 pdst[i] = psrc[i]; 972 } 973 974 // Send an ITS command by copying it to the command queue at the offset defined by GITS_CWRITER. 975 // https://developer.arm.com/documentation/100336/0106/operation/interrupt-translation-service--its-/its-commands-and-errors. 976 GUEST_CODE static noinline void its_send_cmd(uint64 cmdq_base, struct its_cmd_block* cmd) 977 { 978 uint64 cwriter = its_read_u64(GITS_CWRITER); 979 struct its_cmd_block* dst = (struct its_cmd_block*)(cmdq_base + cwriter); 980 uint64 cbaser = its_read_u64(GITS_CBASER); 981 size_t cmdq_size = ((cbaser & 0xFF) + 1) * SZ_4K; 982 guest_memcpy(dst, cmd, sizeof(*cmd)); 983 dmb(); 984 uint64 next = (cwriter + sizeof(*cmd)) % cmdq_size; 985 its_write_u64(GITS_CWRITER, next); 986 // KVM synchronously processes the command after writing to GITS_CWRITER. 987 // Hardware ITS implementation would've required polling here. 988 } 989 990 GUEST_CODE static unsigned long its_find_baser(unsigned int type) 991 { 992 for (int i = 0; i < GITS_BASER_NR_REGS; i++) { 993 uint64 baser; 994 unsigned long offset = GITS_BASER + (i * sizeof(baser)); 995 996 baser = its_read_u64(offset); 997 if (GITS_BASER_TYPE(baser) == type) 998 return offset; 999 } 1000 1001 GUEST_ASSERT(0); 1002 return -1; 1003 } 1004 1005 GUEST_CODE static void its_install_table(unsigned int type, uint64 base, size_t size) 1006 { 1007 unsigned long offset = its_find_baser(type); 1008 uint64 baser = ((size / SZ_64K) - 1) | 1009 GITS_BASER_PAGE_SIZE_64K | 1010 GITS_BASER_InnerShareable | 1011 base | 1012 GITS_BASER_RaWaWb | 1013 GITS_BASER_VALID; 1014 1015 its_write_u64(offset, baser); 1016 } 1017 1018 GUEST_CODE static void its_install_cmdq(uint64 base, size_t size) 1019 { 1020 uint64 cbaser = ((size / SZ_4K) - 1) | 1021 GITS_CBASER_InnerShareable | 1022 base | 1023 GITS_CBASER_RaWaWb | 1024 GITS_CBASER_VALID; 1025 1026 its_write_u64(GITS_CBASER, cbaser); 1027 } 1028 1029 GUEST_CODE static void its_init(uint64 coll_tbl, 1030 uint64 device_tbl, uint64 cmdq) 1031 { 1032 its_install_table(GITS_BASER_TYPE_COLLECTION, coll_tbl, SZ_64K); 1033 its_install_table(GITS_BASER_TYPE_DEVICE, device_tbl, SZ_64K); 1034 its_install_cmdq(cmdq, SZ_64K); 1035 1036 uint32 ctlr = its_read_u32(GITS_CTLR); 1037 ctlr |= GITS_CTLR_ENABLE; 1038 its_write_u32(GITS_CTLR, ctlr); 1039 } 1040 1041 #define GIC_LPI_OFFSET 8192 1042 1043 #define GITS_CMD_MAPD 0x08 1044 #define GITS_CMD_MAPC 0x09 1045 #define GITS_CMD_MAPTI 0x0a 1046 #define GITS_CMD_MAPI 0x0b 1047 #define GITS_CMD_MOVI 0x01 1048 #define GITS_CMD_DISCARD 0x0f 1049 #define GITS_CMD_INV 0x0c 1050 #define GITS_CMD_MOVALL 0x0e 1051 #define GITS_CMD_INVALL 0x0d 1052 #define GITS_CMD_INT 0x03 1053 #define GITS_CMD_CLEAR 0x04 1054 #define GITS_CMD_SYNC 0x05 1055 1056 // Avoid inlining this function, because it may cause emitting constants into .rodata. 1057 GUEST_CODE static noinline void 1058 its_mask_encode(uint64* raw_cmd, uint64 val, int h, int l) 1059 { 1060 uint64 mask = GENMASK_ULL(h, l); 1061 *raw_cmd &= ~mask; 1062 *raw_cmd |= (val << l) & mask; 1063 } 1064 1065 GUEST_CODE static void its_encode_cmd(struct its_cmd_block* cmd, uint8 cmd_nr) 1066 { 1067 its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0); 1068 } 1069 1070 GUEST_CODE static void its_encode_devid(struct its_cmd_block* cmd, uint32 devid) 1071 { 1072 its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32); 1073 } 1074 1075 GUEST_CODE static void its_encode_event_id(struct its_cmd_block* cmd, uint32 id) 1076 { 1077 its_mask_encode(&cmd->raw_cmd[1], id, 31, 0); 1078 } 1079 1080 GUEST_CODE static void its_encode_phys_id(struct its_cmd_block* cmd, uint32 phys_id) 1081 { 1082 its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32); 1083 } 1084 1085 GUEST_CODE static void its_encode_size(struct its_cmd_block* cmd, uint8 size) 1086 { 1087 its_mask_encode(&cmd->raw_cmd[1], size, 4, 0); 1088 } 1089 1090 GUEST_CODE static void its_encode_itt(struct its_cmd_block* cmd, uint64 itt_addr) 1091 { 1092 its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8); 1093 } 1094 1095 GUEST_CODE static void its_encode_valid(struct its_cmd_block* cmd, int valid) 1096 { 1097 its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63); 1098 } 1099 1100 GUEST_CODE static void its_encode_target(struct its_cmd_block* cmd, uint64 target_addr) 1101 { 1102 its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16); 1103 } 1104 1105 // RDbase2 encoded in the fourth double word of the command. 1106 GUEST_CODE static void its_encode_target2(struct its_cmd_block* cmd, uint64 target_addr) 1107 { 1108 its_mask_encode(&cmd->raw_cmd[3], target_addr >> 16, 51, 16); 1109 } 1110 1111 GUEST_CODE static void its_encode_collection(struct its_cmd_block* cmd, uint16 col) 1112 { 1113 its_mask_encode(&cmd->raw_cmd[2], col, 15, 0); 1114 } 1115 1116 GUEST_CODE static noinline void guest_memzero(void* ptr, size_t size) 1117 { 1118 volatile char* p = (char*)ptr; 1119 for (size_t i = 0; i < size; i++) 1120 p[i] = 0; 1121 } 1122 1123 GUEST_CODE static void its_send_mapd_cmd(uint64 cmdq_base, uint32 device_id, uint64 itt_base, 1124 size_t num_idbits, bool valid) 1125 { 1126 struct its_cmd_block cmd; 1127 guest_memzero(&cmd, sizeof(cmd)); 1128 its_encode_cmd(&cmd, GITS_CMD_MAPD); 1129 its_encode_devid(&cmd, device_id); 1130 its_encode_size(&cmd, num_idbits - 1); 1131 its_encode_itt(&cmd, itt_base); 1132 its_encode_valid(&cmd, valid); 1133 1134 its_send_cmd(cmdq_base, &cmd); 1135 } 1136 1137 GUEST_CODE static void its_send_mapc_cmd(uint64 cmdq_base, uint32 vcpu_id, uint32 collection_id, bool valid) 1138 { 1139 struct its_cmd_block cmd; 1140 guest_memzero(&cmd, sizeof(cmd)); 1141 its_encode_cmd(&cmd, GITS_CMD_MAPC); 1142 its_encode_collection(&cmd, collection_id); 1143 its_encode_target(&cmd, vcpu_id); 1144 its_encode_valid(&cmd, valid); 1145 1146 its_send_cmd(cmdq_base, &cmd); 1147 } 1148 1149 GUEST_CODE static void its_send_mapti_cmd(uint64 cmdq_base, uint32 device_id, 1150 uint32 event_id, uint32 collection_id, 1151 uint32 intid) 1152 { 1153 struct its_cmd_block cmd; 1154 guest_memzero(&cmd, sizeof(cmd)); 1155 its_encode_cmd(&cmd, GITS_CMD_MAPTI); 1156 its_encode_devid(&cmd, device_id); 1157 its_encode_event_id(&cmd, event_id); 1158 its_encode_phys_id(&cmd, intid); 1159 its_encode_collection(&cmd, collection_id); 1160 its_send_cmd(cmdq_base, &cmd); 1161 } 1162 1163 GUEST_CODE static void its_send_devid_eventid_icid_cmd(uint64 cmdq_base, uint8 cmd_nr, uint32 device_id, 1164 uint32 event_id, uint32 intid) 1165 { 1166 struct its_cmd_block cmd; 1167 guest_memzero(&cmd, sizeof(cmd)); 1168 its_encode_cmd(&cmd, cmd_nr); 1169 its_encode_devid(&cmd, device_id); 1170 its_encode_event_id(&cmd, event_id); 1171 its_encode_phys_id(&cmd, intid); 1172 its_send_cmd(cmdq_base, &cmd); 1173 } 1174 1175 GUEST_CODE static void its_send_devid_eventid_cmd(uint64 cmdq_base, uint8 cmd_nr, uint32 device_id, 1176 uint32 event_id) 1177 { 1178 struct its_cmd_block cmd; 1179 guest_memzero(&cmd, sizeof(cmd)); 1180 its_encode_cmd(&cmd, cmd_nr); 1181 its_encode_devid(&cmd, device_id); 1182 its_encode_event_id(&cmd, event_id); 1183 its_send_cmd(cmdq_base, &cmd); 1184 } 1185 1186 GUEST_CODE static void its_send_movall_cmd(uint64 cmdq_base, uint32 vcpu_id, uint32 vcpu_id2) 1187 { 1188 struct its_cmd_block cmd; 1189 guest_memzero(&cmd, sizeof(cmd)); 1190 its_encode_cmd(&cmd, GITS_CMD_MOVALL); 1191 its_encode_target(&cmd, vcpu_id); 1192 its_encode_target2(&cmd, vcpu_id2); 1193 1194 its_send_cmd(cmdq_base, &cmd); 1195 } 1196 1197 GUEST_CODE static void 1198 its_send_invall_cmd(uint64 cmdq_base, uint32 collection_id) 1199 { 1200 struct its_cmd_block cmd; 1201 guest_memzero(&cmd, sizeof(cmd)); 1202 its_encode_cmd(&cmd, GITS_CMD_INVALL); 1203 its_encode_collection(&cmd, collection_id); 1204 its_send_cmd(cmdq_base, &cmd); 1205 } 1206 1207 // We assume that the number of supported IDbits for the proproperties table is 16, so the size of the 1208 // table itself is 64K. 1209 // TODO(glider): it may be interesting to use a different size here. 1210 #define SYZOS_NUM_IDBITS 16 1211 1212 GUEST_CODE static void its_send_sync_cmd(uint64 cmdq_base, uint32 vcpu_id) 1213 { 1214 struct its_cmd_block cmd; 1215 guest_memzero(&cmd, sizeof(cmd)); 1216 its_encode_cmd(&cmd, GITS_CMD_SYNC); 1217 its_encode_target(&cmd, vcpu_id); 1218 its_send_cmd(cmdq_base, &cmd); 1219 } 1220 1221 // This function is carefully written in a way that prevents jump table emission. 1222 // SyzOS cannot reference global constants, and compilers are very eager to generate a jump table 1223 // for a switch over GITS commands. 1224 // To work around that, we replace the switch statement with a series of if statements. 1225 // In addition, cmd->type is stored in a volatile variable, so that it is read on each if statement, 1226 // preventing the compiler from folding them together. 1227 GUEST_CODE static noinline void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd) 1228 { 1229 volatile uint8 type = cmd->type; 1230 if (type == GITS_CMD_MAPD) { 1231 uint64 itt_base = ARM64_ADDR_ITS_ITT_TABLES + cmd->devid * SZ_64K; 1232 its_send_mapd_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->devid, itt_base, 1233 SYZOS_NUM_IDBITS, cmd->valid); 1234 return; 1235 } 1236 if (type == GITS_CMD_MAPC) { 1237 its_send_mapc_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid, cmd->cpuid, 1238 cmd->valid); 1239 return; 1240 } 1241 if (type == GITS_CMD_MAPTI) { 1242 its_send_mapti_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->devid, cmd->eventid, 1243 cmd->cpuid, cmd->intid); 1244 return; 1245 } 1246 if (type == GITS_CMD_MAPI || type == GITS_CMD_MOVI) { 1247 its_send_devid_eventid_icid_cmd(ARM64_ADDR_ITS_CMDQ_BASE, type, 1248 cmd->devid, cmd->eventid, cmd->intid); 1249 return; 1250 } 1251 if (type == GITS_CMD_MOVALL) { 1252 its_send_movall_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid, cmd->cpuid2); 1253 return; 1254 } 1255 if (type == GITS_CMD_INVALL) { 1256 its_send_invall_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid); 1257 return; 1258 } 1259 if (type == GITS_CMD_INT || type == GITS_CMD_INV || type == GITS_CMD_DISCARD || type == GITS_CMD_CLEAR) { 1260 its_send_devid_eventid_cmd(ARM64_ADDR_ITS_CMDQ_BASE, type, cmd->devid, 1261 cmd->eventid); 1262 return; 1263 } 1264 if (type == GITS_CMD_SYNC) { 1265 its_send_sync_cmd(ARM64_ADDR_ITS_CMDQ_BASE, cmd->cpuid); 1266 return; 1267 } 1268 } 1269 1270 GUEST_CODE static noinline void guest_setup_its_mappings(uint64 cmdq_base, 1271 uint64 itt_tables, 1272 uint32 nr_events, 1273 uint32 nr_devices, 1274 uint32 nr_cpus) 1275 { 1276 if ((nr_events < 1) || (nr_devices < 1) || (nr_cpus < 1)) 1277 return; 1278 1279 // Event IDs start from 0 and map to LPI IDs starting from GIC_LPI_OFFSET. 1280 uint32 coll_id, device_id, event_id, intid = GIC_LPI_OFFSET; 1281 for (coll_id = 0; coll_id < nr_cpus; coll_id++) { 1282 // If GITS_TYPER.PTA == 0, RDbase is just the CPU id. 1283 its_send_mapc_cmd(cmdq_base, coll_id, coll_id, true); 1284 } 1285 // Round-robin the LPIs to all of the vCPUs in the VM. 1286 coll_id = 0; 1287 for (device_id = 0; device_id < nr_devices; device_id++) { 1288 uint64 itt_base = itt_tables + (device_id * SZ_64K); 1289 its_send_mapd_cmd(cmdq_base, device_id, itt_base, SYZOS_NUM_IDBITS, true); 1290 for (event_id = 0; event_id < nr_events; event_id++) { 1291 its_send_mapti_cmd(cmdq_base, device_id, event_id, coll_id, intid++); 1292 coll_id = (coll_id + 1) % nr_cpus; 1293 } 1294 } 1295 } 1296 1297 GUEST_CODE static void guest_invalidate_all_rdists(uint64 cmdq_base, int nr_cpus) 1298 { 1299 for (int i = 0; i < nr_cpus; i++) 1300 its_send_invall_cmd(cmdq_base, i); 1301 } 1302 1303 // Set up GIRC_PROPBASER and GICR_PENDBASER. 1304 void gic_rdist_enable_lpis(uint64 cfg_table, size_t cfg_table_size, 1305 uint64 pend_table) 1306 { 1307 uint64 rdist_base = gicr_base_cpu(get_cpu_id()); 1308 uint64 val = (cfg_table | 1309 GICR_PROPBASER_InnerShareable | 1310 GICR_PROPBASER_RaWaWb | 1311 ((SYZOS_NUM_IDBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); 1312 1313 writeq(val, rdist_base + GICR_PROPBASER); 1314 1315 val = (pend_table | 1316 GICR_PENDBASER_InnerShareable | 1317 GICR_PENDBASER_RaWaWb); 1318 writeq(val, rdist_base + GICR_PENDBASER); 1319 1320 uint64 ctlr = readl(rdist_base + GICR_CTLR); 1321 ctlr |= GICR_CTLR_ENABLE_LPIS; 1322 writel(ctlr, rdist_base + GICR_CTLR); 1323 } 1324 1325 #define LPI_PROP_DEFAULT_PRIO 0xa0 1326 #define LPI_PROP_GROUP1 (1 << 1) 1327 #define LPI_PROP_ENABLED (1 << 0) 1328 1329 // TODO(glider) non-volatile access is compiled into: 1330 // 0000000000452154 <configure_lpis.constprop.0>: 1331 // 452154: 4f05e460 movi v0.16b, #0xa3 1332 // 452158: 3d800000 str q0, [x0] 1333 // 45215c: d65f03c0 ret 1334 // , which for some reason hangs. 1335 GUEST_CODE static noinline void configure_lpis(uint64 prop_table, int nr_devices, int nr_events) 1336 { 1337 int nr_lpis = nr_devices * nr_events; 1338 volatile uint8* tbl = (uint8*)prop_table; 1339 for (int i = 0; i < nr_lpis; i++) { 1340 tbl[i] = LPI_PROP_DEFAULT_PRIO | 1341 LPI_PROP_GROUP1 | 1342 LPI_PROP_ENABLED; 1343 } 1344 } 1345 1346 GUEST_CODE static void guest_prepare_its(int nr_cpus, int nr_devices, int nr_events) 1347 { 1348 configure_lpis(ARM64_ADDR_ITS_PROP_TABLE, nr_devices, nr_events); 1349 gic_rdist_enable_lpis(ARM64_ADDR_ITS_PROP_TABLE, SZ_64K, ARM64_ADDR_ITS_PEND_TABLES); 1350 its_init(ARM64_ADDR_ITS_COLL_TABLE, ARM64_ADDR_ITS_DEVICE_TABLE, ARM64_ADDR_ITS_CMDQ_BASE); 1351 guest_setup_its_mappings(ARM64_ADDR_ITS_CMDQ_BASE, ARM64_ADDR_ITS_ITT_TABLES, nr_events, nr_devices, nr_cpus); 1352 guest_invalidate_all_rdists(ARM64_ADDR_ITS_CMDQ_BASE, nr_cpus); 1353 } 1354 1355 #endif // EXECUTOR_COMMON_KVM_ARM64_SYZOS_H