X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FKernel%2Fvfs%2Fmmap.c;h=9016d564bd921f7c3d13090c35e9aaac8d65e349;hb=5cab4c07bc13888dc7956194ef9595508072a4eb;hp=c5015e67f02ffc1b6b3bfda5fb03bcdf75592edf;hpb=e02f66c7125bf18f77c6c53587238cbd49da2c89;p=tpg%2Facess2.git diff --git a/KernelLand/Kernel/vfs/mmap.c b/KernelLand/Kernel/vfs/mmap.c index c5015e67..9016d564 100644 --- a/KernelLand/Kernel/vfs/mmap.c +++ b/KernelLand/Kernel/vfs/mmap.c @@ -10,6 +10,7 @@ #include #include #include +#include // MM_USER_MAX #define MMAP_PAGES_PER_BLOCK 16 @@ -22,70 +23,91 @@ struct sVFS_MMapPageBlock tPAddr PhysAddrs[MMAP_PAGES_PER_BLOCK]; }; +// === PROTOTYPES === +//void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset); +void *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask); +int VFS_MMap_MapPage(tVFS_Node *Node, unsigned int PageNum, tVFS_MMapPageBlock *pb, void *mapping_dest, unsigned int Protection); +//int VFS_MUnmap(void *Addr, size_t Length); +bool _range_free(const tPage *Base, Uint NumPages); + // === CODE === void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset) { - tVFS_Handle *h; - tVAddr mapping_dest, mapping_base; - int npages, pagenum; - tVFS_MMapPageBlock *pb, *prev; - - ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset); + ENTER("pDestHint xLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset); if( Flags & MMAP_MAP_ANONYMOUS ) Offset = (tVAddr)DestHint & 0xFFF; - npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE; - pagenum = Offset / PAGE_SIZE; - - mapping_base = (tVAddr)DestHint; - mapping_dest = mapping_base & ~(PAGE_SIZE-1); + unsigned int npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE; + unsigned int pagenum = Offset / PAGE_SIZE; + LOG("npages=%u,pagenum=%u", npages, pagenum); - // TODO: Locate space for the allocation + tVAddr mapping_base = (tVAddr)DestHint; - // Handle anonymous mappings - if( Flags & MMAP_MAP_ANONYMOUS ) + if( Flags & MMAP_MAP_FIXED ) + { + ASSERT( (Flags & MMAP_MAP_FIXED) && DestHint != NULL ); + // Keep and use the hint + // - TODO: Validate that the region pointed to by the hint is correct + } + else { - size_t ofs = 0; - LOG("%i pages anonymous to %p", npages, mapping_dest); - for( ; npages --; mapping_dest += PAGE_SIZE, ofs += PAGE_SIZE ) + Log_Warning("VFS", "MMap: TODO Handle non-fixed mappings"); + + // Locate a free location in the address space (between brk and MM_USER_MAX) + // TODO: Prefer first location after DestHint, but can go below + + // Search downwards from the top of user memory + mapping_base = 0; + for( tPage *dst = (tPage*)MM_USER_MAX - npages; dst > (tPage*)PAGE_SIZE; dst -- ) { - if( MM_GetPhysAddr((void*)mapping_dest) ) { - // TODO: Set flags to COW if needed (well, if shared) - MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); - LOG("clear from %p, %i bytes", (void*)(mapping_base + ofs), - PAGE_SIZE - (mapping_base & (PAGE_SIZE-1)) - ); - memset( (void*)(mapping_base + ofs), 0, PAGE_SIZE - (mapping_base & (PAGE_SIZE-1))); - } - else { - LOG("New empty page"); - // TODO: Map a COW zero page instead - if( !MM_Allocate(mapping_dest) ) { - // TODO: Error - Log_Warning("VFS", "VFS_MMap: Anon alloc to %p failed", mapping_dest); - } - memset((void*)mapping_dest, 0, PAGE_SIZE); - LOG("Anon map to %p", mapping_dest); + if( _range_free(dst, npages) ) { + mapping_base = (tVAddr)dst; + break; } } - LEAVE_RET('p', (void*)mapping_base); + if( mapping_base == 0 ) + { + Log_Warning("VFS", "MMap: Out of address space"); + errno = ENOMEM; + LEAVE('n'); + return NULL; + } } + tPage *mapping_dest = (void*)(mapping_base & ~(PAGE_SIZE-1)); - h = VFS_GetHandle(FD); + if( !_range_free(mapping_dest, npages) ) + { + LOG("Specified range is not free"); + //errno = EINVAL; + //LEAVE('n'); + //return NULL; + Log_Warning("VFS", "MMap: Overwriting/replacing maps at %p+%x", mapping_base, Length); + } + + // Handle anonymous mappings + if( Flags & MMAP_MAP_ANONYMOUS ) + { + // TODO: Comvert \a Protection into a flag set + void *ret = VFS_MMap_Anon((void*)mapping_base, Length, 0, 0); + LEAVE_RET('p', ret); + } + + tVFS_Handle *h = VFS_GetHandle(FD); if( !h || !h->Node ) LEAVE_RET('n', NULL); LOG("h = %p", h); Mutex_Acquire( &h->Node->Lock ); + tVFS_MMapPageBlock *pb, **pb_pnp = (tVFS_MMapPageBlock**)&h->Node->MMapInfo; // Search for existing mapping for each page // - Sorted list of 16 page blocks - for( - pb = h->Node->MMapInfo, prev = NULL; - pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum; - prev = pb, pb = pb->Next - ); + for( pb = h->Node->MMapInfo; pb; pb_pnp = &pb->Next, pb = pb->Next ) + { + if( pb->BaseOffset + MMAP_PAGES_PER_BLOCK > pagenum ) + break; + } LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0); @@ -100,71 +122,22 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, } pb->Next = old_pb; pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK; - if(prev) - prev->Next = pb; - else - h->Node->MMapInfo = pb; + *pb_pnp = pb; } // - Map (and allocate) pages while( npages -- ) { - if( MM_GetPhysAddr( (void*)mapping_dest ) == 0 ) + ASSERTC( pagenum, >=, pb->BaseOffset ); + ASSERTC( pagenum - pb->BaseOffset, <, MMAP_PAGES_PER_BLOCK ); + if( MM_GetPhysAddr( mapping_dest ) == 0 ) { - if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 ) - { - tVFS_NodeType *nt = h->Node->Type; - if( !nt ) - { - // TODO: error - } - else if( nt->MMap ) - nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); - else - { - int read_len; - // Allocate pages and read data - if( MM_Allocate(mapping_dest) == 0 ) { - // TODO: Unwrap - Mutex_Release( &h->Node->Lock ); - LEAVE('n'); - return NULL; - } - // TODO: Clip read length - read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); - // TODO: This was commented out, why? - if( read_len != PAGE_SIZE ) { - memset( (void*)(mapping_dest+read_len), 0, PAGE_SIZE-read_len ); - } - } - pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( (void*)mapping_dest ); - MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node ); - MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); - LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, - pb->PhysAddrs[pagenum - pb->BaseOffset]); - } - else + LOG("Map page to %p", mapping_dest); + if( VFS_MMap_MapPage(h->Node, pagenum, pb, mapping_dest, Protection) ) { - MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] ); - MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); - LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, - pb->PhysAddrs[pagenum - pb->BaseOffset]); - } - h->Node->ReferenceCount ++; - - // Set flags - if( !(Protection & MMAP_PROT_WRITE) ) { - MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO); - } - else { - MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); - } - - if( Protection & MMAP_PROT_EXEC ) { - MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC); - } - else { - MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC); + Mutex_Release( &h->Node->Lock ); + LEAVE('n'); + return NULL; } } else @@ -175,25 +148,27 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); } } - if( Flags & MMAP_MAP_PRIVATE ) + if( Flags & MMAP_MAP_PRIVATE ) { + // TODO: Don't allow the page to change underneath either MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); + } pagenum ++; - mapping_dest += PAGE_SIZE; + mapping_dest ++; // Roll on to next block if needed if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK) { - if( pb->Next && pb->Next->BaseOffset == pagenum ) - pb = pb->Next; - else + if( !pb->Next || pb->Next->BaseOffset != pagenum ) { - tVFS_MMapPageBlock *oldpb = pb; - pb = malloc( sizeof(tVFS_MMapPageBlock) ); - pb->Next = oldpb->Next; - pb->BaseOffset = pagenum; - memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs)); - oldpb->Next = pb; + if( pb->Next ) ASSERTC(pb->Next->BaseOffset % MMAP_PAGES_PER_BLOCK, ==, 0); + tVFS_MMapPageBlock *newpb = malloc( sizeof(tVFS_MMapPageBlock) ); + newpb->Next = pb->Next; + newpb->BaseOffset = pagenum; + memset(newpb->PhysAddrs, 0, sizeof(newpb->PhysAddrs)); + pb->Next = newpb; } + + pb = pb->Next; } } @@ -203,7 +178,149 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, return (void*)mapping_base; } +void *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask) +{ + size_t ofs = (tVAddr)Destination & (PAGE_SIZE-1); + tPage *mapping_dest = (void*)( (char*)Destination - ofs ); + + if( ofs > 0 ) + { + size_t bytes = MIN(PAGE_SIZE - ofs, Length); + + // Allocate a partial page + if( MM_GetPhysAddr(mapping_dest) ) + { + // Already allocated page, clear the area we're touching + ASSERT( ofs + bytes <= PAGE_SIZE ); + + // TODO: Double check that this area isn't already zero + memset( Destination, 0, bytes ); + + MM_SetFlags(mapping_dest, FlagsSet, FlagsMask); + + LOG("#1: Clear %i from %p", Length, Destination); + } + else + { + MM_AllocateZero(mapping_dest); + LOG("#1: Allocate for %p", Destination); + } + mapping_dest ++; + Length -= bytes; + } + while( Length >= PAGE_SIZE ) + { + if( MM_GetPhysAddr( mapping_dest ) ) + { + // We're allocating entire pages here, so free this page and replace with a COW zero + MM_Deallocate(mapping_dest); + LOG("Replace %p with zero page", mapping_dest); + } + else + { + LOG("Allocate zero at %p", mapping_dest); + } + MM_AllocateZero(mapping_dest); + + mapping_dest ++; + Length -= PAGE_SIZE; + } + if( Length > 0 ) + { + ASSERT(Length < PAGE_SIZE); + + // Tail page + if( MM_GetPhysAddr(mapping_dest) ) + { + // TODO: Don't touch page if already zero + memset( mapping_dest, 0, Length ); + LOG("Clear %i in %p", Length, mapping_dest); + } + else + { + MM_AllocateZero(mapping_dest); + LOG("Anon map to %p", mapping_dest); + } + } + + return Destination; +} + +int VFS_MMap_MapPage(tVFS_Node *Node, unsigned int pagenum, tVFS_MMapPageBlock *pb, void *mapping_dest, unsigned int Protection) +{ + if( pb->PhysAddrs[pagenum - pb->BaseOffset] != 0 ) + { + MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] ); + MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); + LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, + pb->PhysAddrs[pagenum - pb->BaseOffset]); + } + else + { + tVFS_NodeType *nt = Node->Type; + if( !nt ) + { + // TODO: error + } + else if( nt->MMap ) + nt->MMap(Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest); + else + { + int read_len; + // Allocate pages and read data + if( MM_Allocate(mapping_dest) == 0 ) { + // TODO: Unwrap + return 1; + } + // TODO: Clip read length + read_len = nt->Read(Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest, 0); + // TODO: This was commented out, why? + if( read_len != PAGE_SIZE ) { + memset( (char*)mapping_dest + read_len, 0, PAGE_SIZE-read_len ); + } + } + pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest ); + MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], Node ); + MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); + LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, + pb->PhysAddrs[pagenum - pb->BaseOffset]); + } + // TODO: Huh? + Node->ReferenceCount ++; + + // Set flags + if( !(Protection & MMAP_PROT_WRITE) ) { + MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO); + } + else { + MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); + } + + if( Protection & MMAP_PROT_EXEC ) { + MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC); + } + else { + MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC); + } + + return 0; +} + int VFS_MUnmap(void *Addr, size_t Length) { + UNIMPLEMENTED(); return 0; } + +bool _range_free(const tPage *Base, Uint NumPages) +{ + for( int i = 0; i < NumPages; i ++ ) + { + if( MM_GetPhysAddr(Base + i) ) + { + // Oh. + return false; + } + } + return true; +}