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 gKernelBase;
22 extern void gKernelEnd;
25 tSpinlock glPhysicalPages;
26 Uint64 *gaSuperBitmap; // 1 bit = 64 Pages, 16 MiB Per Word
27 Uint64 *gaMainBitmap; // 1 bit = 1 Page, 256 KiB per Word
28 Uint64 *gaMultiBitmap; // Each bit means that the page is being used multiple times
29 Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts
30 tPAddr giFirstFreePage; // First possibly free page
31 Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range
32 Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range
33 Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range
34 Uint64 giMaxPhysPage = 0; // Maximum Physical page
38 * \brief Initialise the physical memory map using a Multiboot 1 map
40 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
42 tMBoot_MMapEnt *mmapStart;
45 int numPages, superPages;
51 Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
53 // Scan the physical memory map
54 // Looking for the top of physical memory
55 mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
56 Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
58 while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
60 // Adjust for the size of the entry
62 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
63 ent->Type, ent->Base, ent->Length);
65 // If entry is RAM and is above `maxAddr`, change `maxAddr`
66 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
67 maxAddr = ent->Base + ent->Length;
70 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
73 // Did we find a valid end?
75 // No, darn, let's just use the HighMem hack
76 giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
79 // Goodie, goodie gumdrops
80 giMaxPhysPage = maxAddr >> 12;
82 Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
84 // Find a contigous section of memory to hold it in
85 // - Starting from the end of the kernel
86 // - We also need a region for the super bitmap
87 superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
88 numPages = (giMaxPhysPage + 7) / 8;
89 numPages = (numPages + 0xFFF) >> 12;
90 Log(" MM_InitPhys_Multiboot: numPages = %i, superPages = %i",
91 numPages, superPages);
95 // Ok, naieve allocation, just put it after the kernel
97 vaddr = MM_PAGE_BITMAP;
98 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
101 MM_Map(vaddr, paddr);
105 // - Multi-Alloc Bitmap
106 vaddr = MM_PAGE_DBLBMP;
109 MM_Map(vaddr, paddr);
114 vaddr = MM_PAGE_SUPBMP;
117 MM_Map(vaddr, paddr);
122 // Scan for a nice range
125 int todo = numPages*2 + superPages;
127 tVAddr vaddr = MM_PAGE_BITMAP;
131 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
132 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
142 // Let's not put it below the kernel, shall we?
143 if( ent->Base + ent->Size < (tPAddr)&gKernelBase )
146 // Check if the kernel is in this range
147 if( ent->Base <= (tPAddr)&gKernelBase
148 && ent->Base + ent->Size > (tPAddr)&gKernelEnd - KERNEL_BASE )
150 avail = ent->Length >> 12;
151 avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12;
152 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
154 // No? then we can use all of the block
157 avail = ent->Length >> 12;
161 Log(" MM_InitPhys_Multiboot: paddr=0x%x, avail=%i", paddr, avail);
164 max = todo < avail ? todo : avail;
165 for( i = 0; i < max; i ++ )
167 MM_Map(vaddr, paddr);
171 // Alter the destination address when needed
172 if(todo == superPages+numPages)
173 vaddr = MM_PAGE_DBLBMP;
174 if(todo == superPages)
175 vaddr = MM_PAGE_SUPBMP;
178 // Fast quit if there's nothing left to allocate
183 Log(" MM_InitPhys_Multiboot: Cearing multi bitmap");
185 memset(gaMultiBitmap, 0, numPages<<12);
186 // - initialise to one, then clear the avaliable areas
187 memset(gaMainBitmap, -1, numPages<<12);
188 Log(" MM_InitPhys_Multiboot: Setting main bitmap");
189 // - Clear all Type=1 areas
190 Log(" MM_InitPhys_Multiboot: Clearing valid regions");
193 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
194 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
197 // Check if the type is RAM
198 if(ent->Type != 1) continue;
201 base = ent->Base >> 12;
202 size = ent->Size >> 12;
205 Uint64 val = -1 << (base & 63);
206 gaMainBitmap[base / 64] &= ~val;
208 base += 64 - (base & 63);
210 memset( &gaMainBitmap[base / 64], 0, size/8 );
212 Uint64 val = -1 << (size & 7);
214 gaMainBitmap[base / 64] &= ~val;
218 base = ent->Base >> 12;
219 size = ent->Size >> 12;
220 size = (size + (base & 63) + 63) >> 6;
223 Uint64 val = -1 << (base & 63);
224 gaSuperBitmap[base / 64] &= ~val;
226 base += 64 - (base & 63);
230 // Reference the used pages
232 Log(" MM_InitPhys_Multiboot: Setting kernel area");
233 base = (tPAddr)&gKernelBase >> 12;
234 size = ((tPAddr)&gKernelEnd - KERNEL_BASE - base) >> 12;
235 memset( &gaMainBitmap[base / 64], -1, size/8 );
237 Uint64 val = -1 << (size & 7);
239 gaMainBitmap[base / 64] |= val;
242 Log(" MM_InitPhys_Multiboot: Setting bitmaps' memory");
243 vaddr = MM_PAGE_BITMAP;
244 for( i = 0; i < numPages; i++, vaddr ++ )
246 paddr = MM_GetPhysAddr(vaddr) >> 12;
247 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
249 vaddr = MM_PAGE_DBLBMP;
250 for( i = 0; i < numPages; i++, vaddr += 0x1000 )
252 paddr = MM_GetPhysAddr(vaddr) >> 12;
253 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
255 vaddr = MM_PAGE_SUPBMP;
256 for( i = 0; i < superPages; i++, vaddr += 0x1000 )
258 paddr = MM_GetPhysAddr(vaddr) >> 12;
259 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
262 // Fill the super bitmap
263 Log(" MM_InitPhys_Multiboot: Filling super bitmap");
264 memset(gaSuperBitmap, 0, superPages<<12);
265 for( base = 0; base < giMaxPhysPage/64; base ++)
267 if( gaMainBitmap[ base ] == -1 )
268 gaSuperBitmap[ base/64 ] |= 1 << (base&63);
273 * \brief Allocate a contiguous range of physical pages with a maximum
274 * bit size of \a Bits
275 * \param Num Number of pages to allocate
276 * \param Bits Maximum size of the physical address
277 * \note If \a Bits is <= 0, any sized address is used (with preference
278 * to higher addresses)
280 tPAddr MM_AllocPhysRange(int Num, int Bits)
286 Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
288 if( Bits <= 0 ) // Speedup for the common case
289 rangeID = MM_PHYS_MAX;
291 rangeID = MM_PHYS_MAX;
293 rangeID = MM_PHYS_32BIT;
295 rangeID = MM_PHYS_24BIT;
297 rangeID = MM_PHYS_20BIT;
299 rangeID = MM_PHYS_16BIT;
301 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
303 LOCK(&glPhysicalPages);
304 Log(" MM_AllocPhysRange: i has lock");
306 // Check if the range actually has any free pages
307 while(giPhysRangeFree[rangeID] == 0 && rangeID)
310 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
312 // What the? Oh, man. No free pages
313 if(giPhysRangeFree[rangeID] == 0) {
314 RELEASE(&glPhysicalPages);
317 Warning(" MM_AllocPhysRange: Out of free pages");
319 "Out of memory (unable to fulfil request for %i pages), zero remaining",
325 // Check if there is enough in the range
326 if(giPhysRangeFree[rangeID] >= Num)
328 // Do a cheap scan, scanning upwards from the first free page in
331 addr = giPhysRangeFirst[ rangeID ];
332 while( addr < giPhysRangeLast[ rangeID ] )
334 // Check the super bitmap
335 if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
338 addr &= (1 << (6+6)) - 1;
341 // Check page block (64 pages)
342 if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
345 addr &= (1 << (12+6)) - 1;
348 // Check individual page
349 if( gaMainBitmap[addr >> 6] & (1 << (addr & 63)) ) {
359 // If we don't find a contiguous block, nFree will not be equal
360 // to Num, so we set it to zero and do the expensive lookup.
361 if(nFree != Num) nFree = 0;
366 // Oops. ok, let's do an expensive check (scan down the list
367 // until a free range is found)
369 addr = giPhysRangeLast[ rangeID ];
371 RELEASE(&glPhysicalPages);
375 "Out of memory (unable to fulfil request for %i pages)",
380 Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
382 // Mark pages as allocated
384 for( i = 0; i < Num; i++ )
386 gaiPageReferences[addr >> 6] |= 1 << (addr & 63);
388 if(addr >> 32) rangeID = MM_PHYS_MAX;
389 else if(addr >> 24) rangeID = MM_PHYS_32BIT;
390 else if(addr >> 20) rangeID = MM_PHYS_24BIT;
391 else if(addr >> 16) rangeID = MM_PHYS_20BIT;
392 else if(addr >> 0) rangeID = MM_PHYS_16BIT;
393 giPhysRangeFree[ rangeID ] --;
396 Num += addr & (64-1);
398 Num = (Num + (64-1)) & ~(64-1);
399 for( i = 0; i < Num/64; i++ )
401 if( gaMainBitmap[ addr >> 6 ] == -1 )
402 gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
405 RELEASE(&glPhysicalPages);
410 * \brief Allocate a single physical page, with no preference as to address
413 tPAddr MM_AllocPhys(void)
415 return MM_AllocPhysRange(1, -1);
419 * \brief Reference a physical page
421 void MM_RefPhys(tPAddr PAddr)
423 Uint64 page = PAddr >> 12;
425 if( PAddr >> 12 > giMaxPhysPage ) return ;
427 if( gaMainBitmap[ page >> 6 ] & (1 << (page&63)) )
430 gaMultiBitmap[ page >> 6 ] |= 1 << (page&63);
431 gaiPageReferences[ page ] ++;
436 gaMainBitmap[page >> 6] |= 1 << (page&63);
437 if( gaMainBitmap[page >> 6 ] == -1 )
438 gaSuperBitmap[page>> 12] |= 1 << ((page >> 6) & 63);
443 * \brief Dereference a physical page
445 void MM_DerefPhys(tPAddr PAddr)
447 Uint64 page = PAddr >> 12;
449 if( PAddr >> 12 > giMaxPhysPage ) return ;
451 if( gaMultiBitmap[ page >> 6 ] & (1 << (page&63)) ) {
452 gaiPageReferences[ page ] --;
453 if( gaiPageReferences[ page ] == 1 )
454 gaMultiBitmap[ page >> 6 ] &= ~(1 << (page&63));
455 if( gaiPageReferences[ page ] == 0 )
456 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
459 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
461 if(gaMainBitmap[ page >> 6 ] == 0) {
462 gaSuperBitmap[page >> 12] &= ~(1 << ((page >> 6) & 63));