Stackdb
Stackdb is a stackable, multi-target and -level source debugger and memory forensics library.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
target_arch_x86.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 The University of Utah
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "config.h"
20 #include "common.h"
21 #include "log.h"
22 #include "target.h"
23 #include "target_api.h"
24 #include "target_arch_x86.h"
25 
26 #ifndef PAGE_SHIFT
27 #define PAGE_SHIFT 12
28 #endif
29 
30 #define CR0_PG 0x80000000
31 #define CR4_PAE 0x20
32 #define EFER_LMA 0x400
33 
34 #define PTE_PSE (1 << 7)
35 #define PTE_DIRTY (1 << 5)
36 #define PTE_NOCACHE (1 << 4)
37 #define PTE_WRITETHROUGH (1 << 3)
38 #define PTE_USER (1 << 2)
39 #define PTE_WRITE (1 << 1)
40 #define PTE_PRESENT (1 << 0)
41 
43  REGVAL msr_efer,REGVAL cpuid_edx,
44  arch_x86_v2p_flags_t *flags) {
45  arch_x86_v2p_flags_t pflags = 0;
46 
47  if (!(cr0 & CR0_PG))
48  pflags |= ARCH_X86_V2P_NOTPAGING;
49  if (msr_efer & EFER_LMA)
50  pflags |= ARCH_X86_V2P_LMA;
51  if (cr4 & CR4_PAE)
52  pflags |= ARCH_X86_V2P_PAE;
53  /*
54  * Detecting PSE itself doesn't really matter; we trust the
55  * pagetables; but we would like to know if the CPU is in PSE-36 or
56  * PSE-40 mode. I haven't found a bit for PSE-40 support yet!
57  *
58  * NB: but -- the thing is, for PSE-36 -- the extra bits used for
59  * PSE-36 in the PTE are reserved and must be zero anyway for the
60  * regular PSE case! Thus, just always do PSE-36.
61  */
62  if (!(cpuid_edx & (1 << 3)))
63  pflags |= ARCH_X86_V2P_NOPSE;
64  if (!(cpuid_edx & (1 << 17))) {
65  pflags |= ARCH_X86_V2P_NOPSE36;
66  /*
67  * XXX: just lump this in with PSE36 -- really we should check
68  * if this is an AMD64 CPU...
69  */
70  pflags |= ARCH_X86_V2P_NOPSE40;
71  }
72 
73  if (flags)
74  *flags = pflags;
75 
76  return 0;
77 }
78 
79 /*
80 Architecture Bits used
81  PGD PUD PMD PTE
82 i386 22-31 12-21
83 i386-PAE 30-31 21-29 12-20
84 x86-64 39-47 30-38 21-29 12-20
85 */
86 
87 int target_arch_x86_v2p(struct target *target,ADDR pgd,ADDR virt,
88  arch_x86_v2p_flags_t flags,ADDR *phys) {
89  int curlevel = 0;
90  int ptlevels;
91  ADDR pbase,paddr;
92  ADDR retval;
93  ADDR vmask;
94  ADDR entry;
95  unsigned int size = 8;
96  unsigned int wordsizealignbits;
97  int downshift;
98  int shiftinc;
99  ADDR entryaddrmask;
100  ADDR nentry;
101 
102  if (target->arch->type != ARCH_X86 && target->arch->type != ARCH_X86_64) {
103  errno = EINVAL;
104  return -1;
105  }
106 
107  vdebug(16,LA_TARGET,LF_TARGET,"lookup vaddr = 0x%"PRIxADDR"\n",virt);
108 
109  if (flags & ARCH_X86_V2P_NOTPAGING) {
110  retval = virt >> PAGE_SHIFT;
111  vdebug(16,LA_TARGET,LF_TARGET,"not paging; paddr = 0x%"PRIxADDR"\n",
112  retval);
113  goto out;
114  }
115 
116  if (flags & ARCH_X86_V2P_PV) {
117  if (target->arch->type == ARCH_X86_64)
118  ptlevels = 4;
119  else
120  /* Assume PAE. */
121  ptlevels = 3;
122  }
123  else if (flags & ARCH_X86_V2P_LMA)
124  ptlevels = 4;
125  else if (flags & ARCH_X86_V2P_PAE)
126  ptlevels = 3;
127  else {
128  ptlevels = 2;
129  size = 4;
130  }
131 
132  /* x86-64 long mode. */
133  if (ptlevels == 4) {
134  downshift = 39;
135  vmask = 0x01ffull << downshift;
136  downshift -= 3;
137  shiftinc = 9;
138  }
139  else if (ptlevels == 3) {
140  /* The top level of PAE is special; these are for levels 2 and down. */
141  downshift = 21;
142  vmask = 0x01ffll << downshift;
143  downshift -= 3;
144  shiftinc = 9;
145  }
146  else if (ptlevels == 2) {
147  downshift = 22;
148  vmask = 0x03ffll << downshift;
149  shiftinc = 10;
150  }
151 
152  if (size == 8) {
153  wordsizealignbits = 3;
154  entryaddrmask = 0x00ffffffffff000ull; /* 40 bits, 12 through 51 */
155  }
156  else {
157  wordsizealignbits = 2;
158  entryaddrmask = 0xfffff000ull; /* 20 bits, 12 through 31 */
159  }
160 
162  "lookup: pgd=0x%"PRIxADDR",ptlevels=%d,size=%u,"
163  "downshift=%d,shiftinc=%d,vmask=0x%"PRIxADDR","
164  "entryaddrmask=0x%"PRIxADDR"\n",
165  pgd,ptlevels,size,downshift,shiftinc,vmask,entryaddrmask);
166 
167  if (ptlevels == 4 || ptlevels == 3) {
168  pbase = pgd;
169  for (curlevel = ptlevels; curlevel > 0; --curlevel) {
170  if (ptlevels == 3 && ptlevels == curlevel) {
171  /* Top level of PAE is special -- and remember to align
172  to wordsize! */
173  paddr = pbase + (((0x03ull << 30) & virt) >> (30 - wordsizealignbits));
174  }
175  else {
176  paddr = pbase + ((vmask & virt) >> downshift);
177  /* adjust for next iteration */
178  vmask >>= shiftinc;
179  downshift -= shiftinc;
180  }
181 
182  entry = 0;
183  if (!target_read_physaddr(target,paddr,size,(unsigned char *)&entry)) {
184  verror("could not read L%d entry (pbase 0x%"PRIxADDR","
185  " paddr 0x%"PRIxADDR")!\n",curlevel,pbase,paddr);
186  errno = EINVAL;
187  return -1;
188  }
189 
190  if (!(entry & PTE_PRESENT)) {
192  "nonpresent L%d entry 0x%"PRIxADDR" (pbase 0x%"PRIxADDR
193  ", paddr 0x%"PRIxADDR"\n",curlevel,entry,pbase,paddr);
194  errno = EADDRNOTAVAIL;
195  return -1;
196  }
197  else if (curlevel == 3 && entry & PTE_PSE) {
198  if (!(flags & ARCH_X86_V2P_NOPSE40))
199  nentry =
200  ((entry & (0x0full << 12)) << 20) /* 16-13 -> 35-32 */
201  | ((entry & (0x0full << 16)) << 16) /* 20-17 -> 39-36 */
202  | ((entry & (0x03ffull << 21))); /* 31-22 -> 31-22 */
203  else if (!(flags & ARCH_X86_V2P_NOPSE36))
204  nentry =
205  ((entry & (0x0full << 12)) << 20) /* 16-13 -> 35-32 */
206  | (entry & (0x03ffull << 21)); /* 31-22 -> 31-22 */
207  else if (!(flags & ARCH_X86_V2P_NOPSE))
208  nentry =
209  (entry & ((0x03ffull << 21))); /* 31-22 -> 31-22 */
210  else {
211  verror("PSE set in PTE, but PSE support disabled in CPU!!\n");
212  errno = EINVAL;
213  return -1;
214  }
215 
216  /* Grab the offset */
217  retval = nentry | (virt & 0x3fffffff);
218 
220  "lookup L%d: entry 0x%"PRIxADDR
221  " -> 1GB phys addr 0x%"PRIxADDR"\n",
222  curlevel,entry,retval);
223 
224  goto out;
225  }
226  else if (curlevel == 2 && entry & PTE_PSE) {
227  if (!(flags & ARCH_X86_V2P_NOPSE40))
228  nentry =
229  ((entry & (0x0full << 12)) << 20) /* 16-13 -> 35-32 */
230  | ((entry & (0x0full << 16)) << 16) /* 20-17 -> 39-36 */
231  | ((entry & (0x03ffull << 21))); /* 31-22 -> 31-22 */
232  else if (!(flags & ARCH_X86_V2P_NOPSE36))
233  nentry =
234  ((entry & (0x0full << 12)) << 16) /* 16-13 -> 35-32 */
235  | ((entry & (0x03ffull << 21))); /* 31-22 -> 31-22 */
236  else if (!(flags & ARCH_X86_V2P_NOPSE))
237  nentry =
238  (entry & ((0x03ffull << 21))); /* 31-22 -> 31-22 */
239  else {
240  verror("PSE set in PTE, but PSE support disabled in CPU!!\n");
241  errno = EINVAL;
242  return -1;
243  }
244 
245  /* Grab the offset */
246  retval = nentry | (virt & 0x0001fffff);
247 
249  "lookup L%d: entry 0x%"PRIxADDR
250  " -> 2MB phys addr 0x%"PRIxADDR"\n",
251  curlevel,nentry,retval);
252 
253  goto out;
254  }
255  else {
256  pbase = (entryaddrmask & entry);
257 
259  "lookup L%d: entry 0x%"PRIxADDR" -> table 0x%"PRIxADDR"\n",
260  curlevel,entry,pbase);
261  }
262  }
263 
264  /*
265  * If we get here, we have walked the tables and just need to
266  * grab the index from the vaddr according to target page size;
267  * XXX assume 4K pages for now.
268  */
269  retval = pbase | (virt & 0x0fffull);
270  }
271  else {
272  pbase = pgd;
273  for (curlevel = ptlevels; curlevel > 0; --curlevel) {
274  paddr = pbase + ((vmask & virt) >> downshift);
275  vmask >>= shiftinc;
276  downshift -= shiftinc;
277 
278  entry = 0;
279  if (!target_read_physaddr(target,paddr,size,(unsigned char *)&entry)) {
280  verror("could not read L%d entry (pbase 0x%"PRIxADDR","
281  " paddr 0x%"PRIxADDR")!\n",curlevel,pbase,paddr);
282  errno = EINVAL;
283  return 0;
284  }
285 
286  if (!(entry & PTE_PRESENT)) {
288  "nonpresent L%d entry 0x%"PRIxADDR" (pbase 0x%"PRIxADDR
289  ", paddr 0x%"PRIxADDR"\n",curlevel,entry,pbase,paddr);
290  errno = EADDRNOTAVAIL;
291  return 0;
292  }
293  else if (curlevel == 2 && entry & PTE_PSE) {
294  if (!(flags & ARCH_X86_V2P_NOPSE40))
295  retval =
296  (entry & ((0x0full << 13) << 19)) /* 16-13 -> 35-32 */
297  | (entry & ((0x0full << 17) << 15)) /* 20-17 -> 39-36 */
298  | (entry & ((0x03ffull << 22))); /* 31-22 -> 31-22 */
299  else if (!(flags & ARCH_X86_V2P_NOPSE36))
300  retval =
301  (entry & ((0x0full << 13) << 19)) /* 16-13 -> 35-32 */
302  | (entry & ((0x03ffull << 22))); /* 31-22 -> 31-22 */
303  else if (!(flags & ARCH_X86_V2P_NOPSE))
304  retval =
305  (entry & ((0x03ffull << 22))); /* 31-22 -> 31-22 */
306  else {
307  verror("PSE set in PTE, but PSE support disabled in CPU!!\n");
308  errno = EINVAL;
309  return -1;
310  }
311 
312  /* Grab the offset */
313  retval |= (virt & 0x0001fffff);
314 
316  "lookup L%d: entry 0x%"PRIxADDR
317  " -> 2MB phys addr 0x%"PRIxADDR"\n",
318  curlevel,entry,retval);
319 
320  goto out;
321  }
322  else {
323  pbase = (entryaddrmask & entry);
324 
326  "lookup L%d: entry 0x%"PRIxADDR" -> table 0x%"PRIxADDR"\n",
327  curlevel,entry,pbase);
328  }
329  }
330 
331  /*
332  * If we get here, we have walked the tables and just need to
333  * grab the index from the vaddr according to target page size;
334  * XXX assume 4K pages for now.
335  */
336  retval = entry | (virt & 0x0fffull);
337  }
338 
339  out:
340  if (curlevel == 0) {
341  vdebug(16,LA_TARGET,LF_TARGET,"paddr = 0x%"PRIxADDR"\n",retval);
342  }
343  /* Done! */
344  if (phys)
345  *phys = retval;
346 
347  return 0;
348 }
349 
351  arch_x86_v2p_flags_t flags,
352  char *buf,unsigned int bufsiz) {
353  unsigned int remaining = bufsiz;
354 
355  if (flags & ARCH_X86_V2P_NOTPAGING) {
356  strncpy(buf + (bufsiz - remaining),"NOTPAGING,",remaining);
357  if (strlen("NOTPAGING,") > remaining) {
358  buf[bufsiz-1] = '\0';
359  return bufsiz;
360  }
361  else
362  remaining -= strlen("NOTPAGING,");
363  }
364  if (flags & ARCH_X86_V2P_PAE) {
365  strncpy(buf + (bufsiz - remaining),"PAE,",remaining);
366  if (strlen("PAE,") > remaining) {
367  buf[bufsiz-1] = '\0';
368  return bufsiz;
369  }
370  else
371  remaining -= strlen("PAE,");
372  }
373  if (flags & ARCH_X86_V2P_NOPSE) {
374  strncpy(buf + (bufsiz - remaining),"NOPSE,",remaining);
375  if (strlen("NOPSE,") > remaining) {
376  buf[bufsiz-1] = '\0';
377  return bufsiz;
378  }
379  else
380  remaining -= strlen("NOPSE,");
381  }
382  if (flags & ARCH_X86_V2P_NOPSE36) {
383  strncpy(buf + (bufsiz - remaining),"NOPSE36,",remaining);
384  if (strlen("NOPSE36,") > remaining) {
385  buf[bufsiz-1] = '\0';
386  return bufsiz;
387  }
388  else
389  remaining -= strlen("NOPSE36,");
390  }
391  if (flags & ARCH_X86_V2P_NOPSE40) {
392  strncpy(buf + (bufsiz - remaining),"NOPSE40,",remaining);
393  if (strlen("NOPSE40,") > remaining) {
394  buf[bufsiz-1] = '\0';
395  return bufsiz;
396  }
397  else
398  remaining -= strlen("NOPSE40,");
399  }
400  if (flags & ARCH_X86_V2P_LMA) {
401  strncpy(buf + (bufsiz - remaining),"LMA,",remaining);
402  if (strlen("LMA,") > remaining) {
403  buf[bufsiz-1] = '\0';
404  return bufsiz;
405  }
406  else
407  remaining -= strlen("LMA,");
408  }
409  if (flags & ARCH_X86_V2P_PV) {
410  strncpy(buf + (bufsiz - remaining),"PV,",remaining);
411  if (strlen("PV,") > remaining) {
412  buf[bufsiz-1] = '\0';
413  return bufsiz;
414  }
415  else
416  remaining -= strlen("PV,");
417  }
418 
419  if (remaining >= bufsiz)
420  buf[bufsiz - 1] = '\0';
421  else
422  buf[bufsiz - remaining] = '\0';
423 
424  return bufsiz - remaining;
425 }
arch_type_t type
Definition: arch.h:117
#define CR0_PG
int target_arch_x86_v2p(struct target *target, ADDR pgd, ADDR virt, arch_x86_v2p_flags_t flags, ADDR *phys)
#define vwarnopt(level, area, flags, format,...)
Definition: log.h:37
int target_arch_x86_v2p_flags_snprintf(struct target *target, arch_x86_v2p_flags_t flags, char *buf, unsigned int bufsiz)
#define EFER_LMA
#define CR4_PAE
#define verror(format,...)
Definition: log.h:30
int bufsiz
Definition: target_api.h:3297
arch_x86_v2p_flags_t
unsigned char * target_read_physaddr(struct target *target, ADDR paddr, unsigned long length, unsigned char *buf)
Definition: target_api.c:1083
#define PAGE_SHIFT
char * buf
Definition: target_api.h:3298
int target_arch_x86_v2p_get_flags(struct target *target, REGVAL cr0, REGVAL cr4, REGVAL msr_efer, REGVAL cpuid_edx, arch_x86_v2p_flags_t *flags)
#define PTE_PRESENT
#define vdebug(devel, areas, flags, format,...)
Definition: log.h:302
struct arch * arch
Definition: target_api.h:2603
Definition: log.h:70
uint32_t REGVAL
Definition: common.h:66
Definition: arch.h:102
uint32_t ADDR
Definition: common.h:64
Definition: log.h:162
#define PRIxADDR
Definition: common.h:67
#define PTE_PSE