1 /* 2 * Copyright 2009-2011, 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 fTranslationMap(NULL), 92 fDeleting(false) 93 { 94 rw_lock_init(&fLock, name); 95 // rw_lock_init(&fLock, kernel ? "kernel address space" : "address space"); 96 } 97 98 99 VMAddressSpace::~VMAddressSpace() 100 { 101 TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n", 102 ID())); 103 104 WriteLock(); 105 106 delete fTranslationMap; 107 108 rw_lock_destroy(&fLock); 109 } 110 111 112 /*static*/ status_t 113 VMAddressSpace::Init() 114 { 115 rw_lock_init(&sAddressSpaceTableLock, "address spaces table"); 116 117 // create the area and address space hash tables 118 { 119 new(&sAddressSpaceTable) AddressSpaceTable; 120 status_t error = sAddressSpaceTable.Init(ASPACE_HASH_TABLE_SIZE); 121 if (error != B_OK) 122 panic("vm_init: error creating aspace hash table\n"); 123 } 124 125 // create the initial kernel address space 126 if (Create(B_SYSTEM_TEAM, KERNEL_BASE, KERNEL_SIZE, true, 127 &sKernelAddressSpace) != B_OK) { 128 panic("vm_init: error creating kernel address space!\n"); 129 } 130 131 add_debugger_command("aspaces", &_DumpListCommand, 132 "Dump a list of all address spaces"); 133 add_debugger_command("aspace", &_DumpCommand, 134 "Dump info about a particular address space"); 135 136 return B_OK; 137 } 138 139 140 /*! Deletes all areas in the specified address space, and the address 141 space by decreasing all reference counters. It also marks the 142 address space of being in deletion state, so that no more areas 143 can be created in it. 144 After this, the address space is not operational anymore, but might 145 still be in memory until the last reference has been released. 146 */ 147 void 148 VMAddressSpace::RemoveAndPut() 149 { 150 WriteLock(); 151 fDeleting = true; 152 WriteUnlock(); 153 154 vm_delete_areas(this, true); 155 Put(); 156 } 157 158 159 status_t 160 VMAddressSpace::InitObject() 161 { 162 return B_OK; 163 } 164 165 166 void 167 VMAddressSpace::Dump() const 168 { 169 kprintf("dump of address space at %p:\n", this); 170 kprintf("id: %" B_PRId32 "\n", fID); 171 kprintf("ref_count: %" B_PRId32 "\n", fRefCount); 172 kprintf("fault_count: %" B_PRId32 "\n", fFaultCount); 173 kprintf("translation_map: %p\n", fTranslationMap); 174 kprintf("base: %#" B_PRIxADDR "\n", fBase); 175 kprintf("end: %#" B_PRIxADDR "\n", fEndAddress); 176 kprintf("change_count: %" B_PRId32 "\n", fChangeCount); 177 } 178 179 180 /*static*/ status_t 181 VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel, 182 VMAddressSpace** _addressSpace) 183 { 184 VMAddressSpace* addressSpace = kernel 185 ? (VMAddressSpace*)new(std::nothrow) VMKernelAddressSpace(teamID, base, 186 size) 187 : (VMAddressSpace*)new(std::nothrow) VMUserAddressSpace(teamID, base, 188 size); 189 if (addressSpace == NULL) 190 return B_NO_MEMORY; 191 192 status_t status = addressSpace->InitObject(); 193 if (status != B_OK) { 194 delete addressSpace; 195 return status; 196 } 197 198 TRACE(("VMAddressSpace::Create(): team %" B_PRId32 " (%skernel): %#lx " 199 "bytes starting at %#lx => %p\n", teamID, kernel ? "" : "!", size, 200 base, addressSpace)); 201 202 // create the corresponding translation map 203 status = arch_vm_translation_map_create_map(kernel, 204 &addressSpace->fTranslationMap); 205 if (status != B_OK) { 206 delete addressSpace; 207 return status; 208 } 209 210 // add the aspace to the global hash table 211 rw_lock_write_lock(&sAddressSpaceTableLock); 212 sAddressSpaceTable.InsertUnchecked(addressSpace); 213 rw_lock_write_unlock(&sAddressSpaceTableLock); 214 215 *_addressSpace = addressSpace; 216 return B_OK; 217 } 218 219 220 /*static*/ VMAddressSpace* 221 VMAddressSpace::GetKernel() 222 { 223 // we can treat this one a little differently since it can't be deleted 224 sKernelAddressSpace->Get(); 225 return sKernelAddressSpace; 226 } 227 228 229 /*static*/ team_id 230 VMAddressSpace::CurrentID() 231 { 232 Thread* thread = thread_get_current_thread(); 233 234 if (thread != NULL && thread->team->address_space != NULL) 235 return thread->team->id; 236 237 return B_ERROR; 238 } 239 240 241 /*static*/ VMAddressSpace* 242 VMAddressSpace::GetCurrent() 243 { 244 Thread* thread = thread_get_current_thread(); 245 246 if (thread != NULL) { 247 VMAddressSpace* addressSpace = thread->team->address_space; 248 if (addressSpace != NULL) { 249 addressSpace->Get(); 250 return addressSpace; 251 } 252 } 253 254 return NULL; 255 } 256 257 258 /*static*/ VMAddressSpace* 259 VMAddressSpace::Get(team_id teamID) 260 { 261 rw_lock_read_lock(&sAddressSpaceTableLock); 262 VMAddressSpace* addressSpace = sAddressSpaceTable.Lookup(teamID); 263 if (addressSpace) 264 addressSpace->Get(); 265 rw_lock_read_unlock(&sAddressSpaceTableLock); 266 267 return addressSpace; 268 } 269 270 271 /*static*/ VMAddressSpace* 272 VMAddressSpace::DebugFirst() 273 { 274 return sAddressSpaceTable.GetIterator().Next(); 275 } 276 277 278 /*static*/ VMAddressSpace* 279 VMAddressSpace::DebugNext(VMAddressSpace* addressSpace) 280 { 281 if (addressSpace == NULL) 282 return NULL; 283 284 AddressSpaceTable::Iterator it 285 = sAddressSpaceTable.GetIterator(addressSpace->ID()); 286 it.Next(); 287 return it.Next(); 288 } 289 290 291 /*static*/ VMAddressSpace* 292 VMAddressSpace::DebugGet(team_id teamID) 293 { 294 return sAddressSpaceTable.Lookup(teamID); 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(" %*s id %*s %*s area count area size\n", 348 B_PRINTF_POINTER_WIDTH, "address", B_PRINTF_POINTER_WIDTH, "base", 349 B_PRINTF_POINTER_WIDTH, "end"); 350 351 AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator(); 352 while (VMAddressSpace* space = it.Next()) { 353 int32 areaCount = 0; 354 off_t areaSize = 0; 355 for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator(); 356 VMArea* area = areaIt.Next();) { 357 areaCount++; 358 areaSize += area->Size(); 359 } 360 kprintf("%p %6" B_PRId32 " %#010" B_PRIxADDR " %#10" B_PRIxADDR 361 " %10" B_PRId32 " %10" B_PRIdOFF "\n", space, space->ID(), 362 space->Base(), space->EndAddress(), areaCount, areaSize); 363 } 364 365 return 0; 366 } 367