xref: /haiku/src/system/kernel/arch/x86/arch_vm_translation_map.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
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 #include <arch/vm_translation_map.h>
11 
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <AutoDeleter.h>
16 
17 #include <arch_system_info.h>
18 #include <heap.h>
19 #include <int.h>
20 #include <thread.h>
21 #include <smp.h>
22 #include <util/AutoLock.h>
23 #include <util/queue.h>
24 #include <vm_address_space.h>
25 #include <vm_page.h>
26 #include <vm_priv.h>
27 
28 #include "x86_paging.h"
29 #include "x86_physical_page_mapper.h"
30 
31 
32 //#define TRACE_VM_TMAP
33 #ifdef TRACE_VM_TMAP
34 #	define TRACE(x) dprintf x
35 #else
36 #	define TRACE(x) ;
37 #endif
38 
39 static page_table_entry *sPageHole = NULL;
40 static page_directory_entry *sPageHolePageDir = NULL;
41 static page_directory_entry *sKernelPhysicalPageDirectory = NULL;
42 static page_directory_entry *sKernelVirtualPageDirectory = NULL;
43 
44 
45 // Accessor class to reuse the SinglyLinkedListLink of DeferredDeletable for
46 // vm_translation_map_arch_info.
47 struct ArchTMapGetLink {
48 private:
49 	typedef SinglyLinkedListLink<vm_translation_map_arch_info> Link;
50 
51 public:
52 	inline Link* operator()(vm_translation_map_arch_info* element) const
53 	{
54 		return (Link*)element->GetSinglyLinkedListLink();
55 	}
56 
57 	inline const Link* operator()(
58 		const vm_translation_map_arch_info* element) const
59 	{
60 		return (const Link*)element->GetSinglyLinkedListLink();
61 	}
62 
63 };
64 
65 
66 typedef SinglyLinkedList<vm_translation_map_arch_info, ArchTMapGetLink>
67 	ArchTMapList;
68 
69 
70 static ArchTMapList sTMapList;
71 static spinlock sTMapListLock;
72 
73 #define CHATTY_TMAP 0
74 
75 #define FIRST_USER_PGDIR_ENT    (VADDR_TO_PDENT(USER_BASE))
76 #define NUM_USER_PGDIR_ENTS     (VADDR_TO_PDENT(ROUNDUP(USER_SIZE, \
77 									B_PAGE_SIZE * 1024)))
78 #define FIRST_KERNEL_PGDIR_ENT  (VADDR_TO_PDENT(KERNEL_BASE))
79 #define NUM_KERNEL_PGDIR_ENTS   (VADDR_TO_PDENT(KERNEL_SIZE))
80 #define IS_KERNEL_MAP(map)		(map->arch_data->pgdir_phys \
81 									== sKernelPhysicalPageDirectory)
82 
83 static status_t early_query(addr_t va, addr_t *out_physical);
84 
85 static void flush_tmap(vm_translation_map *map);
86 
87 
88 void *
89 i386_translation_map_get_pgdir(vm_translation_map *map)
90 {
91 	return map->arch_data->pgdir_phys;
92 }
93 
94 
95 void
96 x86_update_all_pgdirs(int index, page_directory_entry e)
97 {
98 	unsigned int state = disable_interrupts();
99 
100 	acquire_spinlock(&sTMapListLock);
101 
102 	ArchTMapList::Iterator it = sTMapList.GetIterator();
103 	while (vm_translation_map_arch_info* info = it.Next())
104 		info->pgdir_virt[index] = e;
105 
106 	release_spinlock(&sTMapListLock);
107 	restore_interrupts(state);
108 }
109 
110 
111 // XXX currently assumes this translation map is active
112 
113 static status_t
114 early_query(addr_t va, addr_t *_physicalAddress)
115 {
116 	page_table_entry *pentry;
117 
118 	if (sPageHolePageDir[VADDR_TO_PDENT(va)].present == 0) {
119 		// no pagetable here
120 		return B_ERROR;
121 	}
122 
123 	pentry = sPageHole + va / B_PAGE_SIZE;
124 	if (pentry->present == 0) {
125 		// page mapping not valid
126 		return B_ERROR;
127 	}
128 
129 	*_physicalAddress = pentry->addr << 12;
130 	return B_OK;
131 }
132 
133 
134 /*!	Acquires the map's recursive lock, and resets the invalidate pages counter
135 	in case it's the first locking recursion.
136 */
137 static status_t
138 lock_tmap(vm_translation_map *map)
139 {
140 	TRACE(("lock_tmap: map %p\n", map));
141 
142 	recursive_lock_lock(&map->lock);
143 	if (recursive_lock_get_recursion(&map->lock) == 1) {
144 		// we were the first one to grab the lock
145 		TRACE(("clearing invalidated page count\n"));
146 		map->arch_data->num_invalidate_pages = 0;
147 	}
148 
149 	return B_OK;
150 }
151 
152 
153 /*!	Unlocks the map, and, if we'll actually losing the recursive lock,
154 	flush all pending changes of this map (ie. flush TLB caches as
155 	needed).
156 */
157 static status_t
158 unlock_tmap(vm_translation_map *map)
159 {
160 	TRACE(("unlock_tmap: map %p\n", map));
161 
162 	if (recursive_lock_get_recursion(&map->lock) == 1) {
163 		// we're about to release it for the last time
164 		flush_tmap(map);
165 	}
166 
167 	recursive_lock_unlock(&map->lock);
168 	return B_OK;
169 }
170 
171 
172 vm_translation_map_arch_info::vm_translation_map_arch_info()
173 	:
174 	pgdir_virt(NULL),
175 	ref_count(1)
176 {
177 }
178 
179 
180 vm_translation_map_arch_info::~vm_translation_map_arch_info()
181 {
182 	// free the page dir
183 	free(pgdir_virt);
184 }
185 
186 
187 void
188 vm_translation_map_arch_info::Delete()
189 {
190 	// remove from global list
191 	InterruptsSpinLocker locker(sTMapListLock);
192 	sTMapList.Remove(this);
193 	locker.Unlock();
194 
195 	if (are_interrupts_enabled())
196 		delete this;
197 	else
198 		deferred_delete(this);
199 }
200 
201 
202 static void
203 destroy_tmap(vm_translation_map *map)
204 {
205 	if (map == NULL)
206 		return;
207 
208 	if (map->arch_data->page_mapper != NULL)
209 		map->arch_data->page_mapper->Delete();
210 
211 	if (map->arch_data->pgdir_virt != NULL) {
212 		// cycle through and free all of the user space pgtables
213 		for (uint32 i = VADDR_TO_PDENT(USER_BASE);
214 				i <= VADDR_TO_PDENT(USER_BASE + (USER_SIZE - 1)); i++) {
215 			addr_t pgtable_addr;
216 			vm_page *page;
217 
218 			if (map->arch_data->pgdir_virt[i].present == 1) {
219 				pgtable_addr = map->arch_data->pgdir_virt[i].addr;
220 				page = vm_lookup_page(pgtable_addr);
221 				if (!page)
222 					panic("destroy_tmap: didn't find pgtable page\n");
223 				vm_page_set_state(page, PAGE_STATE_FREE);
224 			}
225 		}
226 	}
227 
228 	map->arch_data->RemoveReference();
229 
230 	recursive_lock_destroy(&map->lock);
231 }
232 
233 
234 void
235 x86_put_pgtable_in_pgdir(page_directory_entry *entry,
236 	addr_t pgtable_phys, uint32 attributes)
237 {
238 	page_directory_entry table;
239 	// put it in the pgdir
240 	init_page_directory_entry(&table);
241 	table.addr = ADDR_SHIFT(pgtable_phys);
242 
243 	// ToDo: we ignore the attributes of the page table - for compatibility
244 	//	with BeOS we allow having user accessible areas in the kernel address
245 	//	space. This is currently being used by some drivers, mainly for the
246 	//	frame buffer. Our current real time data implementation makes use of
247 	//	this fact, too.
248 	//	We might want to get rid of this possibility one day, especially if
249 	//	we intend to port it to a platform that does not support this.
250 	table.user = 1;
251 	table.rw = 1;
252 	table.present = 1;
253 	update_page_directory_entry(entry, &table);
254 }
255 
256 
257 static void
258 put_page_table_entry_in_pgtable(page_table_entry *entry,
259 	addr_t physicalAddress, uint32 attributes, bool globalPage)
260 {
261 	page_table_entry page;
262 	init_page_table_entry(&page);
263 
264 	page.addr = ADDR_SHIFT(physicalAddress);
265 
266 	// if the page is user accessible, it's automatically
267 	// accessible in kernel space, too (but with the same
268 	// protection)
269 	page.user = (attributes & B_USER_PROTECTION) != 0;
270 	if (page.user)
271 		page.rw = (attributes & B_WRITE_AREA) != 0;
272 	else
273 		page.rw = (attributes & B_KERNEL_WRITE_AREA) != 0;
274 	page.present = 1;
275 
276 	if (globalPage)
277 		page.global = 1;
278 
279 	// put it in the page table
280 	update_page_table_entry(entry, &page);
281 }
282 
283 
284 static size_t
285 map_max_pages_need(vm_translation_map */*map*/, addr_t start, addr_t end)
286 {
287 	// If start == 0, the actual base address is not yet known to the caller and
288 	// we shall assume the worst case.
289 	if (start == 0) {
290 		start = 1023 * B_PAGE_SIZE;
291 		end += 1023 * B_PAGE_SIZE;
292 	}
293 	return VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start);
294 }
295 
296 
297 static status_t
298 map_tmap(vm_translation_map *map, addr_t va, addr_t pa, uint32 attributes)
299 {
300 	page_directory_entry *pd;
301 	page_table_entry *pt;
302 	unsigned int index;
303 
304 	TRACE(("map_tmap: entry pa 0x%lx va 0x%lx\n", pa, va));
305 
306 /*
307 	dprintf("pgdir at 0x%x\n", pgdir);
308 	dprintf("index is %d\n", va / B_PAGE_SIZE / 1024);
309 	dprintf("final at 0x%x\n", &pgdir[va / B_PAGE_SIZE / 1024]);
310 	dprintf("value is 0x%x\n", *(int *)&pgdir[va / B_PAGE_SIZE / 1024]);
311 	dprintf("present bit is %d\n", pgdir[va / B_PAGE_SIZE / 1024].present);
312 	dprintf("addr is %d\n", pgdir[va / B_PAGE_SIZE / 1024].addr);
313 */
314 	pd = map->arch_data->pgdir_virt;
315 
316 	// check to see if a page table exists for this range
317 	index = VADDR_TO_PDENT(va);
318 	if (pd[index].present == 0) {
319 		addr_t pgtable;
320 		vm_page *page;
321 
322 		// we need to allocate a pgtable
323 		page = vm_page_allocate_page(PAGE_STATE_CLEAR, true);
324 
325 		// mark the page WIRED
326 		vm_page_set_state(page, PAGE_STATE_WIRED);
327 
328 		pgtable = page->physical_page_number * B_PAGE_SIZE;
329 
330 		TRACE(("map_tmap: asked for free page for pgtable. 0x%lx\n", pgtable));
331 
332 		// put it in the pgdir
333 		x86_put_pgtable_in_pgdir(&pd[index], pgtable, attributes
334 			| ((attributes & B_USER_PROTECTION) != 0
335 					? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
336 
337 		// update any other page directories, if it maps kernel space
338 		if (index >= FIRST_KERNEL_PGDIR_ENT
339 			&& index < (FIRST_KERNEL_PGDIR_ENT + NUM_KERNEL_PGDIR_ENTS))
340 			x86_update_all_pgdirs(index, pd[index]);
341 
342 		map->map_count++;
343 	}
344 
345 	// now, fill in the pentry
346 	struct thread* thread = thread_get_current_thread();
347 	ThreadCPUPinner pinner(thread);
348 
349 	pt = map->arch_data->page_mapper->GetPageTableAt(
350 		ADDR_REVERSE_SHIFT(pd[index].addr));
351 	index = VADDR_TO_PTENT(va);
352 
353 	put_page_table_entry_in_pgtable(&pt[index], pa, attributes,
354 		IS_KERNEL_MAP(map));
355 
356 	pinner.Unlock();
357 
358 	if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) {
359 		map->arch_data->pages_to_invalidate[
360 			map->arch_data->num_invalidate_pages] = va;
361 	}
362 
363 	map->arch_data->num_invalidate_pages++;
364 
365 	map->map_count++;
366 
367 	return 0;
368 }
369 
370 
371 static status_t
372 unmap_tmap(vm_translation_map *map, addr_t start, addr_t end)
373 {
374 	page_table_entry *pt;
375 	page_directory_entry *pd = map->arch_data->pgdir_virt;
376 	int index;
377 
378 	start = ROUNDOWN(start, B_PAGE_SIZE);
379 	end = ROUNDUP(end, B_PAGE_SIZE);
380 
381 	TRACE(("unmap_tmap: asked to free pages 0x%lx to 0x%lx\n", start, end));
382 
383 restart:
384 	if (start >= end)
385 		return B_OK;
386 
387 	index = VADDR_TO_PDENT(start);
388 	if (pd[index].present == 0) {
389 		// no pagetable here, move the start up to access the next page table
390 		start = ROUNDUP(start + 1, B_PAGE_SIZE);
391 		goto restart;
392 	}
393 
394 	struct thread* thread = thread_get_current_thread();
395 	ThreadCPUPinner pinner(thread);
396 
397 	pt = map->arch_data->page_mapper->GetPageTableAt(
398 		ADDR_REVERSE_SHIFT(pd[index].addr));
399 
400 	for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
401 			index++, start += B_PAGE_SIZE) {
402 		if (pt[index].present == 0) {
403 			// page mapping not valid
404 			continue;
405 		}
406 
407 		TRACE(("unmap_tmap: removing page 0x%lx\n", start));
408 
409 		pt[index].present = 0;
410 		map->map_count--;
411 
412 		if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) {
413 			map->arch_data->pages_to_invalidate[
414 				map->arch_data->num_invalidate_pages] = start;
415 		}
416 
417 		map->arch_data->num_invalidate_pages++;
418 	}
419 
420 	pinner.Unlock();
421 
422 	goto restart;
423 }
424 
425 
426 static status_t
427 query_tmap_interrupt(vm_translation_map *map, addr_t va, addr_t *_physical,
428 	uint32 *_flags)
429 {
430 	page_directory_entry *pd = map->arch_data->pgdir_virt;
431 	page_table_entry *pt;
432 	addr_t physicalPageTable;
433 	int32 index;
434 
435 	*_physical = 0;
436 
437 	index = VADDR_TO_PDENT(va);
438 	if (pd[index].present == 0) {
439 		// no pagetable here
440 		return B_ERROR;
441 	}
442 
443 	// map page table entry
444 	physicalPageTable = ADDR_REVERSE_SHIFT(pd[index].addr);
445 	pt = gPhysicalPageMapper->InterruptGetPageTableAt(physicalPageTable);
446 
447 	index = VADDR_TO_PTENT(va);
448 	*_physical = ADDR_REVERSE_SHIFT(pt[index].addr);
449 
450 	*_flags |= ((pt[index].rw ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA)
451 		| (pt[index].dirty ? PAGE_MODIFIED : 0)
452 		| (pt[index].accessed ? PAGE_ACCESSED : 0)
453 		| (pt[index].present ? PAGE_PRESENT : 0);
454 
455 	return B_OK;
456 }
457 
458 
459 static status_t
460 query_tmap(vm_translation_map *map, addr_t va, addr_t *_physical,
461 	uint32 *_flags)
462 {
463 	page_table_entry *pt;
464 	page_directory_entry *pd = map->arch_data->pgdir_virt;
465 	int32 index;
466 
467 	// default the flags to not present
468 	*_flags = 0;
469 	*_physical = 0;
470 
471 	index = VADDR_TO_PDENT(va);
472 	if (pd[index].present == 0) {
473 		// no pagetable here
474 		return B_NO_ERROR;
475 	}
476 
477 	struct thread* thread = thread_get_current_thread();
478 	ThreadCPUPinner pinner(thread);
479 
480 	pt = map->arch_data->page_mapper->GetPageTableAt(
481 		ADDR_REVERSE_SHIFT(pd[index].addr));
482 	index = VADDR_TO_PTENT(va);
483 
484 	*_physical = ADDR_REVERSE_SHIFT(pt[index].addr);
485 
486 	// read in the page state flags
487 	if (pt[index].user)
488 		*_flags |= (pt[index].rw ? B_WRITE_AREA : 0) | B_READ_AREA;
489 
490 	*_flags |= ((pt[index].rw ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA)
491 		| (pt[index].dirty ? PAGE_MODIFIED : 0)
492 		| (pt[index].accessed ? PAGE_ACCESSED : 0)
493 		| (pt[index].present ? PAGE_PRESENT : 0);
494 
495 	pinner.Unlock();
496 
497 	TRACE(("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va));
498 
499 	return B_OK;
500 }
501 
502 
503 static addr_t
504 get_mapped_size_tmap(vm_translation_map *map)
505 {
506 	return map->map_count;
507 }
508 
509 
510 static status_t
511 protect_tmap(vm_translation_map *map, addr_t start, addr_t end,
512 	uint32 attributes)
513 {
514 	page_table_entry *pt;
515 	page_directory_entry *pd = map->arch_data->pgdir_virt;
516 	int index;
517 
518 	start = ROUNDOWN(start, B_PAGE_SIZE);
519 	end = ROUNDUP(end, B_PAGE_SIZE);
520 
521 	TRACE(("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end,
522 		attributes));
523 
524 restart:
525 	if (start >= end)
526 		return B_OK;
527 
528 	index = VADDR_TO_PDENT(start);
529 	if (pd[index].present == 0) {
530 		// no pagetable here, move the start up to access the next page table
531 		start = ROUNDUP(start + 1, B_PAGE_SIZE);
532 		goto restart;
533 	}
534 
535 	struct thread* thread = thread_get_current_thread();
536 	ThreadCPUPinner pinner(thread);
537 
538 	pt = map->arch_data->page_mapper->GetPageTableAt(
539 		ADDR_REVERSE_SHIFT(pd[index].addr));
540 
541 	for (index = VADDR_TO_PTENT(start); index < 1024 && start < end;
542 			index++, start += B_PAGE_SIZE) {
543 		if (pt[index].present == 0) {
544 			// page mapping not valid
545 			continue;
546 		}
547 
548 		TRACE(("protect_tmap: protect page 0x%lx\n", start));
549 
550 		pt[index].user = (attributes & B_USER_PROTECTION) != 0;
551 		if ((attributes & B_USER_PROTECTION) != 0)
552 			pt[index].rw = (attributes & B_WRITE_AREA) != 0;
553 		else
554 			pt[index].rw = (attributes & B_KERNEL_WRITE_AREA) != 0;
555 
556 		if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) {
557 			map->arch_data->pages_to_invalidate[
558 				map->arch_data->num_invalidate_pages] = start;
559 		}
560 
561 		map->arch_data->num_invalidate_pages++;
562 	}
563 
564 	pinner.Unlock();
565 
566 	goto restart;
567 }
568 
569 
570 static status_t
571 clear_flags_tmap(vm_translation_map *map, addr_t va, uint32 flags)
572 {
573 	page_table_entry *pt;
574 	page_directory_entry *pd = map->arch_data->pgdir_virt;
575 	int index;
576 	int tlb_flush = false;
577 
578 	index = VADDR_TO_PDENT(va);
579 	if (pd[index].present == 0) {
580 		// no pagetable here
581 		return B_OK;
582 	}
583 
584 	struct thread* thread = thread_get_current_thread();
585 	ThreadCPUPinner pinner(thread);
586 
587 	pt = map->arch_data->page_mapper->GetPageTableAt(
588 		ADDR_REVERSE_SHIFT(pd[index].addr));
589 	index = VADDR_TO_PTENT(va);
590 
591 	// clear out the flags we've been requested to clear
592 	if (flags & PAGE_MODIFIED) {
593 		pt[index].dirty = 0;
594 		tlb_flush = true;
595 	}
596 	if (flags & PAGE_ACCESSED) {
597 		pt[index].accessed = 0;
598 		tlb_flush = true;
599 	}
600 
601 	pinner.Unlock();
602 
603 	if (tlb_flush) {
604 		if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) {
605 			map->arch_data->pages_to_invalidate[
606 				map->arch_data->num_invalidate_pages] = va;
607 		}
608 
609 		map->arch_data->num_invalidate_pages++;
610 	}
611 
612 	return B_OK;
613 }
614 
615 
616 static void
617 flush_tmap(vm_translation_map *map)
618 {
619 	if (map->arch_data->num_invalidate_pages <= 0)
620 		return;
621 
622 	struct thread* thread = thread_get_current_thread();
623 	thread_pin_to_current_cpu(thread);
624 
625 	if (map->arch_data->num_invalidate_pages > PAGE_INVALIDATE_CACHE_SIZE) {
626 		// invalidate all pages
627 		TRACE(("flush_tmap: %d pages to invalidate, invalidate all\n",
628 			map->arch_data->num_invalidate_pages));
629 
630 		if (IS_KERNEL_MAP(map)) {
631 			arch_cpu_global_TLB_invalidate();
632 			smp_send_broadcast_ici(SMP_MSG_GLOBAL_INVALIDATE_PAGES, 0, 0, 0,
633 				NULL, SMP_MSG_FLAG_SYNC);
634 		} else {
635 			arch_cpu_user_TLB_invalidate();
636 
637 			int cpu = smp_get_current_cpu();
638 			uint32 cpuMask = map->arch_data->active_on_cpus
639 				& ~((uint32)1 << cpu);
640 			if (cpuMask != 0) {
641 				smp_send_multicast_ici(cpuMask, SMP_MSG_USER_INVALIDATE_PAGES,
642 					0, 0, 0, NULL, SMP_MSG_FLAG_SYNC);
643 			}
644 		}
645 	} else {
646 		TRACE(("flush_tmap: %d pages to invalidate, invalidate list\n",
647 			map->arch_data->num_invalidate_pages));
648 
649 		arch_cpu_invalidate_TLB_list(map->arch_data->pages_to_invalidate,
650 			map->arch_data->num_invalidate_pages);
651 
652 		if (IS_KERNEL_MAP(map)) {
653 			smp_send_broadcast_ici(SMP_MSG_INVALIDATE_PAGE_LIST,
654 				(uint32)map->arch_data->pages_to_invalidate,
655 				map->arch_data->num_invalidate_pages, 0, NULL,
656 				SMP_MSG_FLAG_SYNC);
657 		} else {
658 			int cpu = smp_get_current_cpu();
659 			uint32 cpuMask = map->arch_data->active_on_cpus
660 				& ~((uint32)1 << cpu);
661 			if (cpuMask != 0) {
662 				smp_send_multicast_ici(cpuMask, SMP_MSG_INVALIDATE_PAGE_LIST,
663 					(uint32)map->arch_data->pages_to_invalidate,
664 					map->arch_data->num_invalidate_pages, 0, NULL,
665 					SMP_MSG_FLAG_SYNC);
666 			}
667 		}
668 	}
669 	map->arch_data->num_invalidate_pages = 0;
670 
671 	thread_unpin_from_current_cpu(thread);
672 }
673 
674 
675 static vm_translation_map_ops tmap_ops = {
676 	destroy_tmap,
677 	lock_tmap,
678 	unlock_tmap,
679 	map_max_pages_need,
680 	map_tmap,
681 	unmap_tmap,
682 	query_tmap,
683 	query_tmap_interrupt,
684 	get_mapped_size_tmap,
685 	protect_tmap,
686 	clear_flags_tmap,
687 	flush_tmap
688 
689 	// The physical page ops are initialized by the respective physical page
690 	// mapper.
691 };
692 
693 
694 //	#pragma mark -
695 
696 
697 void
698 x86_early_prepare_page_tables(page_table_entry* pageTables, addr_t address,
699 	size_t size)
700 {
701 	memset(pageTables, 0, B_PAGE_SIZE * (size / (B_PAGE_SIZE * 1024)));
702 
703 	// put the array of pgtables directly into the kernel pagedir
704 	// these will be wired and kept mapped into virtual space to be easy to get
705 	// to
706 	{
707 		addr_t virtualTable = (addr_t)pageTables;
708 
709 		for (size_t i = 0; i < (size / (B_PAGE_SIZE * 1024));
710 				i++, virtualTable += B_PAGE_SIZE) {
711 			addr_t physicalTable;
712 			early_query(virtualTable, &physicalTable);
713 			page_directory_entry* entry = &sPageHolePageDir[
714 				(address / (B_PAGE_SIZE * 1024)) + i];
715 			x86_put_pgtable_in_pgdir(entry, physicalTable,
716 				B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
717 		}
718 	}
719 }
720 
721 
722 //	#pragma mark -
723 //	VM API
724 
725 
726 status_t
727 arch_vm_translation_map_init_map(vm_translation_map *map, bool kernel)
728 {
729 	if (map == NULL)
730 		return B_BAD_VALUE;
731 
732 	TRACE(("vm_translation_map_create\n"));
733 
734 	// initialize the new object
735 	map->ops = &tmap_ops;
736 	map->map_count = 0;
737 
738 	recursive_lock_init(&map->lock, "translation map");
739 	CObjectDeleter<recursive_lock> lockDeleter(&map->lock,
740 		&recursive_lock_destroy);
741 
742 	map->arch_data = new(std::nothrow) vm_translation_map_arch_info;
743 	if (map->arch_data == NULL)
744 		return B_NO_MEMORY;
745 	ObjectDeleter<vm_translation_map_arch_info> archInfoDeleter(map->arch_data);
746 
747 	map->arch_data->active_on_cpus = 0;
748 	map->arch_data->num_invalidate_pages = 0;
749 	map->arch_data->page_mapper = NULL;
750 
751 	if (!kernel) {
752 		// user
753 		// allocate a physical page mapper
754 		status_t error = gPhysicalPageMapper
755 			->CreateTranslationMapPhysicalPageMapper(
756 				&map->arch_data->page_mapper);
757 		if (error != B_OK)
758 			return error;
759 
760 		// allocate a pgdir
761 		map->arch_data->pgdir_virt = (page_directory_entry *)memalign(
762 			B_PAGE_SIZE, B_PAGE_SIZE);
763 		if (map->arch_data->pgdir_virt == NULL) {
764 			map->arch_data->page_mapper->Delete();
765 			return B_NO_MEMORY;
766 		}
767 		vm_get_page_mapping(vm_kernel_address_space_id(),
768 			(addr_t)map->arch_data->pgdir_virt,
769 			(addr_t*)&map->arch_data->pgdir_phys);
770 	} else {
771 		// kernel
772 		// get the physical page mapper
773 		map->arch_data->page_mapper = gKernelPhysicalPageMapper;
774 
775 		// we already know the kernel pgdir mapping
776 		map->arch_data->pgdir_virt = sKernelVirtualPageDirectory;
777 		map->arch_data->pgdir_phys = sKernelPhysicalPageDirectory;
778 	}
779 
780 	// zero out the bottom portion of the new pgdir
781 	memset(map->arch_data->pgdir_virt + FIRST_USER_PGDIR_ENT, 0,
782 		NUM_USER_PGDIR_ENTS * sizeof(page_directory_entry));
783 
784 	// insert this new map into the map list
785 	{
786 		int state = disable_interrupts();
787 		acquire_spinlock(&sTMapListLock);
788 
789 		// copy the top portion of the pgdir from the current one
790 		memcpy(map->arch_data->pgdir_virt + FIRST_KERNEL_PGDIR_ENT,
791 			sKernelVirtualPageDirectory + FIRST_KERNEL_PGDIR_ENT,
792 			NUM_KERNEL_PGDIR_ENTS * sizeof(page_directory_entry));
793 
794 		sTMapList.Add(map->arch_data);
795 
796 		release_spinlock(&sTMapListLock);
797 		restore_interrupts(state);
798 	}
799 
800 	archInfoDeleter.Detach();
801 	lockDeleter.Detach();
802 
803 	return B_OK;
804 }
805 
806 
807 status_t
808 arch_vm_translation_map_init_kernel_map_post_sem(vm_translation_map *map)
809 {
810 	return B_OK;
811 }
812 
813 
814 status_t
815 arch_vm_translation_map_init(kernel_args *args)
816 {
817 	TRACE(("vm_translation_map_init: entry\n"));
818 
819 	// page hole set up in stage2
820 	sPageHole = (page_table_entry *)args->arch_args.page_hole;
821 	// calculate where the pgdir would be
822 	sPageHolePageDir = (page_directory_entry*)
823 		(((addr_t)args->arch_args.page_hole)
824 			+ (B_PAGE_SIZE * 1024 - B_PAGE_SIZE));
825 	// clear out the bottom 2 GB, unmap everything
826 	memset(sPageHolePageDir + FIRST_USER_PGDIR_ENT, 0,
827 		sizeof(page_directory_entry) * NUM_USER_PGDIR_ENTS);
828 
829 	sKernelPhysicalPageDirectory = (page_directory_entry*)
830 		args->arch_args.phys_pgdir;
831 	sKernelVirtualPageDirectory = (page_directory_entry*)
832 		args->arch_args.vir_pgdir;
833 
834 	B_INITIALIZE_SPINLOCK(&sTMapListLock);
835 	new (&sTMapList) ArchTMapList;
836 
837 // TODO: Select the best page mapper!
838 	large_memory_physical_page_ops_init(args, &tmap_ops);
839 
840 	// enable global page feature if available
841 	if (x86_check_feature(IA32_FEATURE_PGE, FEATURE_COMMON)) {
842 		// this prevents kernel pages from being flushed from TLB on
843 		// context-switch
844 		x86_write_cr4(x86_read_cr4() | IA32_CR4_GLOBAL_PAGES);
845 	}
846 
847 	TRACE(("vm_translation_map_init: done\n"));
848 
849 	return B_OK;
850 }
851 
852 
853 status_t
854 arch_vm_translation_map_init_post_sem(kernel_args *args)
855 {
856 	return B_OK;
857 }
858 
859 
860 status_t
861 arch_vm_translation_map_init_post_area(kernel_args *args)
862 {
863 	// now that the vm is initialized, create a region that represents
864 	// the page hole
865 	void *temp;
866 	status_t error;
867 	area_id area;
868 
869 	TRACE(("vm_translation_map_init_post_area: entry\n"));
870 
871 	// unmap the page hole hack we were using before
872 	sKernelVirtualPageDirectory[1023].present = 0;
873 	sPageHolePageDir = NULL;
874 	sPageHole = NULL;
875 
876 	temp = (void *)sKernelVirtualPageDirectory;
877 	area = create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE,
878 		B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
879 	if (area < B_OK)
880 		return area;
881 
882 	error = gPhysicalPageMapper->InitPostArea(args);
883 	if (error != B_OK)
884 		return error;
885 
886 	TRACE(("vm_translation_map_init_post_area: done\n"));
887 	return B_OK;
888 }
889 
890 
891 // XXX horrible back door to map a page quickly regardless of translation map
892 // object, etc.
893 // used only during VM setup.
894 // uses a 'page hole' set up in the stage 2 bootloader. The page hole is created
895 // by pointing one of the pgdir entries back at itself, effectively mapping the
896 // contents of all of the 4MB of pagetables into a 4 MB region. It's only used
897 // here, and is later unmapped.
898 
899 status_t
900 arch_vm_translation_map_early_map(kernel_args *args, addr_t va, addr_t pa,
901 	uint8 attributes, addr_t (*get_free_page)(kernel_args *))
902 {
903 	int index;
904 
905 	TRACE(("early_tmap: entry pa 0x%lx va 0x%lx\n", pa, va));
906 
907 	// check to see if a page table exists for this range
908 	index = VADDR_TO_PDENT(va);
909 	if (sPageHolePageDir[index].present == 0) {
910 		addr_t pgtable;
911 		page_directory_entry *e;
912 		// we need to allocate a pgtable
913 		pgtable = get_free_page(args);
914 		// pgtable is in pages, convert to physical address
915 		pgtable *= B_PAGE_SIZE;
916 
917 		TRACE(("early_map: asked for free page for pgtable. 0x%lx\n", pgtable));
918 
919 		// put it in the pgdir
920 		e = &sPageHolePageDir[index];
921 		x86_put_pgtable_in_pgdir(e, pgtable, attributes);
922 
923 		// zero it out in it's new mapping
924 		memset((unsigned int*)((addr_t)sPageHole
925 			+ (va / B_PAGE_SIZE / 1024) * B_PAGE_SIZE), 0, B_PAGE_SIZE);
926 	}
927 
928 	// now, fill in the pentry
929 	put_page_table_entry_in_pgtable(sPageHole + va / B_PAGE_SIZE, pa,
930 		attributes, IS_KERNEL_ADDRESS(va));
931 
932 	arch_cpu_invalidate_TLB_range(va, va);
933 
934 	return B_OK;
935 }
936 
937