1*4882a593Smuzhiyun================================ 2*4882a593SmuzhiyunApplication Data Integrity (ADI) 3*4882a593Smuzhiyun================================ 4*4882a593Smuzhiyun 5*4882a593SmuzhiyunSPARC M7 processor adds the Application Data Integrity (ADI) feature. 6*4882a593SmuzhiyunADI allows a task to set version tags on any subset of its address 7*4882a593Smuzhiyunspace. Once ADI is enabled and version tags are set for ranges of 8*4882a593Smuzhiyunaddress space of a task, the processor will compare the tag in pointers 9*4882a593Smuzhiyunto memory in these ranges to the version set by the application 10*4882a593Smuzhiyunpreviously. Access to memory is granted only if the tag in given pointer 11*4882a593Smuzhiyunmatches the tag set by the application. In case of mismatch, processor 12*4882a593Smuzhiyunraises an exception. 13*4882a593Smuzhiyun 14*4882a593SmuzhiyunFollowing steps must be taken by a task to enable ADI fully: 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun1. Set the user mode PSTATE.mcde bit. This acts as master switch for 17*4882a593Smuzhiyun the task's entire address space to enable/disable ADI for the task. 18*4882a593Smuzhiyun 19*4882a593Smuzhiyun2. Set TTE.mcd bit on any TLB entries that correspond to the range of 20*4882a593Smuzhiyun addresses ADI is being enabled on. MMU checks the version tag only 21*4882a593Smuzhiyun on the pages that have TTE.mcd bit set. 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun3. Set the version tag for virtual addresses using stxa instruction 24*4882a593Smuzhiyun and one of the MCD specific ASIs. Each stxa instruction sets the 25*4882a593Smuzhiyun given tag for one ADI block size number of bytes. This step must 26*4882a593Smuzhiyun be repeated for entire page to set tags for entire page. 27*4882a593Smuzhiyun 28*4882a593SmuzhiyunADI block size for the platform is provided by the hypervisor to kernel 29*4882a593Smuzhiyunin machine description tables. Hypervisor also provides the number of 30*4882a593Smuzhiyuntop bits in the virtual address that specify the version tag. Once 31*4882a593Smuzhiyunversion tag has been set for a memory location, the tag is stored in the 32*4882a593Smuzhiyunphysical memory and the same tag must be present in the ADI version tag 33*4882a593Smuzhiyunbits of the virtual address being presented to the MMU. For example on 34*4882a593SmuzhiyunSPARC M7 processor, MMU uses bits 63-60 for version tags and ADI block 35*4882a593Smuzhiyunsize is same as cacheline size which is 64 bytes. A task that sets ADI 36*4882a593Smuzhiyunversion to, say 10, on a range of memory, must access that memory using 37*4882a593Smuzhiyunvirtual addresses that contain 0xa in bits 63-60. 38*4882a593Smuzhiyun 39*4882a593SmuzhiyunADI is enabled on a set of pages using mprotect() with PROT_ADI flag. 40*4882a593SmuzhiyunWhen ADI is enabled on a set of pages by a task for the first time, 41*4882a593Smuzhiyunkernel sets the PSTATE.mcde bit fot the task. Version tags for memory 42*4882a593Smuzhiyunaddresses are set with an stxa instruction on the addresses using 43*4882a593SmuzhiyunASI_MCD_PRIMARY or ASI_MCD_ST_BLKINIT_PRIMARY. ADI block size is 44*4882a593Smuzhiyunprovided by the hypervisor to the kernel. Kernel returns the value of 45*4882a593SmuzhiyunADI block size to userspace using auxiliary vector along with other ADI 46*4882a593Smuzhiyuninfo. Following auxiliary vectors are provided by the kernel: 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun ============ =========================================== 49*4882a593Smuzhiyun AT_ADI_BLKSZ ADI block size. This is the granularity and 50*4882a593Smuzhiyun alignment, in bytes, of ADI versioning. 51*4882a593Smuzhiyun AT_ADI_NBITS Number of ADI version bits in the VA 52*4882a593Smuzhiyun ============ =========================================== 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun 55*4882a593SmuzhiyunIMPORTANT NOTES 56*4882a593Smuzhiyun=============== 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun- Version tag values of 0x0 and 0xf are reserved. These values match any 59*4882a593Smuzhiyun tag in virtual address and never generate a mismatch exception. 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun- Version tags are set on virtual addresses from userspace even though 62*4882a593Smuzhiyun tags are stored in physical memory. Tags are set on a physical page 63*4882a593Smuzhiyun after it has been allocated to a task and a pte has been created for 64*4882a593Smuzhiyun it. 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun- When a task frees a memory page it had set version tags on, the page 67*4882a593Smuzhiyun goes back to free page pool. When this page is re-allocated to a task, 68*4882a593Smuzhiyun kernel clears the page using block initialization ASI which clears the 69*4882a593Smuzhiyun version tags as well for the page. If a page allocated to a task is 70*4882a593Smuzhiyun freed and allocated back to the same task, old version tags set by the 71*4882a593Smuzhiyun task on that page will no longer be present. 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun- ADI tag mismatches are not detected for non-faulting loads. 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun- Kernel does not set any tags for user pages and it is entirely a 76*4882a593Smuzhiyun task's responsibility to set any version tags. Kernel does ensure the 77*4882a593Smuzhiyun version tags are preserved if a page is swapped out to the disk and 78*4882a593Smuzhiyun swapped back in. It also preserves that version tags if a page is 79*4882a593Smuzhiyun migrated. 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun- ADI works for any size pages. A userspace task need not be aware of 82*4882a593Smuzhiyun page size when using ADI. It can simply select a virtual address 83*4882a593Smuzhiyun range, enable ADI on the range using mprotect() and set version tags 84*4882a593Smuzhiyun for the entire range. mprotect() ensures range is aligned to page size 85*4882a593Smuzhiyun and is a multiple of page size. 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun- ADI tags can only be set on writable memory. For example, ADI tags can 88*4882a593Smuzhiyun not be set on read-only mappings. 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun 92*4882a593SmuzhiyunADI related traps 93*4882a593Smuzhiyun================= 94*4882a593Smuzhiyun 95*4882a593SmuzhiyunWith ADI enabled, following new traps may occur: 96*4882a593Smuzhiyun 97*4882a593SmuzhiyunDisrupting memory corruption 98*4882a593Smuzhiyun---------------------------- 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun When a store accesses a memory localtion that has TTE.mcd=1, 101*4882a593Smuzhiyun the task is running with ADI enabled (PSTATE.mcde=1), and the ADI 102*4882a593Smuzhiyun tag in the address used (bits 63:60) does not match the tag set on 103*4882a593Smuzhiyun the corresponding cacheline, a memory corruption trap occurs. By 104*4882a593Smuzhiyun default, it is a disrupting trap and is sent to the hypervisor 105*4882a593Smuzhiyun first. Hypervisor creates a sun4v error report and sends a 106*4882a593Smuzhiyun resumable error (TT=0x7e) trap to the kernel. The kernel sends 107*4882a593Smuzhiyun a SIGSEGV to the task that resulted in this trap with the following 108*4882a593Smuzhiyun info:: 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun siginfo.si_signo = SIGSEGV; 111*4882a593Smuzhiyun siginfo.errno = 0; 112*4882a593Smuzhiyun siginfo.si_code = SEGV_ADIDERR; 113*4882a593Smuzhiyun siginfo.si_addr = addr; /* PC where first mismatch occurred */ 114*4882a593Smuzhiyun siginfo.si_trapno = 0; 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun 117*4882a593SmuzhiyunPrecise memory corruption 118*4882a593Smuzhiyun------------------------- 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun When a store accesses a memory location that has TTE.mcd=1, 121*4882a593Smuzhiyun the task is running with ADI enabled (PSTATE.mcde=1), and the ADI 122*4882a593Smuzhiyun tag in the address used (bits 63:60) does not match the tag set on 123*4882a593Smuzhiyun the corresponding cacheline, a memory corruption trap occurs. If 124*4882a593Smuzhiyun MCD precise exception is enabled (MCDPERR=1), a precise 125*4882a593Smuzhiyun exception is sent to the kernel with TT=0x1a. The kernel sends 126*4882a593Smuzhiyun a SIGSEGV to the task that resulted in this trap with the following 127*4882a593Smuzhiyun info:: 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun siginfo.si_signo = SIGSEGV; 130*4882a593Smuzhiyun siginfo.errno = 0; 131*4882a593Smuzhiyun siginfo.si_code = SEGV_ADIPERR; 132*4882a593Smuzhiyun siginfo.si_addr = addr; /* address that caused trap */ 133*4882a593Smuzhiyun siginfo.si_trapno = 0; 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun NOTE: 136*4882a593Smuzhiyun ADI tag mismatch on a load always results in precise trap. 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun 139*4882a593SmuzhiyunMCD disabled 140*4882a593Smuzhiyun------------ 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun When a task has not enabled ADI and attempts to set ADI version 143*4882a593Smuzhiyun on a memory address, processor sends an MCD disabled trap. This 144*4882a593Smuzhiyun trap is handled by hypervisor first and the hypervisor vectors this 145*4882a593Smuzhiyun trap through to the kernel as Data Access Exception trap with 146*4882a593Smuzhiyun fault type set to 0xa (invalid ASI). When this occurs, the kernel 147*4882a593Smuzhiyun sends the task SIGSEGV signal with following info:: 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun siginfo.si_signo = SIGSEGV; 150*4882a593Smuzhiyun siginfo.errno = 0; 151*4882a593Smuzhiyun siginfo.si_code = SEGV_ACCADI; 152*4882a593Smuzhiyun siginfo.si_addr = addr; /* address that caused trap */ 153*4882a593Smuzhiyun siginfo.si_trapno = 0; 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun 156*4882a593SmuzhiyunSample program to use ADI 157*4882a593Smuzhiyun------------------------- 158*4882a593Smuzhiyun 159*4882a593SmuzhiyunFollowing sample program is meant to illustrate how to use the ADI 160*4882a593Smuzhiyunfunctionality:: 161*4882a593Smuzhiyun 162*4882a593Smuzhiyun #include <unistd.h> 163*4882a593Smuzhiyun #include <stdio.h> 164*4882a593Smuzhiyun #include <stdlib.h> 165*4882a593Smuzhiyun #include <elf.h> 166*4882a593Smuzhiyun #include <sys/ipc.h> 167*4882a593Smuzhiyun #include <sys/shm.h> 168*4882a593Smuzhiyun #include <sys/mman.h> 169*4882a593Smuzhiyun #include <asm/asi.h> 170*4882a593Smuzhiyun 171*4882a593Smuzhiyun #ifndef AT_ADI_BLKSZ 172*4882a593Smuzhiyun #define AT_ADI_BLKSZ 48 173*4882a593Smuzhiyun #endif 174*4882a593Smuzhiyun #ifndef AT_ADI_NBITS 175*4882a593Smuzhiyun #define AT_ADI_NBITS 49 176*4882a593Smuzhiyun #endif 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun #ifndef PROT_ADI 179*4882a593Smuzhiyun #define PROT_ADI 0x10 180*4882a593Smuzhiyun #endif 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun #define BUFFER_SIZE 32*1024*1024UL 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun main(int argc, char* argv[], char* envp[]) 185*4882a593Smuzhiyun { 186*4882a593Smuzhiyun unsigned long i, mcde, adi_blksz, adi_nbits; 187*4882a593Smuzhiyun char *shmaddr, *tmp_addr, *end, *veraddr, *clraddr; 188*4882a593Smuzhiyun int shmid, version; 189*4882a593Smuzhiyun Elf64_auxv_t *auxv; 190*4882a593Smuzhiyun 191*4882a593Smuzhiyun adi_blksz = 0; 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun while(*envp++ != NULL); 194*4882a593Smuzhiyun for (auxv = (Elf64_auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) { 195*4882a593Smuzhiyun switch (auxv->a_type) { 196*4882a593Smuzhiyun case AT_ADI_BLKSZ: 197*4882a593Smuzhiyun adi_blksz = auxv->a_un.a_val; 198*4882a593Smuzhiyun break; 199*4882a593Smuzhiyun case AT_ADI_NBITS: 200*4882a593Smuzhiyun adi_nbits = auxv->a_un.a_val; 201*4882a593Smuzhiyun break; 202*4882a593Smuzhiyun } 203*4882a593Smuzhiyun } 204*4882a593Smuzhiyun if (adi_blksz == 0) { 205*4882a593Smuzhiyun fprintf(stderr, "Oops! ADI is not supported\n"); 206*4882a593Smuzhiyun exit(1); 207*4882a593Smuzhiyun } 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun printf("ADI capabilities:\n"); 210*4882a593Smuzhiyun printf("\tBlock size = %ld\n", adi_blksz); 211*4882a593Smuzhiyun printf("\tNumber of bits = %ld\n", adi_nbits); 212*4882a593Smuzhiyun 213*4882a593Smuzhiyun if ((shmid = shmget(2, BUFFER_SIZE, 214*4882a593Smuzhiyun IPC_CREAT | SHM_R | SHM_W)) < 0) { 215*4882a593Smuzhiyun perror("shmget failed"); 216*4882a593Smuzhiyun exit(1); 217*4882a593Smuzhiyun } 218*4882a593Smuzhiyun 219*4882a593Smuzhiyun shmaddr = shmat(shmid, NULL, 0); 220*4882a593Smuzhiyun if (shmaddr == (char *)-1) { 221*4882a593Smuzhiyun perror("shm attach failed"); 222*4882a593Smuzhiyun shmctl(shmid, IPC_RMID, NULL); 223*4882a593Smuzhiyun exit(1); 224*4882a593Smuzhiyun } 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun if (mprotect(shmaddr, BUFFER_SIZE, PROT_READ|PROT_WRITE|PROT_ADI)) { 227*4882a593Smuzhiyun perror("mprotect failed"); 228*4882a593Smuzhiyun goto err_out; 229*4882a593Smuzhiyun } 230*4882a593Smuzhiyun 231*4882a593Smuzhiyun /* Set the ADI version tag on the shm segment 232*4882a593Smuzhiyun */ 233*4882a593Smuzhiyun version = 10; 234*4882a593Smuzhiyun tmp_addr = shmaddr; 235*4882a593Smuzhiyun end = shmaddr + BUFFER_SIZE; 236*4882a593Smuzhiyun while (tmp_addr < end) { 237*4882a593Smuzhiyun asm volatile( 238*4882a593Smuzhiyun "stxa %1, [%0]0x90\n\t" 239*4882a593Smuzhiyun : 240*4882a593Smuzhiyun : "r" (tmp_addr), "r" (version)); 241*4882a593Smuzhiyun tmp_addr += adi_blksz; 242*4882a593Smuzhiyun } 243*4882a593Smuzhiyun asm volatile("membar #Sync\n\t"); 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun /* Create a versioned address from the normal address by placing 246*4882a593Smuzhiyun * version tag in the upper adi_nbits bits 247*4882a593Smuzhiyun */ 248*4882a593Smuzhiyun tmp_addr = (void *) ((unsigned long)shmaddr << adi_nbits); 249*4882a593Smuzhiyun tmp_addr = (void *) ((unsigned long)tmp_addr >> adi_nbits); 250*4882a593Smuzhiyun veraddr = (void *) (((unsigned long)version << (64-adi_nbits)) 251*4882a593Smuzhiyun | (unsigned long)tmp_addr); 252*4882a593Smuzhiyun 253*4882a593Smuzhiyun printf("Starting the writes:\n"); 254*4882a593Smuzhiyun for (i = 0; i < BUFFER_SIZE; i++) { 255*4882a593Smuzhiyun veraddr[i] = (char)(i); 256*4882a593Smuzhiyun if (!(i % (1024 * 1024))) 257*4882a593Smuzhiyun printf("."); 258*4882a593Smuzhiyun } 259*4882a593Smuzhiyun printf("\n"); 260*4882a593Smuzhiyun 261*4882a593Smuzhiyun printf("Verifying data..."); 262*4882a593Smuzhiyun fflush(stdout); 263*4882a593Smuzhiyun for (i = 0; i < BUFFER_SIZE; i++) 264*4882a593Smuzhiyun if (veraddr[i] != (char)i) 265*4882a593Smuzhiyun printf("\nIndex %lu mismatched\n", i); 266*4882a593Smuzhiyun printf("Done.\n"); 267*4882a593Smuzhiyun 268*4882a593Smuzhiyun /* Disable ADI and clean up 269*4882a593Smuzhiyun */ 270*4882a593Smuzhiyun if (mprotect(shmaddr, BUFFER_SIZE, PROT_READ|PROT_WRITE)) { 271*4882a593Smuzhiyun perror("mprotect failed"); 272*4882a593Smuzhiyun goto err_out; 273*4882a593Smuzhiyun } 274*4882a593Smuzhiyun 275*4882a593Smuzhiyun if (shmdt((const void *)shmaddr) != 0) 276*4882a593Smuzhiyun perror("Detach failure"); 277*4882a593Smuzhiyun shmctl(shmid, IPC_RMID, NULL); 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun exit(0); 280*4882a593Smuzhiyun 281*4882a593Smuzhiyun err_out: 282*4882a593Smuzhiyun if (shmdt((const void *)shmaddr) != 0) 283*4882a593Smuzhiyun perror("Detach failure"); 284*4882a593Smuzhiyun shmctl(shmid, IPC_RMID, NULL); 285*4882a593Smuzhiyun exit(1); 286*4882a593Smuzhiyun } 287