]> Joshua Wise's Git repositories - netwatch.git/blob - lib/demap.c
More ICH2-specific code diked out.
[netwatch.git] / lib / demap.c
1 /* demap.h
2  * Paging lookup functions.
3  * NetWatch system management mode administration console
4  *
5  * Copyright 2009, Google Inc.
6  * All rights reserved.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  * 
12  *     * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following disclaimer
16  * in the documentation and/or other materials provided with the
17  * distribution.
18  *     * Neither the name of Google Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived from
20  * this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,           
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY           
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include <state.h>
36 #include <msr.h>
37 #include <reg-k8-msr.h>
38 #include <reg-x86.h>
39 #include <paging.h>
40 #include <output.h>
41 #include <demap.h>
42
43 #define REG_CS_ATTRIB_L         (1<<9)
44 #define PTE_PRESENT             (1<<0)
45 #define PTE_LARGE               (1<<7)
46
47 #define PTE_FOR(x)      (((unsigned int)(x) >> 12) & 0x3FF)
48 #define PDE_FOR(x)      ((unsigned int)(x) >> 22)
49 #define ADDR_12_MASK(x) ((unsigned int)(x) & ~((1 << 12) - 1))
50
51 /* Keep a memoized copy of the operating mode the CPU was in when we
52  * entered SMM, so we don't have to look it up every time.
53  */
54
55 static enum operating_mode mode;
56
57 void reset_operating_mode_memo() {
58         mode = UNKNOWN;
59 }
60
61 /* Check the saved state map to determine what mode we were in. */
62
63 void probe_operating_mode() {
64         unsigned long cr0;
65
66         if (state_get_type() == SMM_TYPE_64) {
67                 /* This is a 64-bit processor, so we may be in 64-bit
68                  * full or compatibility mode. Check if we are. */
69
70                 uint64_t efer = state_get_reg(STATE_REG_EFER);
71
72                 if (efer & EFER_MSR_LMA) {
73                         /* We are in long mode. Is this full 64-bit, or
74                          * comatibility mode? Check the "L" bit of the
75                          * saved CS descriptor to be sure. */
76
77                         if (state_get_reg(STATE_REG_CS_ATTRIB)
78                             & REG_CS_ATTRIB_L) {
79                                 mode = LONG_64BIT;
80                         } else {
81                                 mode = LONG_COMPAT;
82                         }
83
84                         return;
85                 }
86                 /* Otherwise, we are in legacy mode, so do the normal
87                  * 32-bit probes. */
88         }
89
90         /* Either we are on a 32-bit processor, or we are on a 64-bit
91          * processor in legacy mode. */
92
93         if (state_get_reg(STATE_REG_EFLAGS) & EFLAGS_VM) {
94                 mode = V8086;
95                 return;
96         }
97
98         cr0 = state_get_reg(STATE_REG_CR0);
99
100         if (cr0 & CR0_PE)
101                 if (cr0 & CR0_PG)
102                         mode = PROTECTED_PAGING;
103                 else
104                         mode = PROTECTED_NOPAGING;
105         else
106                 mode = REAL;
107 }
108
109 enum operating_mode get_operating_mode() {
110         if (mode == UNKNOWN)
111                 probe_operating_mode();
112
113         return mode;
114 }
115
116 #define LONG_ADDR_MASK  0x000FFFFFFFFFF000
117 #define PAGE_1G_MASK    0x000FFFFFC0000000
118 #define PAGE_2M_MASK    0x000FFFFFFFE00000
119
120 #define LONG_ADDR_SECTION(addr, offset) ((((addr) >> (offset)) & 0x1FF) * 8)
121
122 #define READ_PHYS_QWORD(pa)     ({ uint64_t * p = p2v(pa); if (!p) return 0; *p; })
123
124 /* Given a virtual address from the current CPU context, determine what the
125  * actual corresponding physical address would be, then convert that back
126  * to a virtual address suitable for use within NetWatch.
127  *
128  * If the given address is not mapped in to RAM or is mapped to RAM which
129  * cannot be accessed, returns null.
130  *
131  * XXX: This currently handles both long and 32-bit modes, but only knows
132  * how to parse standard 4-kbyte pages. It assumes all segments span the full
133  * 32-bit address space. Thus, it will return correct results for most
134  * userspace environments in most sane OS kernels, but not necessarily kernel
135  * space (likely to be mapped with large pages) or anything that plays tricks
136  * with segmentation (like NaCl).
137  */
138
139 uint64_t demap_phys (uint64_t vaddr) {
140
141         uint64_t pa = state_get_reg(STATE_REG_CR3) & LONG_ADDR_MASK;
142         uint64_t entry;
143
144         if (mode == UNKNOWN)
145                 probe_operating_mode();
146
147 outputf("demapping %08x %08x m %d", (uint32_t)(vaddr>>32), (uint32_t)vaddr, mode);
148         switch (mode) {
149         case LONG_64BIT:
150         case LONG_COMPAT: {
151                 /* Get PML4 entry */
152                 entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 39));
153                 if (!(entry & PTE_PRESENT)) return 0;
154                 pa = entry & LONG_ADDR_MASK;
155
156                 /* Get PDP entry */
157                 entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 30));
158                 if (!(entry & PTE_PRESENT)) return 0;
159                 pa = entry & LONG_ADDR_MASK;
160
161                 if (entry & PTE_LARGE)
162                         return (entry & PAGE_1G_MASK) + (vaddr & 0x3FFFFFFF);
163
164                 /* Get PDE */
165                 entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 21));
166                 if (!(entry & PTE_PRESENT)) return 0;
167                 pa = entry & LONG_ADDR_MASK;
168
169                 if (entry & PTE_LARGE)
170                         return (entry & PAGE_2M_MASK) + (vaddr & 0x1FFFFF);
171
172                 /* Get PTE */
173                 entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 12));
174                 if (!(entry & PTE_PRESENT)) return 0;
175                 pa = entry & LONG_ADDR_MASK;
176
177                 return pa + (vaddr & 0xFFF);
178         }
179         case PROTECTED_NOPAGING:
180         case REAL:
181                 return vaddr;
182
183         default: {
184                 unsigned long pde = ((unsigned long *)p2v(pa))[PDE_FOR(vaddr)];
185                 unsigned long pte;
186
187                 if (!(pde & PTE_PRESENT)) return 0; 
188                 pte = ((unsigned long *)p2v(ADDR_12_MASK(pde)))[PTE_FOR(vaddr)];
189                 if (!(pte & PTE_PRESENT)) return 0;
190
191                 return (pte & ~0xFFF) + (vaddr & 0xFFF);
192         }
193         }
194 }
195
196 void *demap(uint64_t vaddr) {
197         uint64_t paddr = demap_phys(vaddr);
198         outputf("demap: paddr 0x%08x %08x", (uint32_t)(paddr>>32), (uint32_t)paddr);
199         if (!paddr) return 0;
200         return p2v(paddr);
201 }
202
This page took 0.03328 seconds and 4 git commands to generate.