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
35 // Only used in init, allows the init code to provide pages for use by
36 // the allocator before the bitmaps exist.
37 // 3 entries because the are three calls to MM_AllocPhys in MM_Map
38 #define NUM_STATIC_ALLOC 3
39 tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0};
43 * \brief Initialise the physical memory map using a Multiboot 1 map
45 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
47 tMBoot_MMapEnt *mmapStart;
50 int numPages, superPages;
54 tPAddr paddr, firstFreePage;
56 Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
58 // Scan the physical memory map
59 // Looking for the top of physical memory
60 mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
61 Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
63 while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
65 // Adjust for the size of the entry
67 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
68 ent->Type, ent->Base, ent->Length);
70 // If entry is RAM and is above `maxAddr`, change `maxAddr`
71 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
72 maxAddr = ent->Base + ent->Length;
75 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
78 // Did we find a valid end?
80 // No, darn, let's just use the HighMem hack
81 giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
84 // Goodie, goodie gumdrops
85 giMaxPhysPage = maxAddr >> 12;
87 Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
89 // Find a contigous section of memory to hold it in
90 // - Starting from the end of the kernel
91 // - We also need a region for the super bitmap
92 superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
93 numPages = (giMaxPhysPage + 7) / 8;
94 numPages = (numPages + 0xFFF) >> 12;
95 Log(" MM_InitPhys_Multiboot: numPages = %i, superPages = %i",
96 numPages, superPages);
99 int todo = numPages*2 + superPages;
100 // Ok, naieve allocation, just put it after the kernel
101 // - Allocated Bitmap
102 vaddr = MM_PAGE_BITMAP;
103 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
107 for( i = 0; i < NUM_STATIC_ALLOC; i++) {
108 if(gaiStaticAllocPages[i] != 0) continue;
109 gaiStaticAllocPages[i] = paddr;
113 MM_Map(vaddr, paddr);
119 if( todo == numPages + superPages )
120 vaddr = MM_PAGE_DBLBMP;
121 if( todo == superPages )
122 vaddr = MM_PAGE_SUPBMP;
125 // Scan for a nice range
128 int todo = numPages*2 + superPages;
130 vaddr = MM_PAGE_BITMAP;
134 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
135 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
144 // Let's not put it below the kernel, shall we?
145 if( ent->Base + ent->Size < (tPAddr)&gKernelBase )
148 // Check if the kernel is in this range
149 if( ent->Base <= (tPAddr)&gKernelBase
150 && ent->Base + ent->Size > (tPAddr)&gKernelEnd - KERNEL_BASE )
152 avail = ent->Length >> 12;
153 avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12;
154 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
156 // No? then we can use all of the block
159 avail = ent->Length >> 12;
163 Log(" MM_InitPhys_Multiboot: paddr=0x%x, avail=%i", paddr, avail);
166 while( todo && avail --)
168 // Static Allocations
169 for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) {
170 if(gaiStaticAllocPages[i] != 0) continue;
171 gaiStaticAllocPages[i] = paddr;
178 MM_Map(vaddr, paddr);
183 // Alter the destination address when needed
184 if(todo == superPages+numPages)
185 vaddr = MM_PAGE_DBLBMP;
186 if(todo == superPages)
187 vaddr = MM_PAGE_SUPBMP;
190 // Fast quit if there's nothing left to allocate
194 // Save the current value of paddr to simplify the allocation later
195 firstFreePage = paddr;
197 Log(" MM_InitPhys_Multiboot: Clearing multi bitmap");
199 memset(gaMultiBitmap, 0, numPages<<12);
200 // - initialise to one, then clear the avaliable areas
201 memset(gaMainBitmap, -1, numPages<<12);
202 Log(" MM_InitPhys_Multiboot: Setting main bitmap");
203 // - Clear all Type=1 areas
204 Log(" MM_InitPhys_Multiboot: Clearing valid regions");
207 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
208 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
211 // Check if the type is RAM
212 if(ent->Type != 1) continue;
215 base = ent->Base >> 12;
216 size = ent->Size >> 12;
219 Uint64 val = -1 << (base & 63);
220 gaMainBitmap[base / 64] &= ~val;
222 base += 64 - (base & 63);
224 memset( &gaMainBitmap[base / 64], 0, size/8 );
226 Uint64 val = -1 << (size & 7);
228 gaMainBitmap[base / 64] &= ~val;
232 base = ent->Base >> 12;
233 size = ent->Size >> 12;
234 size = (size + (base & 63) + 63) >> 6;
237 Uint64 val = -1 << (base & 63);
238 gaSuperBitmap[base / 64] &= ~val;
240 base += 64 - (base & 63);
244 // Reference the used pages
245 base = (tPAddr)&gKernelBase >> 12;
246 size = firstFreePage >> 12;
247 memset( &gaMainBitmap[base / 64], -1, size/8 );
249 Uint64 val = -1 << (size & 7);
251 gaMainBitmap[base / 64] |= val;
253 // Free the unused static allocs
254 for( i = 0; i < NUM_STATIC_ALLOC; i++) {
255 if(gaiStaticAllocPages[i] != 0)
257 gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ]
258 &= ~(1 << ((gaiStaticAllocPages[i]>>12)&63));
261 // Fill the super bitmap
262 Log(" MM_InitPhys_Multiboot: Filling super bitmap");
263 memset(gaSuperBitmap, 0, superPages<<12);
264 for( base = 0; base < giMaxPhysPage/64; base ++)
266 if( gaMainBitmap[ base ] == -1 )
267 gaSuperBitmap[ base/64 ] |= 1 << (base&63);
272 * \brief Allocate a contiguous range of physical pages with a maximum
273 * bit size of \a Bits
274 * \param Num Number of pages to allocate
275 * \param Bits Maximum size of the physical address
276 * \note If \a Bits is <= 0, any sized address is used (with preference
277 * to higher addresses)
279 tPAddr MM_AllocPhysRange(int Num, int Bits)
285 Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
287 if( Bits <= 0 ) // Speedup for the common case
288 rangeID = MM_PHYS_MAX;
290 rangeID = MM_PHYS_MAX;
292 rangeID = MM_PHYS_32BIT;
294 rangeID = MM_PHYS_24BIT;
296 rangeID = MM_PHYS_20BIT;
298 rangeID = MM_PHYS_16BIT;
300 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
302 LOCK(&glPhysicalPages);
303 Log(" MM_AllocPhysRange: i has lock");
305 // Check if the range actually has any free pages
306 while(giPhysRangeFree[rangeID] == 0 && rangeID)
309 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
311 // What the? Oh, man. No free pages
312 if(giPhysRangeFree[rangeID] == 0) {
313 RELEASE(&glPhysicalPages);
316 Warning(" MM_AllocPhysRange: Out of free pages");
318 "Out of memory (unable to fulfil request for %i pages), zero remaining",
324 // Check if there is enough in the range
325 if(giPhysRangeFree[rangeID] >= Num)
327 // Do a cheap scan, scanning upwards from the first free page in
330 addr = giPhysRangeFirst[ rangeID ];
331 while( addr < giPhysRangeLast[ rangeID ] )
333 // Check the super bitmap
334 if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
337 addr &= (1 << (6+6)) - 1;
340 // Check page block (64 pages)
341 if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
344 addr &= (1 << (12+6)) - 1;
347 // Check individual page
348 if( gaMainBitmap[addr >> 6] & (1 << (addr & 63)) ) {
358 // If we don't find a contiguous block, nFree will not be equal
359 // to Num, so we set it to zero and do the expensive lookup.
360 if(nFree != Num) nFree = 0;
365 // Oops. ok, let's do an expensive check (scan down the list
366 // until a free range is found)
368 addr = giPhysRangeLast[ rangeID ];
370 RELEASE(&glPhysicalPages);
374 "Out of memory (unable to fulfil request for %i pages)",
379 Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
381 // Mark pages as allocated
383 for( i = 0; i < Num; i++ )
385 gaiPageReferences[addr >> 6] |= 1 << (addr & 63);
387 if(addr >> 32) rangeID = MM_PHYS_MAX;
388 else if(addr >> 24) rangeID = MM_PHYS_32BIT;
389 else if(addr >> 20) rangeID = MM_PHYS_24BIT;
390 else if(addr >> 16) rangeID = MM_PHYS_20BIT;
391 else if(addr >> 0) rangeID = MM_PHYS_16BIT;
392 giPhysRangeFree[ rangeID ] --;
395 Num += addr & (64-1);
397 Num = (Num + (64-1)) & ~(64-1);
398 for( i = 0; i < Num/64; i++ )
400 if( gaMainBitmap[ addr >> 6 ] == -1 )
401 gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
404 RELEASE(&glPhysicalPages);
409 * \brief Allocate a single physical page, with no preference as to address
412 tPAddr MM_AllocPhys(void)
416 // Hack to allow allocation during setup
417 for(i = 0; i < NUM_STATIC_ALLOC; i++) {
418 if( gaiStaticAllocPages[i] ) {
419 tPAddr ret = gaiStaticAllocPages[i];
420 gaiStaticAllocPages[i] = 0;
425 return MM_AllocPhysRange(1, -1);
429 * \brief Reference a physical page
431 void MM_RefPhys(tPAddr PAddr)
433 Uint64 page = PAddr >> 12;
435 if( PAddr >> 12 > giMaxPhysPage ) return ;
437 if( gaMainBitmap[ page >> 6 ] & (1 << (page&63)) )
440 gaMultiBitmap[ page >> 6 ] |= 1 << (page&63);
441 gaiPageReferences[ page ] ++;
446 gaMainBitmap[page >> 6] |= 1 << (page&63);
447 if( gaMainBitmap[page >> 6 ] == -1 )
448 gaSuperBitmap[page>> 12] |= 1 << ((page >> 6) & 63);
453 * \brief Dereference a physical page
455 void MM_DerefPhys(tPAddr PAddr)
457 Uint64 page = PAddr >> 12;
459 if( PAddr >> 12 > giMaxPhysPage ) return ;
461 if( gaMultiBitmap[ page >> 6 ] & (1 << (page&63)) ) {
462 gaiPageReferences[ page ] --;
463 if( gaiPageReferences[ page ] == 1 )
464 gaMultiBitmap[ page >> 6 ] &= ~(1 << (page&63));
465 if( gaiPageReferences[ page ] == 0 )
466 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
469 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
471 if(gaMainBitmap[ page >> 6 ] == 0) {
472 gaSuperBitmap[page >> 12] &= ~(1 << ((page >> 6) & 63));