source: mainline/generic/src/mm/as.c@ 7e4e532

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7e4e532 was 7e4e532, checked in by Jakub Jermar <jakub@…>, 19 years ago

Reimplement ASID stealing logic.
This time, hopefully, with correct synchronization.

  • Property mode set to 100644
File size: 9.5 KB
Line 
1/*
2 * Copyright (C) 2001-2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * This file contains address space manipulation functions.
31 * Roughly speaking, this is a higher-level client of
32 * Virtual Address Translation (VAT) subsystem.
33 */
34
35#include <mm/as.h>
36#include <arch/mm/as.h>
37#include <mm/page.h>
38#include <mm/frame.h>
39#include <mm/slab.h>
40#include <mm/tlb.h>
41#include <arch/mm/page.h>
42#include <genarch/mm/page_pt.h>
43#include <mm/asid.h>
44#include <arch/mm/asid.h>
45#include <arch/types.h>
46#include <typedefs.h>
47#include <synch/spinlock.h>
48#include <config.h>
49#include <adt/list.h>
50#include <panic.h>
51#include <arch/asm.h>
52#include <debug.h>
53#include <memstr.h>
54#include <arch.h>
55#include <print.h>
56
57as_operations_t *as_operations = NULL;
58
59/** Address space lock. It protects inactive_as_with_asid_head. */
60SPINLOCK_INITIALIZE(as_lock);
61
62/**
63 * This list contains address spaces that are not active on any
64 * processor and that have valid ASID.
65 */
66LIST_INITIALIZE(inactive_as_with_asid_head);
67
68/** Kernel address space. */
69as_t *AS_KERNEL = NULL;
70
71static int get_area_flags(as_area_t *a);
72
73/** Initialize address space subsystem. */
74void as_init(void)
75{
76 as_arch_init();
77 AS_KERNEL = as_create(FLAG_AS_KERNEL);
78 if (!AS_KERNEL)
79 panic("can't create kernel address space\n");
80}
81
82/** Create address space.
83 *
84 * @param flags Flags that influence way in wich the address space is created.
85 */
86as_t *as_create(int flags)
87{
88 as_t *as;
89
90 as = (as_t *) malloc(sizeof(as_t), 0);
91 link_initialize(&as->inactive_as_with_asid_link);
92 spinlock_initialize(&as->lock, "as_lock");
93 list_initialize(&as->as_area_head);
94
95 if (flags & FLAG_AS_KERNEL)
96 as->asid = ASID_KERNEL;
97 else
98 as->asid = ASID_INVALID;
99
100 as->refcount = 0;
101 as->page_table = page_table_create(flags);
102
103 return as;
104}
105
106/** Create address space area of common attributes.
107 *
108 * The created address space area is added to the target address space.
109 *
110 * @param as Target address space.
111 * @param type Type of area.
112 * @param size Size of area in multiples of PAGE_SIZE.
113 * @param base Base address of area.
114 *
115 * @return Address space area on success or NULL on failure.
116 */
117as_area_t *as_area_create(as_t *as, as_area_type_t type, size_t size, __address base)
118{
119 ipl_t ipl;
120 as_area_t *a;
121
122 if (base % PAGE_SIZE)
123 panic("addr not aligned to a page boundary");
124
125 ipl = interrupts_disable();
126 spinlock_lock(&as->lock);
127
128 /*
129 * TODO: test as_area which is to be created doesn't overlap with an existing one.
130 */
131
132 a = (as_area_t *) malloc(sizeof(as_area_t), 0);
133
134 spinlock_initialize(&a->lock, "as_area_lock");
135
136 link_initialize(&a->link);
137 a->type = type;
138 a->size = size;
139 a->base = base;
140
141 list_append(&a->link, &as->as_area_head);
142
143 spinlock_unlock(&as->lock);
144 interrupts_restore(ipl);
145
146 return a;
147}
148
149/** Initialize mapping for one page of address space.
150 *
151 * This functions maps 'page' to 'frame' according
152 * to attributes of the address space area to
153 * wich 'page' belongs.
154 *
155 * @param a Target address space.
156 * @param page Virtual page within the area.
157 * @param frame Physical frame to which page will be mapped.
158 */
159void as_set_mapping(as_t *as, __address page, __address frame)
160{
161 as_area_t *a, *area = NULL;
162 link_t *cur;
163 ipl_t ipl;
164
165 ipl = interrupts_disable();
166 spinlock_lock(&as->lock);
167
168 /*
169 * First, try locate an area.
170 */
171 for (cur = as->as_area_head.next; cur != &as->as_area_head; cur = cur->next) {
172 a = list_get_instance(cur, as_area_t, link);
173 spinlock_lock(&a->lock);
174
175 if ((page >= a->base) && (page < a->base + a->size * PAGE_SIZE)) {
176 area = a;
177 break;
178 }
179
180 spinlock_unlock(&a->lock);
181 }
182
183 if (!area) {
184 panic("page not part of any as_area\n");
185 }
186
187 /*
188 * Note: area->lock is held.
189 */
190
191 page_mapping_insert(as, page, frame, get_area_flags(area));
192
193 spinlock_unlock(&area->lock);
194 spinlock_unlock(&as->lock);
195 interrupts_restore(ipl);
196}
197
198/** Handle page fault within the current address space.
199 *
200 * This is the high-level page fault handler.
201 * Interrupts are assumed disabled.
202 *
203 * @param page Faulting page.
204 *
205 * @return 0 on page fault, 1 on success.
206 */
207int as_page_fault(__address page)
208{
209 link_t *cur;
210 as_area_t *a, *area = NULL;
211 __address frame;
212
213 ASSERT(AS);
214 spinlock_lock(&AS->lock);
215
216 /*
217 * Search this areas of this address space for presence of 'page'.
218 */
219 for (cur = AS->as_area_head.next; cur != &AS->as_area_head; cur = cur->next) {
220 a = list_get_instance(cur, as_area_t, link);
221 spinlock_lock(&a->lock);
222
223 if ((page >= a->base) && (page < a->base + a->size * PAGE_SIZE)) {
224
225 /*
226 * We found the area containing 'page'.
227 * TODO: access checking
228 */
229 area = a;
230 break;
231 }
232
233 spinlock_unlock(&a->lock);
234 }
235
236 if (!area) {
237 /*
238 * No area contained mapping for 'page'.
239 * Signal page fault to low-level handler.
240 */
241 spinlock_unlock(&AS->lock);
242 return 0;
243 }
244
245 /*
246 * Note: area->lock is held.
247 */
248
249 /*
250 * In general, there can be several reasons that
251 * can have caused this fault.
252 *
253 * - non-existent mapping: the area is a scratch
254 * area (e.g. stack) and so far has not been
255 * allocated a frame for the faulting page
256 *
257 * - non-present mapping: another possibility,
258 * currently not implemented, would be frame
259 * reuse; when this becomes a possibility,
260 * do not forget to distinguish between
261 * the different causes
262 */
263 frame = PFN2ADDR(frame_alloc(ONE_FRAME, 0));
264 memsetb(PA2KA(frame), FRAME_SIZE, 0);
265
266 /*
267 * Map 'page' to 'frame'.
268 * Note that TLB shootdown is not attempted as only new information is being
269 * inserted into page tables.
270 */
271 page_mapping_insert(AS, page, frame, get_area_flags(area));
272
273 spinlock_unlock(&area->lock);
274 spinlock_unlock(&AS->lock);
275
276 return 1;
277}
278
279/** Switch address spaces.
280 *
281 * @param old Old address space or NULL.
282 * @param new New address space.
283 */
284void as_switch(as_t *old, as_t *new)
285{
286 ipl_t ipl;
287 bool needs_asid = false;
288
289 ipl = interrupts_disable();
290 spinlock_lock(&as_lock);
291
292 /*
293 * First, take care of the old address space.
294 */
295 if (old) {
296 spinlock_lock(&old->lock);
297 ASSERT(old->refcount);
298 if((--old->refcount == 0) && (old != AS_KERNEL)) {
299 /*
300 * The old address space is no longer active on
301 * any processor. It can be appended to the
302 * list of inactive address spaces with assigned
303 * ASID.
304 */
305 ASSERT(old->asid != ASID_INVALID);
306 list_append(&old->inactive_as_with_asid_link, &inactive_as_with_asid_head);
307 }
308 spinlock_unlock(&old->lock);
309 }
310
311 /*
312 * Second, prepare the new address space.
313 */
314 spinlock_lock(&new->lock);
315 if ((new->refcount++ == 0) && (new != AS_KERNEL)) {
316 if (new->asid != ASID_INVALID)
317 list_remove(&new->inactive_as_with_asid_link);
318 else
319 needs_asid = true; /* defer call to asid_get() until new->lock is released */
320 }
321 SET_PTL0_ADDRESS(new->page_table);
322 spinlock_unlock(&new->lock);
323
324 if (needs_asid) {
325 /*
326 * Allocation of new ASID was deferred
327 * until now in order to avoid deadlock.
328 */
329 asid_t asid;
330
331 asid = asid_get();
332 spinlock_lock(&new->lock);
333 new->asid = asid;
334 spinlock_unlock(&new->lock);
335 }
336 spinlock_unlock(&as_lock);
337 interrupts_restore(ipl);
338
339 /*
340 * Perform architecture-specific steps.
341 * (e.g. write ASID to hardware register etc.)
342 */
343 as_install_arch(new);
344
345 AS = new;
346}
347
348/** Compute flags for virtual address translation subsytem.
349 *
350 * The address space area must be locked.
351 * Interrupts must be disabled.
352 *
353 * @param a Address space area.
354 *
355 * @return Flags to be used in page_mapping_insert().
356 */
357int get_area_flags(as_area_t *a)
358{
359 int flags;
360
361 switch (a->type) {
362 case AS_AREA_TEXT:
363 flags = PAGE_EXEC | PAGE_READ | PAGE_USER | PAGE_PRESENT | PAGE_CACHEABLE;
364 break;
365 case AS_AREA_DATA:
366 case AS_AREA_STACK:
367 flags = PAGE_READ | PAGE_WRITE | PAGE_USER | PAGE_PRESENT | PAGE_CACHEABLE;
368 break;
369 default:
370 panic("unexpected as_area_type_t %d", a->type);
371 }
372
373 return flags;
374}
375
376/** Create page table.
377 *
378 * Depending on architecture, create either address space
379 * private or global page table.
380 *
381 * @param flags Flags saying whether the page table is for kernel address space.
382 *
383 * @return First entry of the page table.
384 */
385pte_t *page_table_create(int flags)
386{
387 ASSERT(as_operations);
388 ASSERT(as_operations->page_table_create);
389
390 return as_operations->page_table_create(flags);
391}
Note: See TracBrowser for help on using the repository browser.