source: mainline/generic/src/mm/as.c@ 5be1923

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5be1923 was 5be1923, checked in by Ondrej Palkovsky <ondrap@…>, 19 years ago

Added simpler userspace starting.

  • Property mode set to 100644
File size: 9.7 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/** Free Adress space */
107void as_free(as_t *as)
108{
109 ASSERT(as->refcount == 0);
110
111 /* TODO: free as_areas and other resources held by as */
112 /* TODO: free page table */
113 free(as);
114}
115
116/** Create address space area of common attributes.
117 *
118 * The created address space area is added to the target address space.
119 *
120 * @param as Target address space.
121 * @param type Type of area.
122 * @param size Size of area in multiples of PAGE_SIZE.
123 * @param base Base address of area.
124 *
125 * @return Address space area on success or NULL on failure.
126 */
127as_area_t *as_area_create(as_t *as, as_area_type_t type, size_t size, __address base)
128{
129 ipl_t ipl;
130 as_area_t *a;
131
132 if (base % PAGE_SIZE)
133 panic("addr not aligned to a page boundary");
134
135 ipl = interrupts_disable();
136 spinlock_lock(&as->lock);
137
138 /*
139 * TODO: test as_area which is to be created doesn't overlap with an existing one.
140 */
141
142 a = (as_area_t *) malloc(sizeof(as_area_t), 0);
143
144 spinlock_initialize(&a->lock, "as_area_lock");
145
146 link_initialize(&a->link);
147 a->type = type;
148 a->size = size;
149 a->base = base;
150
151 list_append(&a->link, &as->as_area_head);
152
153 spinlock_unlock(&as->lock);
154 interrupts_restore(ipl);
155
156 return a;
157}
158
159/** Initialize mapping for one page of address space.
160 *
161 * This functions maps 'page' to 'frame' according
162 * to attributes of the address space area to
163 * wich 'page' belongs.
164 *
165 * @param as Target address space.
166 * @param page Virtual page within the area.
167 * @param frame Physical frame to which page will be mapped.
168 */
169void as_set_mapping(as_t *as, __address page, __address frame)
170{
171 as_area_t *a, *area = NULL;
172 link_t *cur;
173 ipl_t ipl;
174
175 ipl = interrupts_disable();
176 spinlock_lock(&as->lock);
177
178 /*
179 * First, try locate an area.
180 */
181 for (cur = as->as_area_head.next; cur != &as->as_area_head; cur = cur->next) {
182 a = list_get_instance(cur, as_area_t, link);
183 spinlock_lock(&a->lock);
184
185 if ((page >= a->base) && (page < a->base + a->size * PAGE_SIZE)) {
186 area = a;
187 break;
188 }
189
190 spinlock_unlock(&a->lock);
191 }
192
193 if (!area) {
194 panic("page not part of any as_area\n");
195 }
196
197 /*
198 * Note: area->lock is held.
199 */
200
201 page_mapping_insert(as, page, frame, get_area_flags(area));
202
203 spinlock_unlock(&area->lock);
204 spinlock_unlock(&as->lock);
205 interrupts_restore(ipl);
206}
207
208/** Handle page fault within the current address space.
209 *
210 * This is the high-level page fault handler.
211 * Interrupts are assumed disabled.
212 *
213 * @param page Faulting page.
214 *
215 * @return 0 on page fault, 1 on success.
216 */
217int as_page_fault(__address page)
218{
219 link_t *cur;
220 as_area_t *a, *area = NULL;
221 __address frame;
222
223 ASSERT(AS);
224 spinlock_lock(&AS->lock);
225
226 /*
227 * Search this areas of this address space for presence of 'page'.
228 */
229 for (cur = AS->as_area_head.next; cur != &AS->as_area_head; cur = cur->next) {
230 a = list_get_instance(cur, as_area_t, link);
231 spinlock_lock(&a->lock);
232
233 if ((page >= a->base) && (page < a->base + a->size * PAGE_SIZE)) {
234
235 /*
236 * We found the area containing 'page'.
237 * TODO: access checking
238 */
239 area = a;
240 break;
241 }
242
243 spinlock_unlock(&a->lock);
244 }
245
246 if (!area) {
247 /*
248 * No area contained mapping for 'page'.
249 * Signal page fault to low-level handler.
250 */
251 spinlock_unlock(&AS->lock);
252 return 0;
253 }
254
255 /*
256 * Note: area->lock is held.
257 */
258
259 /*
260 * In general, there can be several reasons that
261 * can have caused this fault.
262 *
263 * - non-existent mapping: the area is a scratch
264 * area (e.g. stack) and so far has not been
265 * allocated a frame for the faulting page
266 *
267 * - non-present mapping: another possibility,
268 * currently not implemented, would be frame
269 * reuse; when this becomes a possibility,
270 * do not forget to distinguish between
271 * the different causes
272 */
273 frame = PFN2ADDR(frame_alloc(ONE_FRAME, 0));
274 memsetb(PA2KA(frame), FRAME_SIZE, 0);
275
276 /*
277 * Map 'page' to 'frame'.
278 * Note that TLB shootdown is not attempted as only new information is being
279 * inserted into page tables.
280 */
281 page_mapping_insert(AS, page, frame, get_area_flags(area));
282
283 spinlock_unlock(&area->lock);
284 spinlock_unlock(&AS->lock);
285
286 return 1;
287}
288
289/** Switch address spaces.
290 *
291 * @param old Old address space or NULL.
292 * @param new New address space.
293 */
294void as_switch(as_t *old, as_t *new)
295{
296 ipl_t ipl;
297 bool needs_asid = false;
298
299 ipl = interrupts_disable();
300 spinlock_lock(&as_lock);
301
302 /*
303 * First, take care of the old address space.
304 */
305 if (old) {
306 spinlock_lock(&old->lock);
307 ASSERT(old->refcount);
308 if((--old->refcount == 0) && (old != AS_KERNEL)) {
309 /*
310 * The old address space is no longer active on
311 * any processor. It can be appended to the
312 * list of inactive address spaces with assigned
313 * ASID.
314 */
315 ASSERT(old->asid != ASID_INVALID);
316 list_append(&old->inactive_as_with_asid_link, &inactive_as_with_asid_head);
317 }
318 spinlock_unlock(&old->lock);
319 }
320
321 /*
322 * Second, prepare the new address space.
323 */
324 spinlock_lock(&new->lock);
325 if ((new->refcount++ == 0) && (new != AS_KERNEL)) {
326 if (new->asid != ASID_INVALID)
327 list_remove(&new->inactive_as_with_asid_link);
328 else
329 needs_asid = true; /* defer call to asid_get() until new->lock is released */
330 }
331 SET_PTL0_ADDRESS(new->page_table);
332 spinlock_unlock(&new->lock);
333
334 if (needs_asid) {
335 /*
336 * Allocation of new ASID was deferred
337 * until now in order to avoid deadlock.
338 */
339 asid_t asid;
340
341 asid = asid_get();
342 spinlock_lock(&new->lock);
343 new->asid = asid;
344 spinlock_unlock(&new->lock);
345 }
346 spinlock_unlock(&as_lock);
347 interrupts_restore(ipl);
348
349 /*
350 * Perform architecture-specific steps.
351 * (e.g. write ASID to hardware register etc.)
352 */
353 as_install_arch(new);
354
355 AS = new;
356}
357
358/** Compute flags for virtual address translation subsytem.
359 *
360 * The address space area must be locked.
361 * Interrupts must be disabled.
362 *
363 * @param a Address space area.
364 *
365 * @return Flags to be used in page_mapping_insert().
366 */
367int get_area_flags(as_area_t *a)
368{
369 int flags;
370
371 switch (a->type) {
372 case AS_AREA_TEXT:
373 flags = PAGE_EXEC | PAGE_READ | PAGE_USER | PAGE_PRESENT | PAGE_CACHEABLE;
374 break;
375 case AS_AREA_DATA:
376 case AS_AREA_STACK:
377 flags = PAGE_READ | PAGE_WRITE | PAGE_USER | PAGE_PRESENT | PAGE_CACHEABLE;
378 break;
379 default:
380 panic("unexpected as_area_type_t %d", a->type);
381 }
382
383 return flags;
384}
385
386/** Create page table.
387 *
388 * Depending on architecture, create either address space
389 * private or global page table.
390 *
391 * @param flags Flags saying whether the page table is for kernel address space.
392 *
393 * @return First entry of the page table.
394 */
395pte_t *page_table_create(int flags)
396{
397 ASSERT(as_operations);
398 ASSERT(as_operations->page_table_create);
399
400 return as_operations->page_table_create(flags);
401}
Note: See TracBrowser for help on using the repository browser.