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