1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : /*
8 : * Derived from Linux and Windows versions.
9 : * TODO(jrg): locking is nooped. PrintSelector() #if 0'd.
10 : */
11 :
12 : #include <errno.h>
13 : #include <stdio.h>
14 : #include <string.h>
15 : #include "native_client/src/include/portability.h"
16 : #include "native_client/src/shared/platform/nacl_sync.h"
17 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
18 : #include "native_client/src/trusted/service_runtime/arch/x86/nacl_ldt_x86.h"
19 : /* for LDT_ENTRIES */
20 : #include "native_client/src/trusted/service_runtime/arch/x86/sel_ldr_x86.h"
21 :
22 : /* OSX specific */
23 : #include <architecture/i386/desc.h> /* ldt_desc, etc */
24 : #include <architecture/i386/table.h> /* ldt_entry_t */
25 : #include <architecture/i386/sel.h> /* sel_t */
26 : #include <i386/user_ldt.h> /* i386_set_ldt() */
27 :
28 : /* Buffer used to store the current state of the LDT. */
29 : static ldt_entry_t entries[LDT_ENTRIES];
30 :
31 : /*
32 : * The module initializer and finalizer set up a mutex used to guard LDT
33 : * manipulation.
34 : */
35 : static struct NaClMutex nacl_ldt_mutex;
36 :
37 :
38 14 : int NaClLdtInitPlatformSpecific() {
39 14 : return NaClMutexCtor(&nacl_ldt_mutex);
40 : }
41 :
42 9 : void NaClLdtFiniPlatformSpecific() {
43 9 : NaClMutexDtor(&nacl_ldt_mutex);
44 9 : }
45 :
46 : /*
47 : * Find a free selector. Always invoked while holding nacl_ldt_mutex.
48 : */
49 18 : static int NaClFindUnusedEntryNumber(int start, int refresh_state) {
50 18 : int i, retval = 0;
51 :
52 18 : if (refresh_state) {
53 9 : retval = i386_get_ldt(0, entries, LDT_ENTRIES);
54 : }
55 :
56 18 : if (-1 != retval) {
57 18 : retval = -1; /* In case we don't find any free entry */
58 81 : for (i = start; i < LDT_ENTRIES; ++i) {
59 81 : if (!entries[i].code.present) {
60 18 : retval = i;
61 18 : break;
62 : }
63 : }
64 : }
65 18 : return retval;
66 : }
67 :
68 26 : static uint16_t BuildSelector(int sel_index) {
69 : union {
70 : sel_t sel;
71 : uint16_t uintsel;
72 : } u;
73 :
74 : /*
75 : * Return an LDT selector.
76 : */
77 26 : u.sel.rpl = USER_PRIV;
78 26 : u.sel.ti = SEL_LDT;
79 26 : u.sel.index = sel_index;
80 :
81 26 : return u.uintsel;
82 : }
83 :
84 : /*
85 : * Find and allocate an available selector, inserting an LDT entry with the
86 : * appropriate permissions.
87 : */
88 : uint16_t NaClLdtAllocateSelector(int32_t entry_number,
89 : int size_is_in_pages,
90 : NaClLdtDescriptorType type,
91 : int read_exec_only,
92 : void* base_addr,
93 26 : uint32_t size_minus_one) {
94 : ldt_entry_t ldt;
95 26 : int sel_index = -1;
96 :
97 26 : memset(&ldt, 0, sizeof(ldt));
98 :
99 26 : NaClXMutexLock(&nacl_ldt_mutex);
100 :
101 26 : switch (type) {
102 : case NACL_LDT_DESCRIPTOR_DATA:
103 23 : ldt.data.type = (read_exec_only ? DESC_DATA_RONLY : DESC_DATA_WRITE);
104 23 : break;
105 : case NACL_LDT_DESCRIPTOR_CODE:
106 3 : ldt.code.type = (read_exec_only ? DESC_CODE_EXEC : DESC_CODE_READ);
107 3 : break;
108 : default:
109 0 : goto alloc_error;
110 : }
111 :
112 : /* Ideas from win/nacl_ldt.c */
113 26 : ldt.code.dpl = 3;
114 26 : ldt.code.present = 1;
115 26 : ldt.code.opsz = 1;
116 :
117 26 : if ((size_is_in_pages && ((unsigned long) base_addr & 0xfff)) ||
118 : (size_minus_one > 0xfffff)) {
119 : /*
120 : * The base address needs to be page aligned if page granularity.
121 : * If size is in pages no more than 2**20 pages can be protected.
122 : * If size is in bytes no more than 2**20 bytes can be protected.
123 : */
124 : goto alloc_error;
125 : }
126 26 : ldt.code.base00 = ((unsigned long) base_addr) & 0xffff;
127 26 : ldt.code.base16 = (((unsigned long) base_addr) >> 16) & 0xff;
128 26 : ldt.code.base24 = (((unsigned long) base_addr) >> 24) & 0xff;
129 :
130 26 : ldt.code.limit00 = size_minus_one & 0xffff;
131 26 : ldt.code.limit16 = (size_minus_one >> 16) & 0xf;
132 26 : ldt.code.granular = (size_is_in_pages ? DESC_GRAN_PAGE : DESC_GRAN_BYTE);
133 :
134 : /*
135 : * Install the LDT entry.
136 : */
137 26 : if (-1 == entry_number) {
138 : /* -1 means caller did not specify -- allocate the first available. */
139 9 : entry_number = NaClFindUnusedEntryNumber(0, 1);
140 36 : while ((-1 == sel_index) && (-1 != entry_number)) {
141 18 : sel_index = i386_set_ldt(entry_number, &ldt, 1);
142 18 : if (-1 == sel_index) {
143 : /*
144 : * For some reason we failed to allocate the LDT entry - try the next
145 : * available one.
146 : */
147 9 : entry_number = NaClFindUnusedEntryNumber(entry_number + 1, 0);
148 : }
149 : }
150 : } else {
151 : /* The caller specified an entry number - try only this one. */
152 17 : sel_index = i386_set_ldt(entry_number, &ldt, 1);
153 : }
154 :
155 26 : if (-1 == sel_index) {
156 0 : goto alloc_error;
157 : }
158 :
159 26 : NaClXMutexUnlock(&nacl_ldt_mutex);
160 :
161 26 : return BuildSelector(sel_index);
162 :
163 : /*
164 : * All error returns go through this epilog.
165 : */
166 0 : alloc_error:
167 0 : NaClXMutexUnlock(&nacl_ldt_mutex);
168 0 : return 0;
169 : }
170 :
171 : /*
172 : * Allocates a selector whose size is specified in pages.
173 : * Page granular protection requires that the start address be page-aligned.
174 : */
175 : uint16_t NaClLdtAllocatePageSelector(NaClLdtDescriptorType type,
176 : int read_exec_only,
177 : void* base_addr,
178 6 : uint32_t size_in_pages) {
179 6 : return NaClLdtAllocateSelector(-1, 1, type, read_exec_only, base_addr,
180 : size_in_pages - 1);
181 : }
182 :
183 : /*
184 : * Allocates a selector whose size is specified in bytes.
185 : */
186 : uint16_t NaClLdtAllocateByteSelector(NaClLdtDescriptorType type,
187 : int read_exec_only,
188 : void* base_addr,
189 3 : uint32_t size_in_bytes) {
190 3 : return NaClLdtAllocateSelector(-1, 0, type, read_exec_only, base_addr,
191 : size_in_bytes - 1);
192 : }
193 :
194 : /*
195 : * Changes a selector whose size is specified in pages.
196 : * Page granular protection requires that the start address be page-aligned.
197 : */
198 : uint16_t NaClLdtChangePageSelector(int32_t entry_number,
199 : NaClLdtDescriptorType type,
200 : int read_exec_only,
201 : void* base_addr,
202 0 : uint32_t size_in_pages) {
203 0 : if ((uint32_t) entry_number >= LDT_ENTRIES) {
204 0 : return 0;
205 : }
206 0 : return NaClLdtAllocateSelector(entry_number, 1, type, read_exec_only,
207 : base_addr, size_in_pages - 1);
208 : }
209 :
210 : /*
211 : * Changes a selector whose size is specified in bytes.
212 : */
213 : uint16_t NaClLdtChangeByteSelector(int32_t entry_number,
214 : NaClLdtDescriptorType type,
215 : int read_exec_only,
216 : void* base_addr,
217 3 : uint32_t size_in_bytes) {
218 3 : if ((uint32_t) entry_number >= LDT_ENTRIES) {
219 0 : return 0;
220 : }
221 3 : return NaClLdtAllocateSelector(entry_number, 0, type, read_exec_only,
222 : base_addr, size_in_bytes - 1);
223 : }
224 :
225 :
226 0 : void NaClLdtPrintSelector(uint16_t selector) {
227 : /* TODO(jrg) */
228 : #if 0
229 : /* type_name converts the segment type into print name */
230 : static const char* type_name[] = {
231 : "data read only",
232 : "data read_only accessed",
233 : "data read write",
234 : "data read write accessed",
235 : "data read expand",
236 : "data read expand accessed",
237 : "data read write expand",
238 : "data read write expand accessed",
239 : "code execute",
240 : "code execute accessed",
241 : "code execute read",
242 : "code execute read accessed",
243 : "code execute conforming",
244 : "code execute conforming accessed",
245 : "code execute read conforming",
246 : "code execute read conforming accessed"
247 : };
248 :
249 : struct LdtEntry entries[LDT_ENTRIES];
250 : struct LdtEntry entry;
251 : int retval = modify_ldt(0, entries, sizeof(entries));
252 : if (-1 == retval) {
253 : return;
254 : }
255 : entry = entries[selector >> 3];
256 : printf("DESCRIPTOR for selector %04x\n", selector);
257 : /* create functions to do base, limit, and type */
258 : printf(" base %08x\n", (entry.base_24to31 << 24)
259 : | (entry.base_16to23 << 16) | entry.base_00to15);
260 : printf(" limit %08x%s\n",
261 : (((entry.limit_16to19 << 16) | entry.limit_00to15)
262 : << (entry.granularity ? 12 : 0)),
263 : (entry.granularity ? " (page granularity)" : ""));
264 : printf(" type %s, %s\n", ((entry.type & 0x10) ? "user" : "system"),
265 : type_name[entry.type & 0xf]);
266 : printf(" privilege %d\n", entry.descriptor_privilege);
267 : printf(" present %s\n", (entry.present ? "yes" : "no"));
268 : printf(" available %s\n", (entry.available ? "yes" : "no"));
269 : printf(" 64-bit code %s\n", (entry.code_64_bit ? "yes" : "no"));
270 : printf(" op size %s\n", (entry.op_size_32 ? "32" : "16"));
271 : #else
272 : UNREFERENCED_PARAMETER(selector);
273 : #endif
274 0 : }
275 :
276 : /*
277 : * Mark a selector as available for future reuse.
278 : */
279 3 : void NaClLdtDeleteSelector(uint16_t selector) {
280 :
281 3 : NaClXMutexLock(&nacl_ldt_mutex);
282 3 : if (-1 == i386_set_ldt(selector >> 3, NULL, 1)) {
283 0 : perror("NaClLdtDeleteSelector: i386_set_ldt()");
284 : }
285 3 : NaClXMutexUnlock(&nacl_ldt_mutex);
286 3 : }
|