Kernel/x86 - Cleaned up PMM init (and removed mboot from pmm code)
authorJohn Hodge <[email protected]>
Wed, 18 Jul 2012 10:50:26 +0000 (18:50 +0800)
committerJohn Hodge <[email protected]>
Wed, 18 Jul 2012 10:50:26 +0000 (18:50 +0800)
KernelLand/Kernel/Makefile
KernelLand/Kernel/arch/x86/main.c
KernelLand/Kernel/arch/x86/mm_phys.c
KernelLand/Kernel/arch/x86/start.asm
KernelLand/Kernel/include/pmemmap.h [new file with mode: 0644]
KernelLand/Kernel/pmemmap.c [new file with mode: 0644]

index e391f03..7562079 100644 (file)
@@ -25,7 +25,7 @@ ASFLAGS         += -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
 CPPFLAGS       += -I./include -I./arch/$(ARCHDIR)/include -D_MODULE_NAME_=\"Kernel\"
 CPPFLAGS       += -D ARCH=$(ARCH) -D ARCHDIR=$(ARCHDIR) -D PLATFORM=\"$(PLATFORM)\" -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1
 CPPFLAGS       += -D KERNEL_VERSION=$(KERNEL_VERSION) -ffreestanding
-CFLAGS         += -Wall -fno-stack-protector -Wstrict-prototypes -g
+CFLAGS         += -Wall -fno-stack-protector -Wstrict-prototypes -std=gnu99 -g
 CFLAGS         += -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wuninitialized
 CFLAGS          += -O3
 LDFLAGS                += -T arch/$(ARCHDIR)/link.ld -g
@@ -51,6 +51,7 @@ BUILDINFO_OBJ := $(OBJDIR)buildinfo.o$(OBJSUFFIX)
 BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX)
 
 OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ))
+OBJ += pmemmap.o
 OBJ += heap.o logging.o debug.o lib.o libc.o adt.o time.o
 OBJ += drvutil_video.o drvutil_disk.o
 OBJ += messages.o modules.o syscalls.o system.o
@@ -116,7 +117,7 @@ $(OBJDIR)%.ao$(OBJSUFFIX): %.$(AS_SUFFIX) Makefile
        @mkdir -p $(dir $@)
        @$(AS) $(ASFLAGS) $< -o $@
 ifeq ($(AS_SUFFIX),S)
-       @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $<
+       @$(MAKEDEP) $(CPPFLAGS) -MT $@ -MP -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $<
 endif
 
 # C Sources
@@ -124,7 +125,7 @@ $(OBJDIR)%.o$(OBJSUFFIX): %.c Makefile
        @echo --- CC -o $@
        @mkdir -p $(dir $@)
        @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
-       @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $<
+       @$(MAKEDEP) $(CPPFLAGS) -MT $@ -MP -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $<
 
 # Build-time linked modules
 %.xo.$(ARCH):
@@ -149,3 +150,5 @@ $(BUILDINFO_OBJ): $(BUILDINFO_SRC)
 
 # Dependency Files
 -include $(DEPFILES)
+
+%.h:
index 92fef22..466b61e 100644 (file)
@@ -9,16 +9,20 @@
 #include <init.h>
 #include <mm_virt.h>
 #include <mp.h>
+#include <pmemmap.h>
 
 #define        VGA_ERRORS      0
 
+#define KERNEL_LOAD    0x100000        // 1MiB
 #define MAX_ARGSTR_POS (0x400000-0x2000)
+#define MAX_PMEMMAP_ENTS       16
 
 // === IMPORTS ===
+extern char    gKernelEnd[];
 extern void    Heap_Install(void);
 extern void    Desctab_Install(void);
 extern void    MM_PreinitVirtual(void);
-extern void    MM_Install(tMBoot_Info *MBoot);
+extern void    MM_Install(int NPMemRanges, tPMemMapEnt *PMemRanges);
 extern void    MM_InstallVirtual(void);
 extern void    Threads_Init(void);
 extern int     Time_Setup(void);
@@ -41,27 +45,80 @@ struct {
 // === CODE ===
 int kmain(Uint MbMagic, void *MbInfoPtr)
 {
-        int    i;
        tMBoot_Module   *mods;
        tMBoot_Info     *mbInfo;
+       tPMemMapEnt     pmemmap[MAX_PMEMMAP_ENTS];
+        int    nPMemMapEnts;
 
        LogF("Acess2 x86-"PLATFORM" v"EXPAND_STR(KERNEL_VERSION)"\r\n");
        LogF(" Build %i, Git Hash %s\r\n", BUILD_NUM, gsGitHash);
        
-       // Set up non-boot info dependent stuff
-       Desctab_Install();      // Set up GDT and IDT
        MM_PreinitVirtual();    // Initialise virtual mappings
-       
+
+       mbInfo = MbInfoPtr;     
+
        switch(MbMagic)
        {
        // Multiboot 1
-       case MULTIBOOT_MAGIC:
+       case MULTIBOOT_MAGIC: {
+               // TODO: Handle when this isn't in the mapped area
+               gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE);
+               
+               tMBoot_MMapEnt  *ent = (void*)mbInfo->MMapAddr;
+               tMBoot_MMapEnt  *last = (void*)(mbInfo->MMapAddr + mbInfo->MMapLength);
+               
+               // Build up memory map
+               nPMemMapEnts = 0;
+               while( ent < last && nPMemMapEnts < MAX_PMEMMAP_ENTS )
+               {
+                       tPMemMapEnt     *nent = &pmemmap[nPMemMapEnts];
+                       nent->Start = ent->Base;
+                       nent->Length = ent->Length;
+                       switch(ent->Type)
+                       {
+                       case 1:
+                               nent->Type = PMEMTYPE_FREE;
+                               break;
+                       default:
+                               nent->Type = PMEMTYPE_RESERVED;
+                               break;
+                       }
+                       nent->NUMADomain = 0;
+                       
+                       nPMemMapEnts ++;
+                       ent = (void*)( (tVAddr)ent + ent->Size + 4 );
+               }
+
+               // Ensure it's valid
+               nPMemMapEnts = PMemMap_ValidateMap(pmemmap, nPMemMapEnts, MAX_PMEMMAP_ENTS);
+               // TODO: Error handling
+
+               // Replace kernel with PMEMTYPE_USED
+               nPMemMapEnts = PMemMap_MarkRangeUsed(
+                       pmemmap, nPMemMapEnts, MAX_PMEMMAP_ENTS,
+                       KERNEL_LOAD, (tVAddr)&gKernelEnd - KERNEL_LOAD - KERNEL_BASE
+                       );
+
+               // Replace modules with PMEMTYPE_USED
+               nPMemMapEnts = PMemMap_MarkRangeUsed(pmemmap, nPMemMapEnts, MAX_PMEMMAP_ENTS,
+                       mbInfo->Modules, mbInfo->ModuleCount*sizeof(*mods)
+                       );
+               mods = (void*)mbInfo->Modules;
+               for( int i = 0; i < mbInfo->ModuleCount; i ++ )
+               {
+                       nPMemMapEnts = PMemMap_MarkRangeUsed(
+                               pmemmap, nPMemMapEnts, MAX_PMEMMAP_ENTS,
+                               mods->Start, mods->End - mods->Start
+                               );
+               }
+               
+               // Debug - Output map
+               PMemMap_DumpBlocks(pmemmap, nPMemMapEnts);
+
                // Adjust Multiboot structure address
                mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
-               gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE);
                
-               MM_Install( mbInfo );   // Set up physical memory manager
-               break;
+               break; }
        
        // Multiboot 2
        case MULTIBOOT2_MAGIC:
@@ -76,6 +133,9 @@ int kmain(Uint MbMagic, void *MbInfoPtr)
                return 0;
        }
        
+       // Set up physical memory manager
+       MM_Install(nPMemMapEnts, pmemmap);
+       
        MM_InstallVirtual();    // Clean up virtual address space
        Heap_Install();         // Create initial heap
        
@@ -93,7 +153,7 @@ int kmain(Uint MbMagic, void *MbInfoPtr)
        mods = (void*)( mbInfo->Modules + KERNEL_BASE );
        giArch_NumBootModules = mbInfo->ModuleCount;
        gaArch_BootModules = malloc( giArch_NumBootModules * sizeof(*gaArch_BootModules) );
-       for( i = 0; i < mbInfo->ModuleCount; i ++ )
+       for( int i = 0; i < mbInfo->ModuleCount; i ++ )
        {
                 int    ofs;
        
@@ -119,7 +179,6 @@ int kmain(Uint MbMagic, void *MbInfoPtr)
                }
                else
                        gaArch_BootModules[i].ArgString = (char *)mods[i].String + KERNEL_BASE;
-               Log_Log("Arch", " - %s", gaArch_BootModules[i].ArgString);
        }
        
        // Pass on to Independent Loader
index bd1cafc..a331817 100644 (file)
@@ -4,8 +4,9 @@
  */
 #define DEBUG  0
 #include <acess.h>
-#include <mboot.h>
 #include <mm_virt.h>
+#include <pmemmap.h>
+#include <hal_proc.h>
 
 //#define USE_STACK    1
 #define TRACE_ALLOCS   0       // Print trace messages on AllocPhys/DerefPhys
@@ -14,11 +15,10 @@ static const int addrClasses[] = {0,16,20,24,32,64};
 static const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
 
 // === IMPORTS ===
-extern char    gKernelEnd[];
 extern void    Proc_PrintBacktrace(void);
 
 // === PROTOTYPES ===
-void   MM_Install(tMBoot_Info *MBoot);
+void   MM_Install(int NPMemRanges, tPMemMapEnt *PMemRanges);
 //tPAddr       MM_AllocPhys(void);
 //tPAddr       MM_AllocPhysRange(int Pages, int MaxBits);
 //void MM_RefPhys(tPAddr PAddr);
@@ -39,82 +39,66 @@ void        **gaPageNodes = (void*)MM_PAGENODE_BASE;
 #define REFENT_PER_PAGE        (0x1000/sizeof(gaPageReferences[0]))
 
 // === CODE ===
-void MM_Install(tMBoot_Info *MBoot)
+void MM_Install(int NPMemRanges, tPMemMapEnt *PMemRanges)
 {
-       Uint    kernelPages, num;
        Uint    i;
        Uint64  maxAddr = 0;
-       tMBoot_Module   *mods;
-       tMBoot_MMapEnt  *ent;
        
        // --- Find largest address
-       MBoot->MMapAddr |= KERNEL_BASE;
-       ent = (void *)( MBoot->MMapAddr );
-       while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
+       for( i = 0; i < NPMemRanges; i ++ )
        {
-               // Adjust for size
-               ent->Size += 4;
-               
+               tPMemMapEnt     *ent = &PMemRanges[i];
                // If entry is RAM and is above `maxAddr`, change `maxAddr`
-               if(ent->Type == 1)
+               if(ent->Type == PMEMTYPE_FREE || ent->Type == PMEMTYPE_USED)
                {
-                       if(ent->Base + ent->Length > maxAddr)
-                               maxAddr = ent->Base + ent->Length;
+                       if(ent->Start + ent->Length > maxAddr)
+                               maxAddr = ent->Start + ent->Length;
                        giTotalMemorySize += ent->Length >> 12;
                }
-               // Go to next entry
-               ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
        }
        
-       if(maxAddr == 0) {      
-               giPageCount = (MBoot->HighMem >> 2) + 256;      // HighMem is a kByte value
-       }
-       else {
-               giPageCount = maxAddr >> 12;
-       }
+       giPageCount = maxAddr >> 12;
        giLastPossibleFree = giPageCount - 1;
        
        memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32);
        
        // Set up allocateable space
-       ent = (void *)( MBoot->MMapAddr );
-       while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
-       {               
-               memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) );
-               ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
+       for( i = 0; i < NPMemRanges; i ++ )
+       {
+               tPMemMapEnt *ent = &PMemRanges[i];
+               if( ent->Type == PMEMTYPE_FREE )
+               {
+                       Uint64  startpg = ent->Start / PAGE_SIZE;
+                       Uint64  pgcount = ent->Length / PAGE_SIZE;
+                       while( startpg % 32 && pgcount ) {
+                               gaPageBitmap[startpg/32] &= ~(1U << (startpg%32));
+                               startpg ++;
+                               pgcount --;
+                       }
+                       memsetd( &gaPageBitmap[startpg/32], 0, pgcount/32 );
+                       startpg += pgcount - pgcount%32;
+                       pgcount -= pgcount - pgcount%32;
+                       while(pgcount) {
+                               gaPageBitmap[startpg/32] &= ~(1U << (startpg%32));
+                               startpg ++;
+                               pgcount --;
+                       }
+               }
+               else if( ent->Type == PMEMTYPE_USED )
+               {
+                       giPhysAlloc += ent->Length / PAGE_SIZE;
+               }
        }
-       
-       // Get used page count (Kernel)
-       kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000;
-       kernelPages += 0xFFF;   // Page Align
-       kernelPages >>= 12;
-       giPhysAlloc += kernelPages;     // Add to used count    
 
-       // Fill page bitmap
-       num = kernelPages/32;
-       memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num );
-       gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1;
-       
        // Fill Superpage bitmap
-       num = kernelPages/(32*32);
-       memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num );
-       gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1;
-       
-       // Mark Multiboot's pages as taken
-       // - Structure
-       MM_RefPhys( (Uint)MBoot - KERNEL_BASE );
-       // - Module List
-       for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; )
-               MM_RefPhys( MBoot->Modules + (i << 12) );
-       // - Modules
-       mods = (void*)(MBoot->Modules + KERNEL_BASE);
-       for(i = 0; i < MBoot->ModuleCount; i++)
+       // - A set bit means that there are no free pages in this block of 32
+       for( i = 0; i < (giPageCount+31)/32; i ++ )
        {
-               num = (mods[i].End - mods[i].Start + 0xFFF) >> 12;
-               while(num--)
-                       MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) );
+               if( gaPageBitmap[i] + 1 == 0 ) {
+                       gaSuperBitmap[i/32] |= (1 << i%32);
+               }
        }
-
+       
        gaPageReferences = (void*)MM_REFCOUNT_BASE;
 
        Log_Log("PMem", "Physical memory set up (%lli pages of ~%lli MiB used)",
index b6026de..1d4a35d 100644 (file)
@@ -57,6 +57,7 @@ mboot:
        
 [section .text]
 [extern kmain]
+[extern Desctab_Install]
 [global start]
 start:
        ; Just show we're here
@@ -87,11 +88,15 @@ start:
 .higher_half:
        
        mov WORD [0xB8006], 0x0773      ; 's'
+       
+       push ebx        ; Multiboot Info
+       push eax        ; Multiboot Magic Value
+       ; NOTE: These are actually for kmain
+       
+       call Desctab_Install
        mov WORD [0xB8008], 0x0773      ; 's'
 
        ; Call the kernel
-       push ebx        ; Multiboot Info
-       push eax        ; Multiboot Magic Value
        mov WORD [0xB800A], 0x0732      ; '2'
        call kmain
 
diff --git a/KernelLand/Kernel/include/pmemmap.h b/KernelLand/Kernel/include/pmemmap.h
new file mode 100644 (file)
index 0000000..4b89e45
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * pmemmap.h
+ * - Physical Memory Map definitions
+ */
+#ifndef _PMEMMAP_H_
+#define _PMEMMAP_H_
+
+typedef struct sPMemMapEnt     tPMemMapEnt;
+
+enum ePMemMapEntType
+{
+       PMEMTYPE_FREE,  // Free RAM
+       PMEMTYPE_USED,  // Used by Kernel / Modules
+       PMEMTYPE_RESERVED,      // Unavaliable
+       PMEMTYPE_NVRAM, // Non-volatile
+       PMEMTYPE_UNMAPPED       // Nothing on these lines
+};
+
+struct sPMemMapEnt
+{
+       Uint64  Start;
+       Uint64  Length;
+       enum ePMemMapEntType    Type;
+       Uint16  NUMADomain;
+};
+
+extern void    PMemMap_DumpBlocks(tPMemMapEnt *map, int NEnts);
+extern int     PMemMap_SplitBlock(tPMemMapEnt *map, int NEnts, int MaxEnts, int Block, Uint64 Offset);
+extern int     PMemMap_CompactMap(tPMemMapEnt *map, int NEnts, int MaxEnts);
+extern int     PMemMap_ValidateMap(tPMemMapEnt *map, int NEnts, int MaxEnts);
+extern int     PMemMap_MarkRangeUsed(tPMemMapEnt *map, int NEnts, int MaxEnts, Uint64 Base, Uint64 Size);
+
+#endif
+
diff --git a/KernelLand/Kernel/pmemmap.c b/KernelLand/Kernel/pmemmap.c
new file mode 100644 (file)
index 0000000..50f8de1
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * pmemmap.c
+ * - Physical memory map manipulation
+ */
+#include <acess.h>
+#include <pmemmap.h>
+
+// === CODE ===
+void PMemMap_DumpBlocks(tPMemMapEnt *map, int NEnts)
+{
+       for( int i = 0; i < NEnts; i ++ )
+       {
+               Log_Debug("Arch", "%i: %i 0x%02x %08llx+%llx",
+                       i, map[i].Type, map[i].NUMADomain,
+                       map[i].Start, map[i].Length
+                       );
+       }
+}
+
+int PMemMap_SplitBlock(tPMemMapEnt *map, int NEnts, int MaxEnts, int Block, Uint64 Offset)
+{
+       LOG("Splitting %i (%llx+%llx) at %llx", Block, map[Block].Start, map[Block].Length, Offset);
+       
+       Uint64 _len = map[Block].Length;
+       map[Block].Length = Offset;
+       if( NEnts == MaxEnts ) {
+               // out of space
+               return NEnts;
+       }
+       Block ++;
+       if( Block < NEnts ) {
+               // Can't be anything after
+               memmove(&map[Block+1], &map[Block], (NEnts - Block)*sizeof(map[0]));
+       }
+       NEnts ++;
+       
+       // New (free) block
+       map[Block].Start  = map[Block-1].Start + Offset;
+       map[Block].Length = _len - Offset;
+       map[Block].Type = map[Block-1].Type;
+       map[Block].NUMADomain = map[Block-1].NUMADomain;
+       LOG("- New %i %02x %llx+%llx", map[Block].Type, map[Block].NUMADomain, map[Block].Start, map[Block].Length);
+
+       return NEnts;
+}
+
+int PMemMap_CompactMap(tPMemMapEnt *map, int NEnts, int MaxEnts)
+{
+       for( int i = 1; i < NEnts; i ++ )
+       {
+               // Check if the ranges are contiguous
+               if( map[i-1].Start + map[i-1].Length < map[i].Start )
+                       continue ;
+               // Check if the type is the same
+               if( map[i-1].Type != map[i].Type )
+                       continue ;
+               // Check if the NUMA Domains are the same
+               if( map[i-1].NUMADomain != map[i].NUMADomain )
+                       continue ;
+               
+               // Ok, they should be together
+               map[i-1].Length += map[i].Length;
+               memmove(&map[i], &map[i+1], (NEnts - (i+1))*sizeof(map[0]));
+               
+               // Counteract the i++ in the loop iterator
+               i --;
+               NEnts --;
+       }
+       return NEnts;
+}
+
+int PMemMap_ValidateMap(tPMemMapEnt *map, int NEnts, int MaxEnts)
+{
+       // Sort the pmem map
+        int     bNeedsSort = 0;
+       for( int i = 1; i < NEnts; i ++ )
+       {
+               if( map[i-1].Start > map[i].Start ) {
+                       bNeedsSort = 1;
+                       break;
+               }
+       }
+       if( bNeedsSort )
+       {
+               Log_Warning("Arch", "TODO: Impliment memory map sorting");
+               // TODO: Sort memory map
+       }
+       
+       // Ensure that the map has no overlaps
+       for( int i = 1; i < NEnts; i ++ )
+       {
+               if( map[i-1].Start + map[i-1].Length <= map[i].Start )
+                       continue ;
+               // Oops, overlap!
+               Log_Notice("Arch", "Map ranges %llx+%llx and %llx+%llx overlap",
+                       map[i-1].Start, map[i-1].Length,
+                       map[i].Start,   map[i].Length
+                       );
+       }
+       
+       return PMemMap_CompactMap(map, NEnts, MaxEnts);
+}
+
+
+int PMemMap_MarkRangeUsed(tPMemMapEnt *map, int NEnts, int MaxEnts, Uint64 Base, Uint64 Size)
+{
+        int    first;
+       
+       Size = (Size + 0xFFF) & ~0xFFF;
+       Base = Base & ~0xFFF;
+       
+       first = -1;
+       for( int i = 0; i < NEnts; i ++ )
+       {
+               if( map[i].Start + map[i].Length > Base ) {
+                       first = i;
+                       break;
+               }
+       }
+       if( first == -1 ) {
+               // Not in map
+               LOG("%llx+%llx not in map (past end)", Base, Size);
+               return NEnts;
+       }
+       
+       if( map[first].Start > Base ) {
+               // Not in map
+               LOG("%llx+%llx not in map (in hole)", Base, Size);
+               return NEnts;
+       }
+       
+       // Detect single
+       if( map[first].Start <= Base && Base + Size <= map[first].Start + map[first].Length )
+       {
+               // Split before
+               if( map[first].Start < Base )
+               {
+                       if( NEnts == MaxEnts ) {
+                               // out of space... oops
+                               return NEnts;
+                       }
+                       NEnts = PMemMap_SplitBlock(map, NEnts, MaxEnts, first, Base - map[first].Start);
+                       first ++;
+               }
+               
+               // map[first].Start == Base
+               // Split after
+               if( map[first].Length > Size )
+               {
+                       if( NEnts == MaxEnts ) {
+                               // out of space
+                               return NEnts;
+                       }
+                       NEnts = PMemMap_SplitBlock(map, NEnts, MaxEnts, first, Size);
+               }
+               
+               // map[first] is now exactly the block
+               map[first].Type = PMEMTYPE_USED;
+       
+               return PMemMap_CompactMap(map, NEnts, MaxEnts);
+       }
+       else
+       {
+               // Wait... this should never happen, right?
+               Log_Notice("Arch", "Module %llx+%llx overlaps two or more ranges",
+                       Base, Size);
+               PMemMap_DumpBlocks(map, NEnts);
+               // TODO: Error?
+               return NEnts;
+       }
+}
+
+
+

UCC git Repository :: git.ucc.asn.au