diff --git a/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.cpp b/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.cpp new file mode 100644 index 0000000000..ec6e1192dd --- /dev/null +++ b/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + +#include "generic_vm_physical_page_mapper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +//#define TRACE_VM_PHYSICAL_PAGE_MAPPER +#ifdef TRACE_VM_PHYSICAL_PAGE_MAPPER +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + +// data and structures used to represent physical pages mapped into iospace +typedef struct paddr_chunk_descriptor { + struct paddr_chunk_descriptor *next_q; + // must remain first in structure, queue code uses it + int ref_count; + addr_t va; +} paddr_chunk_desc; + +static paddr_chunk_desc *paddr_desc; // will be one per physical chunk +static paddr_chunk_desc **virtual_pmappings; // will be one ptr per virtual chunk in iospace +static int first_free_vmapping; +static int num_virtual_chunks; +static queue mapped_paddr_lru; +static mutex iospace_mutex; +static sem_id iospace_full_sem; + +static generic_map_iospace_chunk_func sMapIOSpaceChunk; +static addr_t sIOSpaceBase; +static size_t sIOSpaceSize; +static size_t sIOSpaceChunkSize; + + +status_t +generic_get_physical_page(addr_t pa, addr_t *va, uint32 flags) +{ + int index; + paddr_chunk_desc *replaced_pchunk; + +restart: + mutex_lock(&iospace_mutex); + + // see if the page is already mapped + index = pa / sIOSpaceChunkSize; + if (paddr_desc[index].va != 0) { + if (paddr_desc[index].ref_count++ == 0) { + // pull this descriptor out of the lru list + queue_remove_item(&mapped_paddr_lru, &paddr_desc[index]); + } + *va = paddr_desc[index].va + pa % sIOSpaceChunkSize; + mutex_unlock(&iospace_mutex); + return B_OK; + } + + // map it + if (first_free_vmapping < num_virtual_chunks) { + // there's a free hole + paddr_desc[index].va = first_free_vmapping * sIOSpaceChunkSize + sIOSpaceBase; + *va = paddr_desc[index].va + pa % sIOSpaceChunkSize; + virtual_pmappings[first_free_vmapping] = &paddr_desc[index]; + paddr_desc[index].ref_count++; + + // push up the first_free_vmapping pointer + for (; first_free_vmapping < num_virtual_chunks; first_free_vmapping++) { + if(virtual_pmappings[first_free_vmapping] == NULL) + break; + } + + sMapIOSpaceChunk(paddr_desc[index].va, + index * sIOSpaceChunkSize); + mutex_unlock(&iospace_mutex); + + return B_OK; + } + + // replace an earlier mapping + if (queue_peek(&mapped_paddr_lru) == NULL) { + // no free slots available + if (flags == PHYSICAL_PAGE_NO_WAIT) { + // punt back to the caller and let them handle this + mutex_unlock(&iospace_mutex); + return B_NO_MEMORY; + } else { + mutex_unlock(&iospace_mutex); + acquire_sem(iospace_full_sem); + goto restart; + } + } + + replaced_pchunk = (paddr_chunk_desc*)queue_dequeue(&mapped_paddr_lru); + paddr_desc[index].va = replaced_pchunk->va; + replaced_pchunk->va = 0; + *va = paddr_desc[index].va + pa % sIOSpaceChunkSize; + paddr_desc[index].ref_count++; + + sMapIOSpaceChunk(paddr_desc[index].va, index * sIOSpaceChunkSize); + + mutex_unlock(&iospace_mutex); + return B_OK; +} + + +status_t +generic_put_physical_page(addr_t va) +{ + paddr_chunk_desc *desc; + + if (va < sIOSpaceBase || va >= sIOSpaceBase + sIOSpaceSize) + panic("someone called put_physical_page on an invalid va 0x%lx\n", va); + va -= sIOSpaceBase; + + mutex_lock(&iospace_mutex); + + desc = virtual_pmappings[va / sIOSpaceChunkSize]; + if (desc == NULL) { + mutex_unlock(&iospace_mutex); + panic("put_physical_page called on page at va 0x%lx which is not checked out\n", va); + return B_ERROR; + } + + if (--desc->ref_count == 0) { + // put it on the mapped lru list + queue_enqueue(&mapped_paddr_lru, desc); + // no sense rescheduling on this one, there's likely a race in the waiting + // thread to grab the iospace_mutex, which would block and eventually get back to + // this thread. waste of time. + release_sem_etc(iospace_full_sem, 1, B_DO_NOT_RESCHEDULE); + } + + mutex_unlock(&iospace_mutex); + + return B_OK; +} + + +// #pragma mark - +// VM API + + +status_t +generic_vm_physical_page_mapper_init(kernel_args *args, + generic_map_iospace_chunk_func mapIOSpaceChunk, addr_t *ioSpaceBase, + size_t ioSpaceSize, size_t ioSpaceChunkSize) +{ + TRACE(("generic_vm_physical_page_mapper_init: entry\n")); + + sMapIOSpaceChunk = mapIOSpaceChunk; + sIOSpaceBase = *ioSpaceBase; + sIOSpaceSize = ioSpaceSize; + sIOSpaceChunkSize = ioSpaceChunkSize; + + // allocate some space to hold physical page mapping info + paddr_desc = (paddr_chunk_desc *)vm_alloc_from_kernel_args(args, + sizeof(paddr_chunk_desc) * 1024, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + num_virtual_chunks = sIOSpaceSize / sIOSpaceChunkSize; + virtual_pmappings = (paddr_chunk_desc **)vm_alloc_from_kernel_args(args, + sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + + TRACE(("paddr_desc %p, virtual_pmappings %p"/*", iospace_pgtables %p"*/"\n", + paddr_desc, virtual_pmappings/*, iospace_pgtables*/)); + + // initialize our data structures + memset(paddr_desc, 0, sizeof(paddr_chunk_desc) * 1024); + memset(virtual_pmappings, 0, sizeof(paddr_chunk_desc *) * num_virtual_chunks); + first_free_vmapping = 0; + queue_init(&mapped_paddr_lru); + iospace_mutex.sem = -1; + iospace_mutex.holder = -1; + iospace_full_sem = -1; + + TRACE(("generic_vm_physical_page_mapper_init: done\n")); + + return B_OK; +} + + +status_t +generic_vm_physical_page_mapper_init_post_area(kernel_args *args) +{ + void *temp; + + TRACE(("generic_vm_physical_page_mapper_init_post_area: entry\n")); + + temp = (void *)paddr_desc; + create_area("physical_page_mapping_descriptors", &temp, B_EXACT_ADDRESS, + ROUNDUP(sizeof(paddr_chunk_desc) * 1024, B_PAGE_SIZE), + B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + + temp = (void *)virtual_pmappings; + create_area("iospace_virtual_chunk_descriptors", &temp, B_EXACT_ADDRESS, + ROUNDUP(sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_PAGE_SIZE), + B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + + TRACE(("generic_vm_physical_page_mapper_init_post_area: creating iospace\n")); + temp = (void *)sIOSpaceBase; + area_id ioSpaceArea = vm_create_null_area(vm_kernel_address_space_id(), + "iospace", &temp, B_EXACT_ADDRESS, sIOSpaceSize); + // TODO: We don't reserve the virtual address space for the IO space in + // generic_vm_physical_page_mapper_init() yet. So theoretically it could + // happen that a part of that space has been reserved by someone else in + // the meantime. + if (ioSpaceArea < 0) { + panic("generic_vm_physical_page_mapper_init_post_area(): Failed to " + "create null area for IO space!\n"); + return ioSpaceArea; + } + + TRACE(("generic_vm_physical_page_mapper_init_post_area: done\n")); + + return B_OK; +} + + +status_t +generic_vm_physical_page_mapper_init_post_sem(kernel_args *args) +{ + mutex_init(&iospace_mutex, "iospace_mutex"); + iospace_full_sem = create_sem(1, "iospace_full_sem"); + + return iospace_full_sem >= B_OK ? B_OK : iospace_full_sem; +} diff --git a/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.h b/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.h new file mode 100644 index 0000000000..ea1ea4127c --- /dev/null +++ b/src/system/kernel/arch/generic/generic_vm_physical_page_mapper.h @@ -0,0 +1,29 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H +#define _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef status_t (*generic_map_iospace_chunk_func)(addr_t, addr_t); + +status_t generic_get_physical_page(addr_t pa, addr_t *va, uint32 flags); +status_t generic_put_physical_page(addr_t va); +status_t generic_vm_physical_page_mapper_init(kernel_args *args, + generic_map_iospace_chunk_func mapIOSpaceChunk, addr_t *ioSpaceBase, + size_t ioSpaceSize, size_t ioSpaceChunkSize); +status_t generic_vm_physical_page_mapper_init_post_area(kernel_args *args); +status_t generic_vm_physical_page_mapper_init_post_sem(kernel_args *args); + +#ifdef __cplusplus +} +#endif + +#endif // _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile index 07203eda4d..fbe3e59c07 100644 --- a/src/system/kernel/arch/x86/Jamfile +++ b/src/system/kernel/arch/x86/Jamfile @@ -3,6 +3,9 @@ SubDir HAIKU_TOP src system kernel arch x86 ; SubDirHdrs [ FDirName $(TARGET_COMMON_DEBUG_OBJECT_DIR) system kernel ] ; # for syscall_numbers.h SubDirHdrs $(HAIKU_TOP) src add-ons kernel drivers input ps2_hid ; +SubDirHdrs $(SUBDIR) $(DOTDOT) generic ; + +SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) generic ] ; KernelStaticLibrary libx86 : arch_cpu.c @@ -24,6 +27,8 @@ KernelStaticLibrary libx86 : arch_user_debugger.cpp bios.cpp cpuid.S + + generic_vm_physical_page_mapper.cpp : $(TARGET_KERNEL_PIC_CCFLAGS) -Wno-unused ; diff --git a/src/system/kernel/arch/x86/arch_vm_translation_map.c b/src/system/kernel/arch/x86/arch_vm_translation_map.c index d046ec3ce2..5f87a24411 100644 --- a/src/system/kernel/arch/x86/arch_vm_translation_map.c +++ b/src/system/kernel/arch/x86/arch_vm_translation_map.c @@ -19,6 +19,8 @@ #include #include +#include "generic_vm_physical_page_mapper.h" + //#define TRACE_VM_TMAP #ifdef TRACE_VM_TMAP # define TRACE(x) dprintf x @@ -61,21 +63,7 @@ typedef struct page_directory_entry { uint32 addr:20; } page_directory_entry; -// data and structures used to represent physical pages mapped into iospace -typedef struct paddr_chunk_descriptor { - struct paddr_chunk_descriptor *next_q; // must remain first in structure, queue code uses it - int ref_count; - addr_t va; -} paddr_chunk_desc; - -static paddr_chunk_desc *paddr_desc; // will be one per physical chunk -static paddr_chunk_desc **virtual_pmappings; // will be one ptr per virtual chunk in iospace -static int first_free_vmapping; -static int num_virtual_chunks; -static queue mapped_paddr_lru; static page_table_entry *iospace_pgtables = NULL; -static mutex iospace_mutex; -static sem_id iospace_full_sem; #define PAGE_INVALIDATE_CACHE_SIZE 64 @@ -96,6 +84,8 @@ static page_directory_entry *sKernelVirtualPageDirectory = NULL; static vm_translation_map *tmap_list; static spinlock tmap_list_lock; +static addr_t sIOSpaceBase; + #define CHATTY_TMAP 0 #define ADDR_SHIFT(x) ((x)>>12) @@ -648,11 +638,11 @@ map_iospace_chunk(addr_t va, addr_t pa) pa &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned va &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned - if (va < IOSPACE_BASE || va >= (IOSPACE_BASE + IOSPACE_SIZE)) + if (va < sIOSpaceBase || va >= (sIOSpaceBase + IOSPACE_SIZE)) panic("map_iospace_chunk: passed invalid va 0x%lx\n", va); ppn = ADDR_SHIFT(pa); - pt = &iospace_pgtables[(va - IOSPACE_BASE) / B_PAGE_SIZE]; + pt = &iospace_pgtables[(va - sIOSpaceBase) / B_PAGE_SIZE]; for (i = 0; i < 1024; i++) { init_page_table_entry(&pt[i]); pt[i].addr = ppn + i; @@ -676,101 +666,14 @@ map_iospace_chunk(addr_t va, addr_t pa) static status_t get_physical_page_tmap(addr_t pa, addr_t *va, uint32 flags) { - int index; - paddr_chunk_desc *replaced_pchunk; - -restart: - mutex_lock(&iospace_mutex); - - // see if the page is already mapped - index = pa / IOSPACE_CHUNK_SIZE; - if (paddr_desc[index].va != 0) { - if (paddr_desc[index].ref_count++ == 0) { - // pull this descriptor out of the lru list - queue_remove_item(&mapped_paddr_lru, &paddr_desc[index]); - } - *va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE; - mutex_unlock(&iospace_mutex); - return B_OK; - } - - // map it - if (first_free_vmapping < num_virtual_chunks) { - // there's a free hole - paddr_desc[index].va = first_free_vmapping * IOSPACE_CHUNK_SIZE + IOSPACE_BASE; - *va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE; - virtual_pmappings[first_free_vmapping] = &paddr_desc[index]; - paddr_desc[index].ref_count++; - - // push up the first_free_vmapping pointer - for (; first_free_vmapping < num_virtual_chunks; first_free_vmapping++) { - if(virtual_pmappings[first_free_vmapping] == NULL) - break; - } - - map_iospace_chunk(paddr_desc[index].va, index * IOSPACE_CHUNK_SIZE); - mutex_unlock(&iospace_mutex); - - return B_OK; - } - - // replace an earlier mapping - if (queue_peek(&mapped_paddr_lru) == NULL) { - // no free slots available - if (flags == PHYSICAL_PAGE_NO_WAIT) { - // punt back to the caller and let them handle this - mutex_unlock(&iospace_mutex); - return B_NO_MEMORY; - } else { - mutex_unlock(&iospace_mutex); - acquire_sem(iospace_full_sem); - goto restart; - } - } - - replaced_pchunk = queue_dequeue(&mapped_paddr_lru); - paddr_desc[index].va = replaced_pchunk->va; - replaced_pchunk->va = 0; - *va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE; - paddr_desc[index].ref_count++; - - map_iospace_chunk(paddr_desc[index].va, index * IOSPACE_CHUNK_SIZE); - - mutex_unlock(&iospace_mutex); - return B_OK; + return generic_get_physical_page(pa, va, flags); } static status_t put_physical_page_tmap(addr_t va) { - paddr_chunk_desc *desc; - - if (va < IOSPACE_BASE || va >= IOSPACE_BASE + IOSPACE_SIZE) - panic("someone called put_physical_page on an invalid va 0x%lx\n", va); - va -= IOSPACE_BASE; - - mutex_lock(&iospace_mutex); - - desc = virtual_pmappings[va / IOSPACE_CHUNK_SIZE]; - if (desc == NULL) { - mutex_unlock(&iospace_mutex); - panic("put_physical_page called on page at va 0x%lx which is not checked out\n", va); - return B_ERROR; - } - - if (--desc->ref_count == 0) { - // put it on the mapped lru list - queue_enqueue(&mapped_paddr_lru, desc); - // no sense rescheduling on this one, there's likely a race in the waiting - // thread to grab the iospace_mutex, which would block and eventually get back to - // this thread. waste of time. - release_sem_etc(iospace_full_sem, 1, B_DO_NOT_RESCHEDULE); - } - - mutex_unlock(&iospace_mutex); - - return B_OK; + return generic_put_physical_page(va); } @@ -881,6 +784,7 @@ status_t arch_vm_translation_map_init(kernel_args *args) { cpuid_info info; + status_t error; TRACE(("vm_translation_map_init: entry\n")); @@ -898,26 +802,20 @@ arch_vm_translation_map_init(kernel_args *args) tmap_list = NULL; // allocate some space to hold physical page mapping info - paddr_desc = (paddr_chunk_desc *)vm_alloc_from_kernel_args(args, - sizeof(paddr_chunk_desc) * 1024, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - num_virtual_chunks = IOSPACE_SIZE / IOSPACE_CHUNK_SIZE; - virtual_pmappings = (paddr_chunk_desc **)vm_alloc_from_kernel_args(args, - sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); iospace_pgtables = (page_table_entry *)vm_alloc_from_kernel_args(args, B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)), B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - TRACE(("paddr_desc %p, virtual_pmappings %p, iospace_pgtables %p\n", - paddr_desc, virtual_pmappings, iospace_pgtables)); + TRACE(("iospace_pgtables %p\n", iospace_pgtables)); + + // init physical page mapper + sIOSpaceBase = IOSPACE_BASE; + error = generic_vm_physical_page_mapper_init(args, map_iospace_chunk, + &sIOSpaceBase, IOSPACE_SIZE, IOSPACE_CHUNK_SIZE); + if (error != B_OK) + return error; // initialize our data structures - memset(paddr_desc, 0, sizeof(paddr_chunk_desc) * 1024); - memset(virtual_pmappings, 0, sizeof(paddr_chunk_desc *) * num_virtual_chunks); - first_free_vmapping = 0; - queue_init(&mapped_paddr_lru); memset(iospace_pgtables, 0, B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024))); - iospace_mutex.sem = -1; - iospace_mutex.holder = -1; - iospace_full_sem = -1; TRACE(("mapping iospace_pgtables\n")); @@ -932,7 +830,7 @@ arch_vm_translation_map_init(kernel_args *args) virt_pgtable = (addr_t)iospace_pgtables; for (i = 0; i < (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)); i++, virt_pgtable += B_PAGE_SIZE) { early_query(virt_pgtable, &phys_pgtable); - e = &page_hole_pgdir[(IOSPACE_BASE / (B_PAGE_SIZE * 1024)) + i]; + e = &page_hole_pgdir[(sIOSpaceBase / (B_PAGE_SIZE * 1024)) + i]; put_pgtable_in_pgdir(e, phys_pgtable, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); } } @@ -953,10 +851,7 @@ arch_vm_translation_map_init(kernel_args *args) status_t arch_vm_translation_map_init_post_sem(kernel_args *args) { - mutex_init(&iospace_mutex, "iospace_mutex"); - iospace_full_sem = create_sem(1, "iospace_full_sem"); - - return iospace_full_sem >= B_OK ? B_OK : iospace_full_sem; + return generic_vm_physical_page_mapper_init_post_sem(args); } @@ -966,6 +861,7 @@ arch_vm_translation_map_init_post_area(kernel_args *args) // now that the vm is initialized, create a region that represents // the page hole void *temp; + status_t error; TRACE(("vm_translation_map_init_post_area: entry\n")); @@ -978,25 +874,14 @@ arch_vm_translation_map_init_post_area(kernel_args *args) create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - temp = (void *)paddr_desc; - create_area("physical_page_mapping_descriptors", &temp, B_EXACT_ADDRESS, - ROUNDUP(sizeof(paddr_chunk_desc) * 1024, B_PAGE_SIZE), - B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - - temp = (void *)virtual_pmappings; - create_area("iospace_virtual_chunk_descriptors", &temp, B_EXACT_ADDRESS, - ROUNDUP(sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_PAGE_SIZE), - B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - temp = (void *)iospace_pgtables; create_area("iospace_pgtables", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)), B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); - TRACE(("vm_translation_map_init_post_area: creating iospace\n")); - temp = (void *)IOSPACE_BASE; - vm_create_null_area(vm_kernel_address_space_id(), "iospace", &temp, - B_EXACT_ADDRESS, IOSPACE_SIZE); + error = generic_vm_physical_page_mapper_init_post_area(args); + if (error != B_OK) + return error; TRACE(("vm_translation_map_init_post_area: done\n"));