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