4 * Physical Memory Manager
12 MM_PHYS_16BIT, // Does anything need this?
13 MM_PHYS_20BIT, // Real-Mode
14 MM_PHYS_24BIT, // ISA DMA
15 MM_PHYS_32BIT, // x86 Hardware
16 MM_PHYS_MAX, // Doesn't care
21 extern void gKernelEnd;
24 tSpinlock glPhysicalPages;
25 Uint64 *gaSuperBitmap; // 1 bit = 64 Pages
26 Uint64 *gaMainBitmap; // 1 bit = 1 Page
27 Uint64 *gaMultiBitmap; // Each bit means that the page is being used multiple times
28 Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
29 tPAddr giFirstFreePage; // First possibly free page
30 Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range
31 Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range
32 Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range
33 Uint64 giMaxPhysPage = 0; // Maximum Physical page
36 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
38 tMBoot_MMapEnt *mmapStart;
44 Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
46 // Scan the physical memory map
47 // Looking for the top of physical memory
48 mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
49 Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
51 while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
53 // Adjust for the size of the entry
55 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
56 ent->Type, ent->Base, ent->Length);
58 // If entry is RAM and is above `maxAddr`, change `maxAddr`
59 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
60 maxAddr = ent->Base + ent->Length;
63 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
66 // Did we find a valid end?
68 // No, darn, let's just use the HighMem hack
69 giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
72 // Goodie, goodie gumdrops
73 giMaxPhysPage = maxAddr >> 12;
75 Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
77 // Find a contigous section of memory to hold it in
78 // - Starting from the end of the kernel
79 // - We also need a region for the super bitmap
80 superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
81 numPages = (giMaxPhysPage + 7) / 8;
82 numPages = (numPages + 0xFFF) >> 12;
83 Log(" MM_InitPhys_Multiboot: numPages = %i", numPages);
87 // Ok, naieve allocation, just put it after the kernel
89 tVAddr vaddr = MM_PAGE_BITMAP;
90 tPAddr paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
97 // - Multi-Alloc Bitmap
98 vaddr = MM_PAGE_DBLBMP;
101 MM_Map(vaddr, paddr);
106 vaddr = MM_PAGE_SUPBMP;
109 MM_Map(vaddr, paddr);
114 // Scan for a nice range
117 int todo = numPages*2 + superPages;
119 tVAddr vaddr = MM_PAGE_BITMAP;
123 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
124 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
134 // Let's not put it below the kernel, shall we?
135 if( ent->Base + ent->Size < (tPAddr)&gKernelEnd )
138 // Check if the kernel is in this range
139 if( ent->Base < (tPAddr)&gKernelEnd - KERNEL_BASE && ent->Base + ent->Size > (tPAddr)&gKernelEnd )
141 avail = (ent->Size-((tPAddr)&gKernelEnd-KERNEL_BASE-ent->Base)) >> 12;
142 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
144 // No? then we can use all of the block
147 avail = ent->Size >> 12;
152 max = todo < avail ? todo : avail;
153 for( i = 0; i < max; i ++ )
155 MM_Map(vaddr, paddr);
160 // Alter the destination address when needed
161 if(todo == superPages+numPages)
162 vaddr = MM_PAGE_DBLBMP;
163 if(todo == superPages)
164 vaddr = MM_PAGE_SUPBMP;
166 // Fast quit if there's nothing left to allocate
172 // - initialise to one, then clear the avaliable areas
173 memset(gaSuperBitmap, -1, superPages<<12);
174 memset(gaMainBitmap, -1, numPages<<12);
175 memset(gaMultiBitmap, 0, numPages<<12);
176 // - Clear all Type=1 areas
179 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
180 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
184 // Check if the type is RAM
185 if(ent->Type != 1) continue;
188 base = ent->Base >> 12;
189 size = ent->Size >> 12;
192 Uint64 val = -1 << (base & 63);
193 gaMainBitmap[base / 64] &= ~val;
195 base += 64 - (base & 63);
197 memset( &gaMainBitmap[base / 64], 0, size/8 );
199 Uint64 val = -1 << (size & 7);
201 gaMainBitmap[base / 64] &= ~val;
205 base = ent->Base >> 12;
206 size = ent->Size >> 12;
207 size = (size + (base & 63) + 63) >> 6;
210 Uint64 val = -1 << (base & 63);
211 gaSuperBitmap[base / 64] &= ~val;
213 base += 64 - (base & 63);
215 memset( &gaSuperBitmap[base / 64], 0, size/8 );
217 Uint64 val = -1 << (size & 7);
219 gaSuperBitmap[base / 64] &= ~val;
223 // Reference the used pages
225 // - Reference Counts and Bitmap
229 * \brief Allocate a contiguous range of physical pages with a maximum
230 * bit size of \a Bits
231 * \param Num Number of pages to allocate
232 * \param Bits Maximum size of the physical address
233 * \note If \a Bits is <= 0, any sized address is used (with preference
234 * to higher addresses)
236 tPAddr MM_AllocPhysRange(int Num, int Bits)
242 Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
244 if( Bits <= 0 ) // Speedup for the common case
245 rangeID = MM_PHYS_MAX;
247 rangeID = MM_PHYS_MAX;
249 rangeID = MM_PHYS_32BIT;
251 rangeID = MM_PHYS_24BIT;
253 rangeID = MM_PHYS_20BIT;
255 rangeID = MM_PHYS_16BIT;
257 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
259 LOCK(&glPhysicalPages);
260 Log(" MM_AllocPhysRange: i has lock");
262 // Check if the range actually has any free pages
263 while(giPhysRangeFree[rangeID] == 0 && rangeID)
266 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
268 // What the? Oh, man. No free pages
269 if(giPhysRangeFree[rangeID] == 0) {
270 RELEASE(&glPhysicalPages);
273 Warning(" MM_AllocPhysRange: Out of free pages");
275 "Out of memory (unable to fulfil request for %i pages), zero remaining",
281 // Check if there is enough in the range
282 if(giPhysRangeFree[rangeID] >= Num)
284 // Do a cheap scan, scanning upwards from the first free page in
287 addr = giPhysRangeFirst[ rangeID ];
288 while( addr < giPhysRangeLast[ rangeID ] )
290 // Check the super bitmap
291 if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
294 addr &= (1 << (6+6)) - 1;
297 // Check page block (64 pages)
298 if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
301 addr &= (1 << (12+6)) - 1;
304 // Check individual page
305 if( gaMainBitmap[addr >> 6] & (1 << (addr & 63)) ) {
315 // If we don't find a contiguous block, nFree will not be equal
316 // to Num, so we set it to zero and do the expensive lookup.
317 if(nFree != Num) nFree = 0;
322 // Oops. ok, let's do an expensive check (scan down the list
323 // until a free range is found)
325 addr = giPhysRangeLast[ rangeID ];
327 RELEASE(&glPhysicalPages);
331 "Out of memory (unable to fulfil request for %i pages)",
336 Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
338 // Mark pages as allocated
340 for( i = 0; i < Num; i++ )
342 gaiPageReferences[addr >> 6] |= 1 << (addr & 63);
344 if(addr >> 32) rangeID = MM_PHYS_MAX;
345 else if(addr >> 24) rangeID = MM_PHYS_32BIT;
346 else if(addr >> 20) rangeID = MM_PHYS_24BIT;
347 else if(addr >> 16) rangeID = MM_PHYS_20BIT;
348 else if(addr >> 0) rangeID = MM_PHYS_16BIT;
349 giPhysRangeFree[ rangeID ] --;
352 Num += addr & (64-1);
354 Num = (Num + (64-1)) & ~(64-1);
355 for( i = 0; i < Num/64; i++ )
357 if( gaMainBitmap[ addr >> 6 ] == -1 )
358 gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
361 RELEASE(&glPhysicalPages);
366 * \brief Allocate a single physical page, with no preference as to address
369 tPAddr MM_AllocPhys(void)
371 return MM_AllocPhysRange(1, -1);
375 * \brief Reference a physical page
377 void MM_RefPhys(tPAddr PAddr)
379 Uint64 page = PAddr >> 12;
381 if( PAddr >> 12 > giMaxPhysPage ) return ;
383 if( gaMainBitmap[ page >> 6 ] & (1 << (page&63)) )
386 gaMultiBitmap[ page >> 6 ] |= 1 << (page&63);
387 gaiPageReferences[ page ] ++;
392 gaMainBitmap[page >> 6] |= 1 << (page&63);
393 if( gaMainBitmap[page >> 6 ] == -1 )
394 gaSuperBitmap[page>> 12] |= 1 << ((page >> 6) & 63);
399 * \brief Dereference a physical page
401 void MM_DerefPhys(tPAddr PAddr)
403 Uint64 page = PAddr >> 12;
405 if( PAddr >> 12 > giMaxPhysPage ) return ;
407 if( gaMultiBitmap[ page >> 6 ] & (1 << (page&63)) ) {
408 gaiPageReferences[ page ] --;
409 if( gaiPageReferences[ page ] == 1 )
410 gaMultiBitmap[ page >> 6 ] &= ~(1 << (page&63));
411 if( gaiPageReferences[ page ] == 0 )
412 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
415 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
417 if(gaMainBitmap[ page >> 6 ] == 0) {
418 gaSuperBitmap[page >> 12] &= ~(1 << ((page >> 6) & 63));