4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/systm.h>
29 #include <sys/archsystm.h>
30 #include <sys/debug.h>
31 #include <sys/bootconf.h>
32 #include <sys/bootsvcs.h>
33 #include <sys/bootinfo.h>
35 #include <sys/cmn_err.h>
36 #include <sys/param.h>
37 #include <sys/machparam.h>
38 #include <sys/machsystm.h>
39 #include <sys/promif.h>
42 #include <sys/hypervisor.h>
44 #include <vm/kboot_mmu.h>
45 #include <vm/hat_pte.h>
46 #include <vm/hat_i86.h>
47 #include <vm/seg_kmem.h>
51 * Joe's debug printing
54 bop_printf(NULL, "kboot_mmu.c: %s is %" PRIx64 "\n", #x, (uint64_t)(x));
56 #define DBG(x) /* naught */
60 * Page table and memory stuff.
62 static caddr_t window
;
63 static caddr_t pte_to_window
;
66 * this are needed by mmu_init()
68 int kbm_nx_support
= 0; /* NX bit in PTEs is in use */
69 int kbm_pae_support
= 0; /* PAE is 64 bit Page table entries */
70 int kbm_pge_support
= 0; /* PGE is Page table global bit enabled */
71 int kbm_largepage_support
= 0;
72 uint_t kbm_nucleus_size
= 0;
74 #define BOOT_SHIFT(l) (shift_amt[l])
75 #define BOOT_SZ(l) ((size_t)1 << BOOT_SHIFT(l))
76 #define BOOT_OFFSET(l) (BOOT_SZ(l) - 1)
77 #define BOOT_MASK(l) (~BOOT_OFFSET(l))
80 * Initialize memory management parameters for boot time page table management
83 kbm_init(struct xboot_info
*bi
)
86 * configure mmu information
88 kbm_nucleus_size
= (uintptr_t)bi
->bi_kseg_size
;
89 kbm_largepage_support
= bi
->bi_use_largepage
;
90 kbm_nx_support
= bi
->bi_use_nx
;
91 kbm_pae_support
= bi
->bi_use_pae
;
92 kbm_pge_support
= bi
->bi_use_pge
;
93 window
= bi
->bi_pt_window
;
95 pte_to_window
= bi
->bi_pte_to_pt_window
;
97 if (kbm_pae_support
) {
98 shift_amt
= shift_amt_pae
;
108 shift_amt
= shift_amt_nopae
;
109 ptes_per_table
= 1024;
111 lpagesize
= FOUR_MEG
;
116 xen_info
= bi
->bi_xen_start_info
;
117 mfn_list
= (mfn_t
*)xen_info
->mfn_list
;
119 mfn_count
= xen_info
->nr_pages
;
122 top_page_table
= bi
->bi_top_page_table
;
127 * Change the addressible page table window to point at a given page
131 kbm_remap_window(paddr_t physaddr
, int writeable
)
133 x86pte_t pt_bits
= PT_NOCONSIST
| PT_VALID
| PT_WRITABLE
;
139 pt_bits
&= ~PT_WRITABLE
;
140 if (HYPERVISOR_update_va_mapping((uintptr_t)window
,
141 pa_to_ma(physaddr
) | pt_bits
, UVMF_INVLPG
| UVMF_LOCAL
) < 0)
142 bop_panic("HYPERVISOR_update_va_mapping() failed");
145 *((x86pte_t
*)pte_to_window
) = physaddr
| pt_bits
;
147 *((x86pte32_t
*)pte_to_window
) = physaddr
| pt_bits
;
148 mmu_tlbflush_entry(window
);
155 * Add a mapping for the physical page at the given virtual address.
158 kbm_map(uintptr_t va
, paddr_t pa
, uint_t level
, uint_t is_kernel
)
161 paddr_t pte_physaddr
;
165 panic("kbm_map() called too late");
167 pteval
= pa_to_ma(pa
) | PT_NOCONSIST
| PT_VALID
| PT_WRITABLE
;
169 pteval
|= PT_PAGESIZE
;
170 if (kbm_pge_support
&& is_kernel
)
175 * try update_va_mapping first - fails if page table is missing.
177 if (HYPERVISOR_update_va_mapping(va
, pteval
,
178 UVMF_INVLPG
| UVMF_LOCAL
) == 0)
183 * Find the pte that will map this address. This creates any
184 * missing intermediate level page tables.
186 ptep
= find_pte(va
, &pte_physaddr
, level
, 0);
188 bop_panic("kbm_map: find_pte returned NULL");
191 if (HYPERVISOR_update_va_mapping(va
, pteval
, UVMF_INVLPG
| UVMF_LOCAL
))
192 bop_panic("HYPERVISOR_update_va_mapping() failed");
197 *((x86pte32_t
*)ptep
) = pteval
;
198 mmu_tlbflush_entry((caddr_t
)va
);
205 * Add a mapping for the machine page at the given virtual address.
208 kbm_map_ma(maddr_t ma
, uintptr_t va
, uint_t level
)
210 paddr_t pte_physaddr
;
213 pteval
= ma
| PT_NOCONSIST
| PT_VALID
| PT_REF
| PT_WRITABLE
;
215 pteval
|= PT_PAGESIZE
;
218 * try update_va_mapping first - fails if page table is missing.
220 if (HYPERVISOR_update_va_mapping(va
,
221 pteval
, UVMF_INVLPG
| UVMF_LOCAL
) == 0)
225 * Find the pte that will map this address. This creates any
226 * missing intermediate level page tables
228 (void) find_pte(va
, &pte_physaddr
, level
, 0);
230 if (HYPERVISOR_update_va_mapping(va
,
231 pteval
, UVMF_INVLPG
| UVMF_LOCAL
) != 0)
232 bop_panic("HYPERVISOR_update_va_mapping failed");
239 * Probe the boot time page tables to find the first mapping
240 * including va (or higher) and return non-zero if one is found.
241 * va is updated to the starting address and len to the pagesize.
242 * pp will be set to point to the 1st page_t of the mapped page(s).
244 * Note that if va is in the middle of a large page, the returned va
245 * will be less than what was asked for.
248 kbm_probe(uintptr_t *va
, size_t *len
, pfn_t
*pfn
, uint_t
*prot
)
252 paddr_t pte_physaddr
;
257 panic("kbm_probe() called too late");
265 if (IN_VA_HOLE(probe_va
))
266 probe_va
= mmu
.hole_end
;
268 if (IN_HYPERVISOR_VA(probe_va
))
269 #if defined(__amd64) && defined(__xpv)
270 probe_va
= HYPERVISOR_VIRT_END
;
276 * If we don't have a valid PTP/PTE at this level
277 * then we can bump VA by this level's pagesize and try again.
278 * When the probe_va wraps around, we are done.
280 ptep
= find_pte(probe_va
, &pte_physaddr
, l
, 1);
282 bop_panic("kbm_probe: find_pte returned NULL");
286 pte_val
= *((x86pte32_t
*)ptep
);
287 if (!PTE_ISVALID(pte_val
)) {
288 probe_va
= (probe_va
& BOOT_MASK(l
)) + BOOT_SZ(l
);
295 * If this entry is a pointer to a lower level page table
298 if (!PTE_ISPAGE(pte_val
, l
)) {
305 * We found a boot level page table entry
308 *va
= probe_va
& ~(*len
- 1);
309 *pfn
= PTE2PFN(pte_val
, l
);
312 *prot
= PROT_READ
| PROT_EXEC
;
313 if (PTE_GET(pte_val
, PT_WRITABLE
))
317 * pt_nx is cleared if processor doesn't support NX bit
319 if (PTE_GET(pte_val
, mmu
.pt_nx
))
328 * Destroy a boot loader page table 4K mapping.
331 kbm_unmap(uintptr_t va
)
334 panic("kbm_unmap() called too late");
337 (void) HYPERVISOR_update_va_mapping(va
, 0,
338 UVMF_INVLPG
| UVMF_LOCAL
);
342 uint_t probe_only
= 1;
344 ptep
= find_pte(va
, NULL
, level
, probe_only
);
351 *((x86pte32_t
*)ptep
) = 0;
352 mmu_tlbflush_entry((caddr_t
)va
);
359 * Change a boot loader page table 4K mapping.
360 * Returns the pfn of the old mapping.
363 kbm_remap(uintptr_t va
, pfn_t pfn
)
367 uint_t probe_only
= 1;
368 x86pte_t pte_val
= pa_to_ma(pfn_to_pa(pfn
)) | PT_WRITABLE
|
369 PT_NOCONSIST
| PT_VALID
;
373 panic("kbm_remap() called too late");
374 ptep
= find_pte(va
, NULL
, level
, probe_only
);
376 bop_panic("kbm_remap: find_pte returned NULL");
381 old_pte
= *((x86pte32_t
*)ptep
);
384 if (HYPERVISOR_update_va_mapping(va
, pte_val
, UVMF_INVLPG
| UVMF_LOCAL
))
385 bop_panic("HYPERVISOR_update_va_mapping() failed");
388 *((x86pte_t
*)ptep
) = pte_val
;
390 *((x86pte32_t
*)ptep
) = pte_val
;
391 mmu_tlbflush_entry((caddr_t
)va
);
394 if (!(old_pte
& PT_VALID
) || ma_to_pa(old_pte
) == -1)
395 return (PFN_INVALID
);
396 return (mmu_btop(ma_to_pa(old_pte
)));
401 * Change a boot loader page table 4K mapping to read only.
404 kbm_read_only(uintptr_t va
, paddr_t pa
)
406 x86pte_t pte_val
= pa_to_ma(pa
) |
407 PT_NOCONSIST
| PT_REF
| PT_MOD
| PT_VALID
;
410 if (HYPERVISOR_update_va_mapping(va
, pte_val
, UVMF_INVLPG
| UVMF_LOCAL
))
411 bop_panic("HYPERVISOR_update_va_mapping() failed");
416 ptep
= find_pte(va
, NULL
, level
, 0);
418 bop_panic("kbm_read_only: find_pte returned NULL");
423 *((x86pte32_t
*)ptep
) = pte_val
;
424 mmu_tlbflush_entry((caddr_t
)va
);
429 * interfaces for kernel debugger to access physical memory
431 static x86pte_t save_pte
;
436 static int first_time
= 1;
444 save_pte
= *((x86pte_t
*)pte_to_window
);
446 save_pte
= *((x86pte32_t
*)pte_to_window
);
447 return (kbm_remap_window(pa
, 0));
454 if (HYPERVISOR_update_va_mapping((uintptr_t)window
, save_pte
,
455 UVMF_INVLPG
| UVMF_LOCAL
) < 0)
456 bop_panic("HYPERVISOR_update_va_mapping() failed");
459 *((x86pte_t
*)pte_to_window
) = save_pte
;
461 *((x86pte32_t
*)pte_to_window
) = save_pte
;
462 mmu_tlbflush_entry(window
);
467 get_pteval(paddr_t table
, uint_t index
)
469 void *table_ptr
= kbm_remap_window(table
, 0);
472 return (((x86pte_t
*)table_ptr
)[index
]);
473 return (((x86pte32_t
*)table_ptr
)[index
]);
478 set_pteval(paddr_t table
, uint_t index
, uint_t level
, x86pte_t pteval
)
480 void *table_ptr
= kbm_remap_window(table
, 0);
482 ((x86pte_t
*)table_ptr
)[index
] = pteval
;
484 ((x86pte32_t
*)table_ptr
)[index
] = pteval
;
485 if (level
== top_level
&& level
== 2)
491 make_ptable(x86pte_t
*pteval
, uint_t level
)
496 new_table
= do_bop_phys_alloc(MMU_PAGESIZE
, MMU_PAGESIZE
);
497 table_ptr
= kbm_remap_window(new_table
, 1);
498 bzero(table_ptr
, MMU_PAGESIZE
);
500 /* Remove write permission to the new page table. */
501 (void) kbm_remap_window(new_table
, 0);
504 if (level
== top_level
&& level
== 2)
505 *pteval
= pa_to_ma(new_table
) | PT_VALID
;
507 *pteval
= pa_to_ma(new_table
) |
508 PT_VALID
| PT_REF
| PT_USER
| PT_WRITABLE
;
514 map_pte(paddr_t table
, uint_t index
)
516 void *table_ptr
= kbm_remap_window(table
, 0);
517 return ((x86pte_t
*)((caddr_t
)table_ptr
+ index
* pte_size
));