xref: /linux/Documentation/arch/sparc/adi.rst (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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