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