1 /* 2 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2008, Jérôme Duval. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <KernelExport.h> 15 16 #include <smp.h> 17 #include <util/AutoLock.h> 18 #include <vm.h> 19 #include <vm_address_space.h> 20 #include <vm_page.h> 21 #include <vm_priv.h> 22 23 #include <arch/vm.h> 24 #include <arch/int.h> 25 #include <arch/cpu.h> 26 27 #include <arch/x86/bios.h> 28 29 #include "x86_paging.h" 30 31 32 //#define TRACE_ARCH_VM 33 #ifdef TRACE_ARCH_VM 34 # define TRACE(x) dprintf x 35 #else 36 # define TRACE(x) ; 37 #endif 38 39 #define TRACE_MTRR_ARCH_VM 40 #ifdef TRACE_MTRR_ARCH_VM 41 # define TRACE_MTRR(x...) dprintf(x) 42 #else 43 # define TRACE_MTRR(x...) 44 #endif 45 46 47 #define kMaxMemoryTypeRegisters 32 48 49 void *gDmaAddress; 50 51 static uint32 sMemoryTypeBitmap; 52 static int32 sMemoryTypeIDs[kMaxMemoryTypeRegisters]; 53 static uint32 sMemoryTypeRegisterCount; 54 static spinlock sMemoryTypeLock; 55 56 57 static int32 58 allocate_mtrr(void) 59 { 60 InterruptsSpinLocker _(&sMemoryTypeLock); 61 62 // find free bit 63 64 for (uint32 index = 0; index < sMemoryTypeRegisterCount; index++) { 65 if (sMemoryTypeBitmap & (1UL << index)) 66 continue; 67 68 sMemoryTypeBitmap |= 1UL << index; 69 return index; 70 } 71 72 return -1; 73 } 74 75 76 static void 77 free_mtrr(int32 index) 78 { 79 InterruptsSpinLocker _(&sMemoryTypeLock); 80 81 sMemoryTypeBitmap &= ~(1UL << index); 82 } 83 84 85 #if 0 86 /*! 87 Checks if the provided range overlaps an existing mtrr range 88 If it actually extends an existing range, extendedIndex is filled 89 */ 90 static bool 91 is_memory_overlapping(uint64 base, uint64 length, int32 *extendedIndex) 92 { 93 *extendedIndex = -1; 94 for (uint32 index = 0; index < sMemoryTypeRegisterCount; index++) { 95 if (sMemoryTypeBitmap & (1UL << index)) { 96 uint64 b,l; 97 uint8 t; 98 x86_get_mtrr(index, &b, &l, &t); 99 100 // check first for write combining extensions 101 if (base <= b 102 && (base + length) >= (b + l) 103 && t == IA32_MTR_WRITE_COMBINING) { 104 *extendedIndex = index; 105 return true; 106 } 107 if ((base >= b && base < (b + l)) 108 || ((base + length) > b 109 && (base + length) <= (b + l))) 110 return true; 111 } 112 } 113 return false; 114 } 115 #endif // 0 116 117 118 static uint64 119 nearest_power(uint64 value) 120 { 121 uint64 power = 1UL << 12; 122 // 12 bits is the smallest supported alignment/length 123 124 while (value > power) 125 power <<= 1; 126 127 return power; 128 } 129 130 131 static void 132 nearest_powers(uint64 value, uint64 *lower, uint64 *upper) 133 { 134 uint64 power = 1UL << 12; 135 *lower = power; 136 // 12 bits is the smallest supported alignment/length 137 138 while (value >= power) { 139 *lower = power; 140 power <<= 1; 141 } 142 143 *upper = power; 144 } 145 146 147 static status_t 148 set_memory_type(int32 id, uint64 base, uint64 length, uint32 type) 149 { 150 int32 index = -1; 151 152 if (type == 0) 153 return B_OK; 154 155 switch (type) { 156 case B_MTR_UC: 157 type = IA32_MTR_UNCACHED; 158 break; 159 case B_MTR_WC: 160 type = IA32_MTR_WRITE_COMBINING; 161 break; 162 case B_MTR_WT: 163 type = IA32_MTR_WRITE_THROUGH; 164 break; 165 case B_MTR_WP: 166 type = IA32_MTR_WRITE_PROTECTED; 167 break; 168 case B_MTR_WB: 169 type = IA32_MTR_WRITE_BACK; 170 break; 171 default: 172 return B_BAD_VALUE; 173 } 174 175 if (sMemoryTypeRegisterCount == 0) 176 return B_NOT_SUPPORTED; 177 178 #if 0 179 // check if it overlaps 180 if (type == IA32_MTR_WRITE_COMBINING 181 && is_memory_overlapping(base, length, &index)) { 182 if (index < 0) { 183 dprintf("allocate MTRR failed, it overlaps an existing MTRR slot\n"); 184 return B_BAD_VALUE; 185 } 186 // we replace an existing write-combining mtrr with a bigger one at the index position 187 } 188 #endif 189 190 // length must be a power of 2; just round it up to the next value 191 length = nearest_power(length); 192 193 if (length + base <= base) { 194 // 4GB overflow 195 return B_BAD_VALUE; 196 } 197 198 // base must be aligned to the length 199 if (base & (length - 1)) 200 return B_BAD_VALUE; 201 202 if (index < 0) 203 index = allocate_mtrr(); 204 if (index < 0) 205 return B_ERROR; 206 207 TRACE_MTRR("allocate MTRR slot %ld, base = %Lx, length = %Lx, type=0x%lx\n", 208 index, base, length, type); 209 210 sMemoryTypeIDs[index] = id; 211 x86_set_mtrr(index, base, length, type); 212 213 return B_OK; 214 } 215 216 217 #define MTRR_MAX_SOLUTIONS 5 // usually MTRR count is eight, keep a few for other needs 218 #define MTRR_MIN_SIZE 0x80000 // 512 KB 219 static int64 sSolutions[MTRR_MAX_SOLUTIONS]; 220 static int32 sSolutionCount; 221 static int64 sPropositions[MTRR_MAX_SOLUTIONS]; 222 223 224 /*! Find the nearest powers of two for a value, save current iteration, 225 then make recursives calls for the remaining values. 226 It uses at most MTRR_MAX_SOLUTIONS levels of recursion because 227 only that count of MTRR registers are available to map the memory. 228 */ 229 static void 230 find_nearest(uint64 value, int iteration) 231 { 232 int i; 233 uint64 down, up; 234 TRACE_MTRR("find_nearest %Lx %d\n", value, iteration); 235 if (iteration > (MTRR_MAX_SOLUTIONS - 1) || (iteration + 1) >= sSolutionCount) { 236 if (sSolutionCount > MTRR_MAX_SOLUTIONS) { 237 // no solutions yet, save something 238 for (i=0; i<iteration; i++) 239 sSolutions[i] = sPropositions[i]; 240 sSolutionCount = iteration; 241 } 242 return; 243 } 244 nearest_powers(value, &down, &up); 245 sPropositions[iteration] = down; 246 if (value - down < MTRR_MIN_SIZE) { 247 for (i=0; i<=iteration; i++) 248 sSolutions[i] = sPropositions[i]; 249 sSolutionCount = iteration + 1; 250 return; 251 } 252 find_nearest(value - down, iteration + 1); 253 sPropositions[iteration] = -up; 254 if (up - value < MTRR_MIN_SIZE) { 255 for (i=0; i<=iteration; i++) 256 sSolutions[i] = sPropositions[i]; 257 sSolutionCount = iteration + 1; 258 return; 259 } 260 find_nearest(up - value, iteration + 1); 261 } 262 263 264 /*! Set up MTRR to map the memory to write-back using uncached if necessary */ 265 static void 266 set_memory_write_back(int32 id, uint64 base, uint64 length) 267 { 268 status_t err; 269 TRACE_MTRR("set_memory_write_back base %Lx length %Lx\n", base, length); 270 sSolutionCount = MTRR_MAX_SOLUTIONS + 1; 271 find_nearest(length, 0); 272 273 #ifdef TRACE_MTRR 274 dprintf("solutions: "); 275 for (int i=0; i<sSolutionCount; i++) 276 dprintf("0x%Lx ", sSolutions[i]); 277 dprintf("\n"); 278 #endif 279 280 for (int run = 0; run < 2; run++) { 281 bool nextDown = false; 282 uint64 newBase = base; 283 284 for (int i = 0; i < sSolutionCount; i++) { 285 if (sSolutions[i] < 0) { 286 if (nextDown) 287 newBase += sSolutions[i]; 288 289 if ((run == 0) == nextDown) { 290 err = set_memory_type(id, newBase, -sSolutions[i], 291 nextDown ? B_MTR_UC : B_MTR_WB); 292 if (err != B_OK) { 293 dprintf("set_memory_type returned %s (0x%lx)\n", 294 strerror(err), err); 295 } 296 } 297 298 if (!nextDown) 299 newBase -= sSolutions[i]; 300 nextDown = !nextDown; 301 } else { 302 if (nextDown) 303 newBase -= sSolutions[i]; 304 305 if ((run == 0) == nextDown) { 306 err = set_memory_type(id, newBase, sSolutions[i], 307 nextDown ? B_MTR_UC : B_MTR_WB); 308 if (err != B_OK) { 309 dprintf("set_memory_type returned %s (0x%lx)\n", 310 strerror(err), err); 311 } 312 } 313 314 if (!nextDown) 315 newBase += sSolutions[i]; 316 } 317 } 318 } 319 } 320 321 322 // #pragma mark - 323 324 325 status_t 326 arch_vm_init(kernel_args *args) 327 { 328 TRACE(("arch_vm_init: entry\n")); 329 return 0; 330 } 331 332 333 /*! Marks DMA region as in-use, and maps it into the kernel space */ 334 status_t 335 arch_vm_init_post_area(kernel_args *args) 336 { 337 area_id id; 338 339 TRACE(("arch_vm_init_post_area: entry\n")); 340 341 // account for DMA area and mark the pages unusable 342 vm_mark_page_range_inuse(0x0, 0xa0000 / B_PAGE_SIZE); 343 344 // map 0 - 0xa0000 directly 345 id = map_physical_memory("dma_region", (void *)0x0, 0xa0000, 346 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 347 &gDmaAddress); 348 if (id < 0) { 349 panic("arch_vm_init_post_area: unable to map dma region\n"); 350 return B_NO_MEMORY; 351 } 352 353 return bios_init(); 354 } 355 356 357 /*! Gets rid of all yet unmapped (and therefore now unused) page tables */ 358 status_t 359 arch_vm_init_end(kernel_args *args) 360 { 361 TRACE(("arch_vm_init_endvm: entry\n")); 362 363 // throw away anything in the kernel_args.pgtable[] that's not yet mapped 364 vm_free_unused_boot_loader_range(KERNEL_BASE, 365 0x400000 * args->arch_args.num_pgtables); 366 367 return B_OK; 368 } 369 370 371 status_t 372 arch_vm_init_post_modules(kernel_args *args) 373 { 374 // void *cookie; 375 376 // the x86 CPU modules are now accessible 377 378 sMemoryTypeRegisterCount = x86_count_mtrrs(); 379 if (sMemoryTypeRegisterCount == 0) 380 return B_OK; 381 382 // not very likely, but play safe here 383 if (sMemoryTypeRegisterCount > kMaxMemoryTypeRegisters) 384 sMemoryTypeRegisterCount = kMaxMemoryTypeRegisters; 385 386 // init memory type ID table 387 388 for (uint32 i = 0; i < sMemoryTypeRegisterCount; i++) { 389 sMemoryTypeIDs[i] = -1; 390 } 391 392 // set the physical memory ranges to write-back mode 393 394 for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) { 395 set_memory_write_back(-1, args->physical_memory_range[i].start, 396 args->physical_memory_range[i].size); 397 } 398 399 return B_OK; 400 } 401 402 403 void 404 arch_vm_aspace_swap(struct vm_address_space *from, struct vm_address_space *to) 405 { 406 // This functions is only invoked when a userland thread is in the process 407 // of dying. It switches to the kernel team and does whatever cleanup is 408 // necessary (in case it is the team's main thread, it will delete the 409 // team). 410 // It is however not necessary to change the page directory. Userland team's 411 // page directories include all kernel mappings as well. Furthermore our 412 // arch specific translation map data objects are ref-counted, so they won't 413 // go away as long as they are still used on any CPU. 414 } 415 416 417 bool 418 arch_vm_supports_protection(uint32 protection) 419 { 420 // x86 always has the same read/write properties for userland and the 421 // kernel. 422 // That's why we do not support user-read/kernel-write access. While the 423 // other way around is not supported either, we don't care in this case 424 // and give the kernel full access. 425 if ((protection & (B_READ_AREA | B_WRITE_AREA)) == B_READ_AREA 426 && protection & B_KERNEL_WRITE_AREA) 427 return false; 428 429 return true; 430 } 431 432 433 void 434 arch_vm_unset_memory_type(struct vm_area *area) 435 { 436 uint32 index; 437 438 if (area->memory_type == 0) 439 return; 440 441 // find index for area ID 442 443 for (index = 0; index < sMemoryTypeRegisterCount; index++) { 444 if (sMemoryTypeIDs[index] == area->id) { 445 x86_set_mtrr(index, 0, 0, 0); 446 447 sMemoryTypeIDs[index] = -1; 448 free_mtrr(index); 449 break; 450 } 451 } 452 } 453 454 455 status_t 456 arch_vm_set_memory_type(struct vm_area *area, addr_t physicalBase, 457 uint32 type) 458 { 459 area->memory_type = type >> MEMORY_TYPE_SHIFT; 460 return set_memory_type(area->id, physicalBase, area->size, type); 461 } 462