xref: /OK3568_Linux_fs/kernel/Documentation/sparc/adi.rst (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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