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