1// Copyright 2016 The Fuchsia Authors 2// Copyright (c) 2014 Travis Geiselbrecht 3// 4// Use of this source code is governed by a MIT-style 5// license that can be found in the LICENSE file or at 6// https://opensource.org/licenses/MIT 7 8#include "vm_priv.h" 9#include <assert.h> 10#include <err.h> 11#include <inttypes.h> 12#include <kernel/mutex.h> 13#include <kernel/thread_lock.h> 14#include <lib/console.h> 15#include <lib/ktrace.h> 16#include <object/diagnostics.h> 17#include <string.h> 18#include <trace.h> 19#include <vm/fault.h> 20#include <vm/pmm.h> 21#include <vm/vm.h> 22#include <vm/vm_address_region.h> 23#include <vm/vm_aspace.h> 24#include <zircon/types.h> 25 26#define LOCAL_TRACE MAX(VM_GLOBAL_TRACE, 0) 27#define TRACE_PAGE_FAULT 0 28 29// This file mostly contains C wrappers around the underlying C++ objects, conforming to 30// the older api. 31 32static inline void vmm_context_switch(VmAspace* oldspace, VmAspace* newaspace) { 33 DEBUG_ASSERT(thread_lock_held()); 34 35 ArchVmAspace::ContextSwitch(oldspace ? &oldspace->arch_aspace() : nullptr, 36 newaspace ? &newaspace->arch_aspace() : nullptr); 37} 38 39void vmm_context_switch(vmm_aspace_t* oldspace, vmm_aspace_t* newaspace) { 40 vmm_context_switch(reinterpret_cast<VmAspace*>(oldspace), reinterpret_cast<VmAspace*>(newaspace)); 41} 42 43zx_status_t vmm_page_fault_handler(vaddr_t addr, uint flags) { 44 45 // hardware fault, mark it as such 46 flags |= VMM_PF_FLAG_HW_FAULT; 47 48#if TRACE_PAGE_FAULT || LOCAL_TRACE 49 thread_t* current_thread = get_current_thread(); 50 TRACEF("thread %s va %#" PRIxPTR ", flags 0x%x\n", current_thread->name, addr, flags); 51#endif 52 53 ktrace(TAG_PAGE_FAULT, (uint32_t)(addr >> 32), (uint32_t)addr, flags, arch_curr_cpu_num()); 54 55 // get the address space object this pointer is in 56 VmAspace* aspace = VmAspace::vaddr_to_aspace(addr); 57 if (!aspace) { 58 return ZX_ERR_NOT_FOUND; 59 } 60 61 // page fault it 62 zx_status_t status = aspace->PageFault(addr, flags); 63 64 // If it's a user fault, dump info about process memory usage. 65 // If it's a kernel fault, the kernel could possibly already 66 // hold locks on VMOs, Aspaces, etc, so we can't safely do 67 // this. 68 if ((status == ZX_ERR_NOT_FOUND) && (flags & VMM_PF_FLAG_USER)) { 69 printf("PageFault: %zu free pages\n", pmm_count_free_pages()); 70 DumpProcessMemoryUsage("PageFault: MemoryUsed: ", 8 * 256); 71 } 72 73 ktrace(TAG_PAGE_FAULT_EXIT, (uint32_t)(addr >> 32), (uint32_t)addr, flags, arch_curr_cpu_num()); 74 75 return status; 76} 77 78void vmm_set_active_aspace(vmm_aspace_t* aspace) { 79 LTRACEF("aspace %p\n", aspace); 80 81 thread_t* t = get_current_thread(); 82 DEBUG_ASSERT(t); 83 84 if (aspace == t->aspace) { 85 return; 86 } 87 88 // grab the thread lock and switch to the new address space 89 Guard<spin_lock_t, IrqSave> thread_lock_guard{ThreadLock::Get()}; 90 vmm_aspace_t* old = t->aspace; 91 t->aspace = aspace; 92 vmm_context_switch(old, t->aspace); 93} 94 95vmm_aspace_t* vmm_get_kernel_aspace(void) { 96 return reinterpret_cast<vmm_aspace_t*>(VmAspace::kernel_aspace()); 97} 98 99static int cmd_vmm(int argc, const cmd_args* argv, uint32_t flags) { 100 if (argc < 2) { 101 notenoughargs: 102 printf("not enough arguments\n"); 103 usage: 104 printf("usage:\n"); 105 printf("%s aspaces\n", argv[0].str); 106 printf("%s kaspace\n", argv[0].str); 107 printf("%s alloc <size> <align_pow2>\n", argv[0].str); 108 printf("%s alloc_physical <paddr> <size> <align_pow2>\n", argv[0].str); 109 printf("%s alloc_contig <size> <align_pow2>\n", argv[0].str); 110 printf("%s free_region <address>\n", argv[0].str); 111 printf("%s create_aspace\n", argv[0].str); 112 printf("%s create_test_aspace\n", argv[0].str); 113 printf("%s free_aspace <address>\n", argv[0].str); 114 printf("%s set_test_aspace <address>\n", argv[0].str); 115 return ZX_ERR_INTERNAL; 116 } 117 118 static fbl::RefPtr<VmAspace> test_aspace; 119 if (!test_aspace) { 120 test_aspace = fbl::WrapRefPtr(VmAspace::kernel_aspace()); 121 } 122 123 if (!strcmp(argv[1].str, "aspaces")) { 124 DumpAllAspaces(true); 125 } else if (!strcmp(argv[1].str, "kaspace")) { 126 VmAspace::kernel_aspace()->Dump(true); 127 } else if (!strcmp(argv[1].str, "alloc")) { 128 if (argc < 3) { 129 goto notenoughargs; 130 } 131 132 void* ptr = (void*)0x99; 133 uint8_t align = (argc >= 4) ? (uint8_t)argv[3].u : 0u; 134 zx_status_t err = test_aspace->Alloc("alloc test", argv[2].u, &ptr, align, 0, 0); 135 printf("VmAspace::Alloc returns %d, ptr %p\n", err, ptr); 136 } else if (!strcmp(argv[1].str, "alloc_physical")) { 137 if (argc < 4) { 138 goto notenoughargs; 139 } 140 141 void* ptr = (void*)0x99; 142 uint8_t align = (argc >= 5) ? (uint8_t)argv[4].u : 0u; 143 zx_status_t err = test_aspace->AllocPhysical("physical test", argv[3].u, &ptr, align, argv[2].u, 144 0, ARCH_MMU_FLAG_UNCACHED_DEVICE | ARCH_MMU_FLAG_PERM_READ | 145 ARCH_MMU_FLAG_PERM_WRITE); 146 printf("VmAspace::AllocPhysical returns %d, ptr %p\n", err, ptr); 147 } else if (!strcmp(argv[1].str, "alloc_contig")) { 148 if (argc < 3) { 149 goto notenoughargs; 150 } 151 152 void* ptr = (void*)0x99; 153 uint8_t align = (argc >= 4) ? (uint8_t)argv[3].u : 0u; 154 zx_status_t err = test_aspace->AllocContiguous("contig test", argv[2].u, &ptr, align, 0, 155 ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE); 156 printf("VmAspace::AllocContiguous returns %d, ptr %p\n", err, ptr); 157 } else if (!strcmp(argv[1].str, "free_region")) { 158 if (argc < 2) { 159 goto notenoughargs; 160 } 161 162 zx_status_t err = test_aspace->FreeRegion(reinterpret_cast<vaddr_t>(argv[2].u)); 163 printf("VmAspace::FreeRegion returns %d\n", err); 164 } else if (!strcmp(argv[1].str, "create_aspace")) { 165 fbl::RefPtr<VmAspace> aspace = VmAspace::Create(0, "test"); 166 printf("VmAspace::Create aspace %p\n", aspace.get()); 167 } else if (!strcmp(argv[1].str, "create_test_aspace")) { 168 fbl::RefPtr<VmAspace> aspace = VmAspace::Create(0, "test"); 169 printf("VmAspace::Create aspace %p\n", aspace.get()); 170 171 test_aspace = aspace; 172 get_current_thread()->aspace = reinterpret_cast<vmm_aspace_t*>(aspace.get()); 173 thread_sleep(1); // XXX hack to force it to reschedule and thus load the aspace 174 } else if (!strcmp(argv[1].str, "free_aspace")) { 175 if (argc < 2) { 176 goto notenoughargs; 177 } 178 179 fbl::RefPtr<VmAspace> aspace = fbl::WrapRefPtr((VmAspace*)(void*)argv[2].u); 180 if (test_aspace == aspace) { 181 test_aspace = nullptr; 182 } 183 184 if (get_current_thread()->aspace == reinterpret_cast<vmm_aspace_t*>(aspace.get())) { 185 get_current_thread()->aspace = nullptr; 186 thread_sleep(1); // hack 187 } 188 189 zx_status_t err = aspace->Destroy(); 190 printf("VmAspace::Destroy() returns %d\n", err); 191 } else if (!strcmp(argv[1].str, "set_test_aspace")) { 192 if (argc < 2) { 193 goto notenoughargs; 194 } 195 196 test_aspace = fbl::WrapRefPtr((VmAspace*)(void*)argv[2].u); 197 get_current_thread()->aspace = reinterpret_cast<vmm_aspace_t*>(test_aspace.get()); 198 thread_sleep(1); // XXX hack to force it to reschedule and thus load the aspace 199 } else { 200 printf("unknown command\n"); 201 goto usage; 202 } 203 204 return ZX_OK; 205} 206 207STATIC_COMMAND_START 208#if LK_DEBUGLEVEL > 0 209STATIC_COMMAND("vmm", "virtual memory manager", &cmd_vmm) 210#endif 211STATIC_COMMAND_END(vmm); 212