Statistics
| Branch: | Revision:

root / prex-0.9.0 / bsp / hal / x86 / arch / mmu.c @ 03e9c04a

History | View | Annotate | Download (7.33 KB)

1
/*-
2
 * Copyright (c) 2005-2009, Kohsuke Ohtani
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
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

    
30
/*
31
 * mmu.c - memory management unit support routines
32
 */
33

    
34
/*
35
 * This module provides virtual/physical address translation for
36
 * intel x86 MMU. This kernel will do only page level translation
37
 * and protection and it does not use x86 segment mechanism.
38
 */
39

    
40
#include <machine/syspage.h>
41
#include <kernel.h>
42
#include <page.h>
43
#include <mmu.h>
44
#include <cpu.h>
45
#include <cpufunc.h>
46

    
47
/*
48
 * Boot page directory.
49
 * This works as a template for all page directory.
50
 */
51
static pgd_t boot_pgd = (pgd_t)BOOT_PGD;
52

    
53
/*
54
 * Map physical memory range into virtual address
55
 *
56
 * Returns 0 on success, or ENOMEM on failure.
57
 *
58
 * Map type can be one of the following type.
59
 *   PG_UNMAP  - Remove mapping
60
 *   PG_READ   - Read only mapping
61
 *   PG_WRITE  - Read/write allowed
62
 *   PG_KERNEL - Kernel page
63
 *   PG_IO     - I/O memory
64
 *
65
 * Setup the appropriate page tables for mapping. If there is no
66
 * page table for the specified address, new page table is
67
 * allocated.
68
 *
69
 * This routine does not return any error even if the specified
70
 * address has been already mapped to other physical address.
71
 * In this case, it will just override the existing mapping.
72
 *
73
 * In order to unmap the page, pg_type is specified as 0.  But,
74
 * the page tables are not released even if there is no valid
75
 * page entry in it. All page tables are released when mmu_delmap()
76
 * is called when task is terminated.
77
 *
78
 * TODO: TLB should be flushed for specific page by invalpg in
79
 * case of i486.
80
 */
81
int
82
mmu_map(pgd_t pgd, paddr_t pa, vaddr_t va, size_t size, int type)
83
{
84
        uint32_t pte_flag = 0;
85
        uint32_t pde_flag = 0;
86
        pte_t pte;
87
        paddr_t pg;
88

    
89
        pa = round_page(pa);
90
        va = round_page(va);
91
        size = trunc_page(size);
92

    
93
        /*
94
         * Set page flag
95
         */
96
        switch (type) {
97
        case PG_UNMAP:
98
                pte_flag = 0;
99
                pde_flag = (uint32_t)(PDE_PRESENT | PDE_WRITE | PDE_USER);
100
                break;
101
        case PG_READ:
102
                pte_flag = (uint32_t)(PTE_PRESENT | PTE_USER);
103
                pde_flag = (uint32_t)(PDE_PRESENT | PDE_WRITE | PDE_USER);
104
                break;
105
        case PG_WRITE:
106
                pte_flag = (uint32_t)(PTE_PRESENT | PTE_WRITE | PTE_USER);
107
                pde_flag = (uint32_t)(PDE_PRESENT | PDE_WRITE | PDE_USER);
108
                break;
109
        case PG_SYSTEM:
110
                pde_flag = (uint32_t)(PDE_PRESENT | PDE_WRITE);
111
                pte_flag = (uint32_t)(PTE_PRESENT | PTE_WRITE);
112
                break;
113
        case PG_IOMEM:
114
                pde_flag = (uint32_t)(PDE_PRESENT | PDE_WRITE);
115
                pte_flag = (uint32_t)(PTE_PRESENT | PTE_WRITE | PTE_NCACHE);
116
                break;
117
        default:
118
                panic("mmu_map");
119
        }
120
        /*
121
         * Map all pages
122
         */
123
        while (size > 0) {
124
                if (pte_present(pgd, va)) {
125
                        /* Page table already exists for the address */
126
                        pte = vtopte(pgd, va);
127
                } else {
128
                        ASSERT(pte_flag != 0);
129
                        if ((pg = page_alloc(PAGE_SIZE)) == 0) {
130
                                DPRINTF(("Error: MMU mapping failed\n"));
131
                                return ENOMEM;
132
                        }
133
                        pgd[PAGE_DIR(va)] = (uint32_t)pg | pde_flag;
134
                        pte = (pte_t)ptokv(pg);
135
                        memset(pte, 0, PAGE_SIZE);
136
                }
137
                /* Set new entry into page table */
138
                pte[PAGE_TABLE(va)] = (uint32_t)pa | pte_flag;
139

    
140
                /* Process next page */
141
                pa += PAGE_SIZE;
142
                va += PAGE_SIZE;
143
                size -= PAGE_SIZE;
144
        }
145
        flush_tlb();
146
        return 0;
147
}
148

    
149
/*
150
 * Create new page map.
151
 *
152
 * Returns a page directory on success, or NULL on failure.  This
153
 * routine is called when new task is created. All page map must
154
 * have the same kernel page table in it. So, the kernel page
155
 * tables are copied to newly created map.
156
 */
157
pgd_t
158
mmu_newmap(void)
159
{
160
        paddr_t pg;
161
        pgd_t pgd;
162
        int i;
163

    
164
        /* Allocate page directory */
165
        if ((pg = page_alloc(PAGE_SIZE)) == 0)
166
                return NO_PGD;
167
        pgd = (pgd_t)ptokv(pg);
168
        memset(pgd, 0, PAGE_SIZE);
169

    
170
        /* Copy kernel page tables */
171
        i = PAGE_DIR(KERNBASE);
172
        memcpy(&pgd[i], &boot_pgd[i], (size_t)(1024 - i));
173
        return pgd;
174
}
175

    
176
/*
177
 * Terminate all page mapping.
178
 */
179
void
180
mmu_terminate(pgd_t pgd)
181
{
182
        int i;
183
        pte_t pte;
184

    
185
        flush_tlb();
186

    
187
        /* Release all user page table */
188
        for (i = 0; i < PAGE_DIR(KERNBASE); i++) {
189
                pte = (pte_t)pgd[i];
190
                if (pte != 0)
191
                        page_free((paddr_t)((paddr_t)pte & PTE_ADDRESS),
192
                                  PAGE_SIZE);
193
        }
194
        /* Release page directory */
195
        page_free(kvtop(pgd), PAGE_SIZE);
196
}
197

    
198
/*
199
 * Switch to new page directory
200
 *
201
 * This is called when context is switched.
202
 * Whole TLB are flushed automatically by loading
203
 * CR3 register.
204
 */
205
void
206
mmu_switch(pgd_t pgd)
207
{
208
        uint32_t phys = (uint32_t)kvtop(pgd);
209

    
210
        if (phys != get_cr3())
211
                set_cr3(phys);
212
}
213

    
214
/*
215
 * Returns the physical address for the specified virtual address.
216
 * This routine checks if the virtual area actually exist.
217
 * It returns 0 if at least one page is not mapped.
218
 */
219
paddr_t
220
mmu_extract(pgd_t pgd, vaddr_t va, size_t size)
221
{
222
        pte_t pte;
223
        vaddr_t start, end, pg;
224
        paddr_t pa;
225

    
226
        start = trunc_page(va);
227
        end = trunc_page(va + size - 1);
228

    
229
        /* Check all pages exist */
230
        for (pg = start; pg <= end; pg += PAGE_SIZE) {
231
                if (!pte_present(pgd, pg))
232
                        return 0;
233
                pte = vtopte(pgd, pg);
234
                if (!page_present(pte, pg))
235
                        return 0;
236
        }
237

    
238
        /* Get physical address */
239
        pte = vtopte(pgd, start);
240
        pa = (paddr_t)ptetopg(pte, start);
241
        return pa + (paddr_t)(va - start);
242
}
243

    
244
/*
245
 * Initialize mmu
246
 *
247
 * Paging is already enabled in locore.S. And, physical address
248
 * 0-4M has been already mapped into kernel space in locore.S.
249
 * Now, all physical memory is mapped into kernel virtual address
250
 * as straight 1:1 mapping. User mode access is not allowed for
251
 * these kernel pages.
252
 * page_init() must be called before calling this routine.
253
 *
254
 * Note: This routine requires 4K bytes to map 4M bytes memory. So,
255
 * if the system has a lot of RAM, the "used memory" by kernel will
256
 * become large, too. For example, page table requires 512K bytes
257
 * for 512M bytes system RAM.
258
 */
259
void
260
mmu_init(struct mmumap *mmumap_table)
261
{
262
        struct mmumap *map;
263
        int map_type = 0;
264

    
265
        for (map = mmumap_table; map->type != 0; map++) {
266
                switch (map->type) {
267
                case VMT_RAM:
268
                case VMT_ROM:
269
                case VMT_DMA:
270
                        map_type = PG_SYSTEM;
271
                        break;
272
                case VMT_IO:
273
                        map_type = PG_IOMEM;
274
                        break;
275
                }
276

    
277
                if (mmu_map(boot_pgd, map->phys, map->virt,
278
                            (size_t)map->size, map_type))
279
                        panic("mmu_init");
280
        }
281
}