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); 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(nogrow) VMKernelAddressSpace(teamID, base, size) 196 : (VMAddressSpace*)new(nogrow) VMUserAddressSpace(teamID, base, size); 197 if (addressSpace == NULL) 198 return B_NO_MEMORY; 199 200 status_t status = addressSpace->InitObject(); 201 if (status != B_OK) { 202 delete addressSpace; 203 return status; 204 } 205 206 TRACE(("vm_create_aspace: team %ld (%skernel): %#lx bytes starting at " 207 "%#lx => %p\n", id, kernel ? "!" : "", size, base, addressSpace)); 208 209 // create the corresponding translation map 210 status = arch_vm_translation_map_create_map(kernel, 211 &addressSpace->fTranslationMap); 212 if (status != B_OK) { 213 delete addressSpace; 214 return status; 215 } 216 217 // add the aspace to the global hash table 218 rw_lock_write_lock(&sAddressSpaceTableLock); 219 sAddressSpaceTable.InsertUnchecked(addressSpace); 220 rw_lock_write_unlock(&sAddressSpaceTableLock); 221 222 *_addressSpace = addressSpace; 223 return B_OK; 224 } 225 226 227 /*static*/ VMAddressSpace* 228 VMAddressSpace::GetKernel() 229 { 230 // we can treat this one a little differently since it can't be deleted 231 sKernelAddressSpace->Get(); 232 return sKernelAddressSpace; 233 } 234 235 236 /*static*/ team_id 237 VMAddressSpace::CurrentID() 238 { 239 struct thread* thread = thread_get_current_thread(); 240 241 if (thread != NULL && thread->team->address_space != NULL) 242 return thread->team->id; 243 244 return B_ERROR; 245 } 246 247 248 /*static*/ VMAddressSpace* 249 VMAddressSpace::GetCurrent() 250 { 251 struct thread* thread = thread_get_current_thread(); 252 253 if (thread != NULL) { 254 VMAddressSpace* addressSpace = thread->team->address_space; 255 if (addressSpace != NULL) { 256 addressSpace->Get(); 257 return addressSpace; 258 } 259 } 260 261 return NULL; 262 } 263 264 265 /*static*/ VMAddressSpace* 266 VMAddressSpace::Get(team_id teamID) 267 { 268 rw_lock_read_lock(&sAddressSpaceTableLock); 269 VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID); 270 if (addressSpace) 271 addressSpace->Get(); 272 rw_lock_read_unlock(&sAddressSpaceTableLock); 273 274 return addressSpace; 275 } 276 277 278 /*static*/ VMAddressSpace* 279 VMAddressSpace::DebugFirst() 280 { 281 return sAddressSpaceTable.GetIterator().Next(); 282 } 283 284 285 /*static*/ VMAddressSpace* 286 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace) 287 { 288 if (addressSpace == NULL) 289 return NULL; 290 291 AddressSpaceTable::Iterator it 292 = sAddressSpaceTable.GetIterator(addressSpace->ID()); 293 it.Next(); 294 return it.Next(); 295 } 296 297 298 /*static*/ void 299 VMAddressSpace::_DeleteIfUnreferenced(team_id id) 300 { 301 rw_lock_write_lock(&sAddressSpaceTableLock); 302 303 bool remove = false; 304 VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(id); 305 if (addressSpace != NULL && addressSpace->fRefCount == 0) { 306 sAddressSpaceTable.RemoveUnchecked(addressSpace); 307 remove = true; 308 } 309 310 rw_lock_write_unlock(&sAddressSpaceTableLock); 311 312 if (remove) 313 delete addressSpace; 314 } 315 316 317 /*static*/ int 318 VMAddressSpace::_DumpCommand(int argc, char** argv) 319 { 320 VMAddressSpace* aspace; 321 322 if (argc < 2) { 323 kprintf("aspace: not enough arguments\n"); 324 return 0; 325 } 326 327 // if the argument looks like a number, treat it as such 328 329 { 330 team_id id = strtoul(argv[1], NULL, 0); 331 332 aspace = sAddressSpaceTable.Lookup(id); 333 if (aspace == NULL) { 334 kprintf("invalid aspace id\n"); 335 } else { 336 aspace->Dump(); 337 } 338 return 0; 339 } 340 return 0; 341 } 342 343 344 /*static*/ int 345 VMAddressSpace::_DumpListCommand(int argc, char** argv) 346 { 347 kprintf(" address id base end area count " 348 " area size\n"); 349 350 AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator(); 351 while (VMAddressSpace* space = it.Next()) { 352 int32 areaCount = 0; 353 off_t areaSize = 0; 354 for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator(); 355 VMArea* area = areaIt.Next();) { 356 if (area->cache->type != CACHE_TYPE_NULL) { 357 areaCount++; 358 areaSize += area->Size(); 359 } 360 } 361 kprintf("%p %6" B_PRId32 " %#010" B_PRIxADDR " %#10" B_PRIxADDR 362 " %10" B_PRId32 " %10" B_PRIdOFF "\n", space, space->ID(), 363 space->Base(), space->EndAddress(), areaCount, areaSize); 364 } 365 366 return 0; 367 } 368