]>
Commit | Line | Data |
---|---|---|
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 |