tlb.c
11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
/*
* Copyright (C) 1998 by the Board of Trustees
* of Leland Stanford Junior University.
* Copyright (C) 1998 Digital Equipment Corporation
*
* This file is part of the SimOS distribution.
* See LICENSE file for terms of the license.
*
*/
/*
* The TLB miss handler and related MMU simulation routines
*/
#include <assert.h>
#include "sim_error.h"
#include "gamma.h"
#include "thread.h"
#include "protos.h"
#include "header.h"
#include "sim_error.h"
#include "gamma.h"
/*
* Only a functional simulation of the virtual to physical translation is
* done here. The page table is simulated as a hash table, the key being the
* lower bits of the virtual address.
*
* The special argument mmap indicates that if there is no mapping on a
* mmap address, a new one should be introduced. Normally, this will be
* considered as an error.
*/
ulong
tlb_miss (thread_ptr pthread, long addr,
long vpage, long tbkey, long tbtag, int mmap_create_map)
{
page_t *pg;
struct header *h = pthread->headerp;
/* Do a page table lookup */
pg = pthread->page_bucket[tbkey];
for (; pg; pg = pg->next) {
if (pg->tag == tbtag) {
pthread->page_bucket[tbkey]->lookaside = pg;
return ((ulong) pg->page + TB_OFFSET (addr));
}
}
/* If control comes here, that means the page table lookup failed and
* that there is no mapping for this virtual address. The page table
* lookup and the fault handler routines have been merged for the sake of
* simulation efficiency.
*
* Actions taken:
* Check if address is private or shared.
* If shared, look up global table. This WILL return phys_addr.
* If private, allocate here.
*/
if IS_SHARED (addr) {
return map_shared (pthread, addr);
}
if ((addr >= MMAP_AREA_START)
&& (addr < pthread->mmap_size + MMAP_AREA_START)
&& (mmap_create_map == 0)) {
/* The mapping couldn't be introduced */
CPUError("Couldn't map the page at 0x%lx (in area)\n", addr);
ASSERT(0);
return -1;
}
/*
* The page is private. Check if the address falls in the data segment
*/
if ((addr >= h->data_start) &&
(addr < h->data_start + h->total_data_size)
|| ( (addr >= MMAP_AREA_START)
&& (addr < pthread->mmap_size + MMAP_AREA_START))) {
pg = (page_t *) malloc (sizeof (page_t));
if (pg == NULL) {
fatal ("addr2phys: cannot allocate private page table entry\n");
}
/*
* Introduce a new mapping, which goes in right at the beginning of
* the list
*/
pg->next = pthread->page_bucket[tbkey];
pthread->page_bucket[tbkey] = pg;
pg->tag = tbtag;
/* Allocate simulated physical memory - i.e. a chunk of AINT heap */
pg->page = (void *) mmap ((caddr_t) NULL, TB_PAGESIZE,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_VARIABLE
| MAP_PRIVATE, -1, 0);
if (pg->page == (caddr_t) (-1))
fatal ("addr2phys: cannot allocate private page\n");
/* mark the page private */
pg->flags = PAGE_PRIVATE;
/* Introduce a TLB entry */
pthread->page_bucket[tbkey]->lookaside = pg;
/* Hack to speed-up copy_addr_space at fork-time */
pthread->num_pages++;
pthread->num_private++;
return ((ulong) pg->page + TB_OFFSET (addr));
}
/* Does it fall in any of the other read only segments ? */
if ((addr >= h->rdata_start &&
addr < ( h->rdata_start + h->rdata_size)) ||
(addr >= h->rconst_start &&
addr < ( h->rconst_start + h->rconst_size)) ||
(addr >= h->pdata_start
&& addr < ( h->pdata_start + h->pdata_size))) {
/* Allocate page and map as above */
pg = (page_t *) malloc (sizeof (page_t));
if (pg == NULL) {
fatal ("addr2phys: cannot allocate page table "
"entry for rdonly segment \n");
}
/* Introduce the mapping into the hash table */
pg->next = pthread->page_bucket[tbkey];
pthread->page_bucket[tbkey] = pg;
pg->tag = tbtag;
/* Allocate simulated physical page */
pg->page = (void *) mmap ((caddr_t) NULL, TB_PAGESIZE,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_VARIABLE
| MAP_PRIVATE, -1, 0);
if (pg->page == (caddr_t) (-1))
fatal ("addr2phys: cannot allocate page for rdonly segment\n");
/* mark it private */
pg->flags = PAGE_PRIVATE;
/* Introduce a TLB entry */
pthread->page_bucket[tbkey]->lookaside = pg;
/* Hack to speed-up copy_addr_space at fork-time */
pthread->num_pages++;
pthread->num_private++;
return ((ulong) pg->page + TB_OFFSET (addr));
}
/* The mapping couldn't be introduced */
CPUError("Couldn't map the page at %x --tail\n", addr);
ASSERT(0);
return -1;
}
#ifdef SOLO
ulong text_tlb_miss(thread_ptr pthread, long addr,
long vpage, long tbkey, long tbtag, int mmap_create_map)
{
page_t *pg;
struct header *h = pthread->headerp;
/* Do a page table lookup */
pg = pthread->page_bucket[tbkey];
for (; pg; pg = pg->next) {
if (pg->tag == tbtag) {
pthread->page_bucket[tbkey]->lookaside = pg;
return ((ulong) pg->page + TB_OFFSET (addr));
}
}
CPUPrint("text_tlb_miss %x \n",vpage);
pg = (page_t *) malloc (sizeof (page_t));
if (pg == NULL) {
fatal ("addr2phys: cannot allocate private page table entry\n");
}
/*
* Introduce a new mapping, which goes in right at the beginning of
* the list
*/
pg->next = pthread->page_bucket[tbkey];
pthread->page_bucket[tbkey] = pg;
pg->tag = tbtag;
/* Allocate simulated physical memory - i.e. a chunk of AINT heap */
pg->page = (void *) mmap ((caddr_t) NULL, TB_PAGESIZE,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_VARIABLE
| MAP_PRIVATE, -1, 0);
if (pg->page == (caddr_t) (-1))
fatal ("addr2phys: cannot allocate private page\n");
/* mark the page private */
pg->flags = PAGE_PRIVATE;
/* Introduce a TLB entry */
pthread->page_bucket[tbkey]->lookaside = pg;
/* Hack to speed-up copy_addr_space at fork-time */
pthread->num_pages++;
pthread->num_private++;
return ((ulong) pg->page + TB_OFFSET (addr));
}
#endif /* GAMMA */
/*
* Handles introducing the correct mapping into the TLB for shared memory
* regions
*/
ulong
map_shared (thread_ptr pthread, long address)
{
int shmid;
struct shm_descriptor *shm_ds;
long glob_addr;
register long vpage, tbkey, tbtag;
register struct page_table_entry *sh_pg;
struct page_table_entry *pg;
/* Find out the shmid from the Shmem_regions list */
for (shm_ds = pthread->shmem_regions;
shm_ds;
shm_ds = shm_ds->next) {
if ((address >= shm_ds->addr)
&& (address < shm_ds->addr + shm_ds->size))
break;
}
/* If not found - invalid address. Return null */
if (shm_ds == NULL) {
/* The mapping couldn't be introduced */
CPUError("Couldn't map the page at %lx\n (shm_ds) ", address);
return -1;
}
/* Found. Now construct the global lookup address */
shmid = shm_ds->shmid;
glob_addr = shm_seg[shmid].addr + (address - shm_ds->addr);
/* Look up the global TB */
pg = (struct page_table_entry *)
malloc (sizeof (struct page_table_entry));
if (pg == NULL)
fatal ("addr2phys: cannot allocate local page-table-entry "
"(shared seg)\n");
vpage = TB_VPAGE (glob_addr);
tbkey = TB_KEY (vpage);
tbtag = TB_TAG (vpage);
for (sh_pg = shmem_page_table[tbkey]; sh_pg; sh_pg = sh_pg->next) {
if (sh_pg->tag == tbtag)
break;
}
if (sh_pg) {
/* Hit in the global TB. Enter in local TB and get out of here */
pg->tag = TB_TAG (TB_VPAGE (address));
pg->page = sh_pg->page;
pg->next = pthread->page_bucket[TB_KEY (TB_VPAGE (address))];
pg->flags = PAGE_SHARED;
pthread->page_bucket[TB_KEY (TB_VPAGE (address))] = pg;
pg->lookaside = pg;
pthread->num_pages++;
return ((long) pg->page + TB_OFFSET (address));
}
/* If we are here, then we did not find the page in the global TB */
/* Allocate the page table entry */
sh_pg = (struct page_table_entry *)
malloc (sizeof (struct page_table_entry));
if (sh_pg == NULL)
fatal ("addr2phys: cannot allocate "
"shared-page-table entry (global)\n");
/* Make an entry in the global page table */
sh_pg->next = shmem_page_table[tbkey];
shmem_page_table[tbkey] = sh_pg;
sh_pg->tag = tbtag;
sh_pg->page = mmap ((caddr_t) NULL, TB_PAGESIZE,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_VARIABLE | MAP_PRIVATE,
-1, 0);
if (sh_pg->page == (caddr_t) (-1))
fatal ("addr2phys: cannot allocate shared page\n");
/* make an entry in the per thread page table */
vpage = TB_VPAGE (address);
pg->tag = TB_TAG (vpage);
pg->page = sh_pg->page;
pg->next = pthread->page_bucket[TB_KEY (vpage)];
pg->flags = PAGE_SHARED;
pthread->page_bucket[TB_KEY (vpage)] = pg;
pg->lookaside = pg;
pthread->num_pages++;
return ((long) pg->page + TB_OFFSET (address));
}
/*
* same as tlb_miss, except that it uses a file descriptor and maps a page of
* the file into the virtual address space of pthread.
*/
ulong
mmap_tlb_miss (thread_ptr pthread, long addr, long vpage, long tbkey,
long tbtag, long len, int prot, int pfd, int flags,
off_t offset)
{
page_t *pg;
struct header *h = pthread->headerp;
/* Do a page table lookup */
pg = pthread->page_bucket[tbkey];
for (; pg; pg = pg->next) {
if (pg->tag == tbtag) {
pthread->page_bucket[tbkey]->lookaside = pg;
return ((ulong) pg->page + TB_OFFSET (addr));
}
}
/*
* The page is private. Check if the address falls in the data segment
*/
if ((addr >= MMAP_AREA_START)
&& (addr < pthread->mmap_size + MMAP_AREA_START)) {
pg = (page_t *) malloc (sizeof (page_t));
if (pg == NULL) {
fatal ("addr2phys: cannot allocate private page table entry\n");
}
/*
* Introduce a new mapping, which goes in right at the beginning of
* the list
*/
pg->next = pthread->page_bucket[tbkey];
pthread->page_bucket[tbkey] = pg;
pg->tag = tbtag;
/* Allocate a mmaped file page */
pg->page = (void *) mmap ((caddr_t) NULL, len,
prot,
flags, pfd, offset);
if (pg->page == (caddr_t) (-1))
fatal ("addr2phys: cannot allocate private page\n");
/* mark the page private */
pg->flags = PAGE_PRIVATE;
/* Introduce a TLB entry */
pthread->page_bucket[tbkey]->lookaside = pg;
/* Hack to speed-up copy_addr_space at fork-time */
pthread->num_pages++;
pthread->num_private++;
return ((ulong) pg->page + TB_OFFSET (addr));
}
/* The mapping couldn't be introduced */
CPUError("Couldn't map the page at %x (mapping\n", addr);
return -1;
}
/*
* Checks if a given page is already mapped.
*/
is_mapped(thread_ptr pthread, long addr,
long vpage, long tbkey, long tbtag)
{
page_t *pg;
/* Do a page table lookup */
pg = pthread->page_bucket[tbkey];
for (; pg; pg = pg->next) {
if (pg->tag == tbtag) {
return 1;
}
}
return 0;
}
/*
* Unmaps the page containing the given address
*/
void
page_unmap(thread_ptr pthread, ulong address)
{
ulong page, tag, key;
page_t *entry, *prev;
/* extract the relevant fields from the address */
page = TB_VPAGE(address);
tag = TB_TAG(page);
key = TB_KEY(page);
entry = pthread->page_bucket[key];
prev = entry;
/* walk through the pages in this bucket */
for (; entry; prev = entry, entry = entry->next) {
/* do a tag comparision */
if (entry->tag == tag) {
/* found it, now unmap the page */
/* get it out of the list */
if (entry != prev)
/* short circuit */
prev->next = entry->next;
else
/* this must me the first entry */
pthread->page_bucket[key] = entry->next;
if (!IS_SHARED(address)) {
/* unmap the page if it is not shared */
if ( munmap(entry->page, TB_PAGESIZE) == -1 )
perror("munmap");
}
/* and release the entry */
free(entry);
break;
}
}
}