1 /* 2 * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 #include <vm/VMAddressSpace.h> 12 13 #include <stdlib.h> 14 15 #include <new> 16 17 #include <KernelExport.h> 18 19 #include <util/OpenHashTable.h> 20 21 #include <heap.h> 22 #include <thread.h> 23 #include <vm/vm.h> 24 #include <vm/VMArea.h> 25 #include <vm/VMCache.h> 26 27 #include "VMKernelAddressSpace.h" 28 #include "VMUserAddressSpace.h" 29 30 31 //#define TRACE_VM 32 #ifdef TRACE_VM 33 # define TRACE(x) dprintf x 34 #else 35 # define TRACE(x) ; 36 #endif 37 38 39 #define ASPACE_HASH_TABLE_SIZE 1024 40 41 42 // #pragma mark - AddressSpaceHashDefinition 43 44 45 struct AddressSpaceHashDefinition { 46 typedef team_id KeyType; 47 typedef VMAddressSpace ValueType; 48 49 size_t HashKey(team_id key) const 50 { 51 return key; 52 } 53 54 size_t Hash(const VMAddressSpace* value) const 55 { 56 return HashKey(value->ID()); 57 } 58 59 bool Compare(team_id key, const VMAddressSpace* value) const 60 { 61 return value->ID() == key; 62 } 63 64 VMAddressSpace*& GetLink(VMAddressSpace* value) const 65 { 66 return value->HashTableLink(); 67 } 68 }; 69 70 typedef BOpenHashTable<AddressSpaceHashDefinition> AddressSpaceTable; 71 72 static AddressSpaceTable sAddressSpaceTable; 73 static rw_lock sAddressSpaceTableLock; 74 75 VMAddressSpace* VMAddressSpace::sKernelAddressSpace; 76 77 78 // #pragma mark - VMAddressSpace 79 80 81 VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size, 82 const char* name) 83 : 84 fBase(base), 85 fEndAddress(base + (size - 1)), 86 fFreeSpace(size), 87 fID(id), 88 fRefCount(1), 89 fFaultCount(0), 90 fChangeCount(0), 91 fDeleting(false) 92 { 93 rw_lock_init(&fLock, name); 94 // rw_lock_init(&fLock, kernel ? "kernel address space" : "address space"); 95 } 96 97 98 VMAddressSpace::~VMAddressSpace() 99 { 100 TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n", 101 ID())); 102 103 WriteLock(); 104 105 delete fTranslationMap; 106 107 rw_lock_destroy(&fLock); 108 } 109 110 111 /*static*/ status_t 112 VMAddressSpace::Init() 113 { 114 rw_lock_init(&sAddressSpaceTableLock, "address spaces table"); 115 116 // create the area and address space hash tables 117 { 118 new(&sAddressSpaceTable) AddressSpaceTable; 119 status_t error = sAddressSpaceTable.Init(ASPACE_HASH_TABLE_SIZE); 120 if (error != B_OK) 121 panic("vm_init: error creating aspace hash table\n"); 122 } 123 124 // create the initial kernel address space 125 if (Create(B_SYSTEM_TEAM, KERNEL_BASE, KERNEL_SIZE, true, 126 &sKernelAddressSpace) != B_OK) { 127 panic("vm_init: error creating kernel address space!\n"); 128 } 129 130 add_debugger_command("aspaces", &_DumpListCommand, 131 "Dump a list of all address spaces"); 132 add_debugger_command("aspace", &_DumpCommand, 133 "Dump info about a particular address space"); 134 135 return B_OK; 136 } 137 138 139 /*static*/ status_t 140 VMAddressSpace::InitPostSem() 141 { 142 status_t status = sKernelAddressSpace->fTranslationMap->InitPostSem(); 143 if (status != B_OK) 144 return status; 145 146 return B_OK; 147 } 148 149 150 /*! Deletes all areas in the specified address space, and the address 151 space by decreasing all reference counters. It also marks the 152 address space of being in deletion state, so that no more areas 153 can be created in it. 154 After this, the address space is not operational anymore, but might 155 still be in memory until the last reference has been released. 156 */ 157 void 158 VMAddressSpace::RemoveAndPut() 159 { 160 WriteLock(); 161 fDeleting = true; 162 WriteUnlock(); 163 164 vm_delete_areas(this, true); 165 Put(); 166 } 167 168 169 status_t 170 VMAddressSpace::InitObject() 171 { 172 return B_OK; 173 } 174 175 176 void 177 VMAddressSpace::Dump() const 178 { 179 kprintf("dump of address space at %p:\n", this); 180 kprintf("id: %" B_PRId32 "\n", fID); 181 kprintf("ref_count: %" B_PRId32 "\n", fRefCount); 182 kprintf("fault_count: %" B_PRId32 "\n", fFaultCount); 183 kprintf("translation_map: %p\n", fTranslationMap); 184 kprintf("base: %#" B_PRIxADDR "\n", fBase); 185 kprintf("end: %#" B_PRIxADDR "\n", fEndAddress); 186 kprintf("change_count: %" B_PRId32 "\n", fChangeCount); 187 } 188 189 190 /*static*/ status_t 191 VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel, 192 VMAddressSpace** _addressSpace) 193 { 194 VMAddressSpace* addressSpace = kernel 195 ? (VMAddressSpace*)new(std::nothrow) VMKernelAddressSpace(teamID, base, 196 size) 197 : (VMAddressSpace*)new(std::nothrow) VMUserAddressSpace(teamID, base, 198 size); 199 if (addressSpace == NULL) 200 return B_NO_MEMORY; 201 202 status_t status = addressSpace->InitObject(); 203 if (status != B_OK) { 204 delete addressSpace; 205 return status; 206 } 207 208 TRACE(("vm_create_aspace: team %ld (%skernel): %#lx bytes starting at " 209 "%#lx => %p\n", id, kernel ? "!" : "", size, base, addressSpace)); 210 211 // create the corresponding translation map 212 status = arch_vm_translation_map_create_map(kernel, 213 &addressSpace->fTranslationMap); 214 if (status != B_OK) { 215 delete addressSpace; 216 return status; 217 } 218 219 // add the aspace to the global hash table 220 rw_lock_write_lock(&sAddressSpaceTableLock); 221 sAddressSpaceTable.InsertUnchecked(addressSpace); 222 rw_lock_write_unlock(&sAddressSpaceTableLock); 223 224 *_addressSpace = addressSpace; 225 return B_OK; 226 } 227 228 229 /*static*/ VMAddressSpace* 230 VMAddressSpace::GetKernel() 231 { 232 // we can treat this one a little differently since it can't be deleted 233 sKernelAddressSpace->Get(); 234 return sKernelAddressSpace; 235 } 236 237 238 /*static*/ team_id 239 VMAddressSpace::CurrentID() 240 { 241 struct thread* thread = thread_get_current_thread(); 242 243 if (thread != NULL && thread->team->address_space != NULL) 244 return thread->team->id; 245 246 return B_ERROR; 247 } 248 249 250 /*static*/ VMAddressSpace* 251 VMAddressSpace::GetCurrent() 252 { 253 struct thread* thread = thread_get_current_thread(); 254 255 if (thread != NULL) { 256 VMAddressSpace* addressSpace = thread->team->address_space; 257 if (addressSpace != NULL) { 258 addressSpace->Get(); 259 return addressSpace; 260 } 261 } 262 263 return NULL; 264 } 265 266 267 /*static*/ VMAddressSpace* 268 VMAddressSpace::Get(team_id teamID) 269 { 270 rw_lock_read_lock(&sAddressSpaceTableLock); 271 VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID); 272 if (addressSpace) 273 addressSpace->Get(); 274 rw_lock_read_unlock(&sAddressSpaceTableLock); 275 276 return addressSpace; 277 } 278 279 280 /*static*/ VMAddressSpace* 281 VMAddressSpace::DebugFirst() 282 { 283 return sAddressSpaceTable.GetIterator().Next(); 284 } 285 286 287 /*static*/ VMAddressSpace* 288 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace) 289 { 290 if (addressSpace == NULL) 291 return NULL; 292 293 AddressSpaceTable::Iterator it 294 = sAddressSpaceTable.GetIterator(addressSpace->ID()); 295 it.Next(); 296 return it.Next(); 297 } 298 299 300 /*static*/ void 301 VMAddressSpace::_DeleteIfUnreferenced(team_id id) 302 { 303 rw_lock_write_lock(&sAddressSpaceTableLock); 304 305 bool remove = false; 306 VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id); 307 if (addressSpace != NULL && addressSpace->fRefCount == 0) { 308 sAddressSpaceTable.RemoveUnchecked(addressSpace); 309 remove = true; 310 } 311 312 rw_lock_write_unlock(&sAddressSpaceTableLock); 313 314 if (remove) 315 delete addressSpace; 316 } 317 318 319 /*static*/ int 320 VMAddressSpace::_DumpCommand(int argc, char** argv) 321 { 322 VMAddressSpace* aspace; 323 324 if (argc < 2) { 325 kprintf("aspace: not enough arguments\n"); 326 return 0; 327 } 328 329 // if the argument looks like a number, treat it as such 330 331 { 332 team_id id = strtoul(argv[1], NULL, 0); 333 334 aspace = sAddressSpaceTable.Lookup(id); 335 if (aspace == NULL) { 336 kprintf("invalid aspace id\n"); 337 } else { 338 aspace->Dump(); 339 } 340 return 0; 341 } 342 return 0; 343 } 344 345 346 /*static*/ int 347 VMAddressSpace::_DumpListCommand(int argc, char** argv) 348 { 349 kprintf(" address id base end area count " 350 " area size\n"); 351 352 AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator(); 353 while (VMAddressSpace* space = it.Next()) { 354 int32 areaCount = 0; 355 off_t areaSize = 0; 356 for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator(); 357 VMArea* area = areaIt.Next();) { 358 if (area->cache->type != CACHE_TYPE_NULL) { 359 areaCount++; 360 areaSize += area->Size(); 361 } 362 } 363 kprintf("%p %6" B_PRId32 " %#010" B_PRIxADDR " %#10" B_PRIxADDR 364 " %10" B_PRId32 " %10" B_PRIdOFF "\n", space, space->ID(), 365 space->Base(), space->EndAddress(), areaCount, areaSize); 366 } 367 368 return 0; 369 } 370