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, 16 MiB Per Word
26 Uint64 *gaMainBitmap; // 1 bit = 1 Page, 256 KiB per Word
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
37 * \brief Initialise the physical memory map using a Multiboot 1 map
39 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
41 tMBoot_MMapEnt *mmapStart;
44 int numPages, superPages;
50 Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
52 // Scan the physical memory map
53 // Looking for the top of physical memory
54 mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
55 Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
57 while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
59 // Adjust for the size of the entry
61 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
62 ent->Type, ent->Base, ent->Length);
64 // If entry is RAM and is above `maxAddr`, change `maxAddr`
65 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
66 maxAddr = ent->Base + ent->Length;
69 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
72 // Did we find a valid end?
74 // No, darn, let's just use the HighMem hack
75 giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value
78 // Goodie, goodie gumdrops
79 giMaxPhysPage = maxAddr >> 12;
81 Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
83 // Find a contigous section of memory to hold it in
84 // - Starting from the end of the kernel
85 // - We also need a region for the super bitmap
86 superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12;
87 numPages = (giMaxPhysPage + 7) / 8;
88 numPages = (numPages + 0xFFF) >> 12;
89 Log(" MM_InitPhys_Multiboot: numPages = %i, superPages = ",
90 numPages, superPages);
94 // Ok, naieve allocation, just put it after the kernel
96 vaddr = MM_PAGE_BITMAP;
97 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
100 MM_Map(vaddr, paddr);
104 // - Multi-Alloc Bitmap
105 vaddr = MM_PAGE_DBLBMP;
108 MM_Map(vaddr, paddr);
113 vaddr = MM_PAGE_SUPBMP;
116 MM_Map(vaddr, paddr);
121 // Scan for a nice range
124 int todo = numPages*2 + superPages;
126 tVAddr vaddr = MM_PAGE_BITMAP;
130 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
131 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
141 // Let's not put it below the kernel, shall we?
142 if( ent->Base + ent->Size < (tPAddr)&gKernelEnd )
145 // Check if the kernel is in this range
146 if( ent->Base < (tPAddr)&gKernelEnd - KERNEL_BASE && ent->Base + ent->Size > (tPAddr)&gKernelEnd )
148 avail = (ent->Size-((tPAddr)&gKernelEnd-KERNEL_BASE-ent->Base)) >> 12;
149 paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
151 // No? then we can use all of the block
154 avail = ent->Size >> 12;
159 max = todo < avail ? todo : avail;
160 for( i = 0; i < max; i ++ )
162 MM_Map(vaddr, paddr);
167 // Alter the destination address when needed
168 if(todo == superPages+numPages)
169 vaddr = MM_PAGE_DBLBMP;
170 if(todo == superPages)
171 vaddr = MM_PAGE_SUPBMP;
173 // Fast quit if there's nothing left to allocate
179 // - initialise to one, then clear the avaliable areas
180 memset(gaMainBitmap, -1, numPages<<12);
181 memset(gaMultiBitmap, 0, numPages<<12);
182 // - Clear all Type=1 areas
185 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
186 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
189 // Check if the type is RAM
190 if(ent->Type != 1) continue;
193 base = ent->Base >> 12;
194 size = ent->Size >> 12;
197 Uint64 val = -1 << (base & 63);
198 gaMainBitmap[base / 64] &= ~val;
200 base += 64 - (base & 63);
202 memset( &gaMainBitmap[base / 64], 0, size/8 );
204 Uint64 val = -1 << (size & 7);
206 gaMainBitmap[base / 64] &= ~val;
210 base = ent->Base >> 12;
211 size = ent->Size >> 12;
212 size = (size + (base & 63) + 63) >> 6;
215 Uint64 val = -1 << (base & 63);
216 gaSuperBitmap[base / 64] &= ~val;
218 base += 64 - (base & 63);
222 // Reference the used pages
224 base = 0x100000 >> 12;
225 size = ((tPAddr)&gKernelEnd - KERNEL_BASE - base) >> 12;
226 memset( &gaMainBitmap[base / 64], -1, size/8 );
228 Uint64 val = -1 << (size & 7);
230 gaMainBitmap[base / 64] |= val;
232 // - Reference Counts and Bitmap
233 vaddr = MM_PAGE_BITMAP;
234 for( i = 0; i < numPages; i++, vaddr ++ )
236 paddr = MM_GetPhysAddr(vaddr) >> 12;
237 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
239 vaddr = MM_PAGE_DBLBMP;
240 for( i = 0; i < numPages; i++, vaddr += 0x1000 )
242 paddr = MM_GetPhysAddr(vaddr) >> 12;
243 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
245 vaddr = MM_PAGE_SUPBMP;
246 for( i = 0; i < superPages; i++, vaddr += 0x1000 )
248 paddr = MM_GetPhysAddr(vaddr) >> 12;
249 gaMainBitmap[paddr >> 6] |= 1 << (paddr&63);
252 // Fill the super bitmap
253 memset(gaSuperBitmap, 0, superPages<<12);
254 for( base = 0; base < giMaxPhysPage/64; base ++)
256 if( gaMainBitmap[ base ] == -1 )
257 gaSuperBitmap[ base/64 ] |= 1 << (base&63);
262 * \brief Allocate a contiguous range of physical pages with a maximum
263 * bit size of \a Bits
264 * \param Num Number of pages to allocate
265 * \param Bits Maximum size of the physical address
266 * \note If \a Bits is <= 0, any sized address is used (with preference
267 * to higher addresses)
269 tPAddr MM_AllocPhysRange(int Num, int Bits)
275 Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
277 if( Bits <= 0 ) // Speedup for the common case
278 rangeID = MM_PHYS_MAX;
280 rangeID = MM_PHYS_MAX;
282 rangeID = MM_PHYS_32BIT;
284 rangeID = MM_PHYS_24BIT;
286 rangeID = MM_PHYS_20BIT;
288 rangeID = MM_PHYS_16BIT;
290 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
292 LOCK(&glPhysicalPages);
293 Log(" MM_AllocPhysRange: i has lock");
295 // Check if the range actually has any free pages
296 while(giPhysRangeFree[rangeID] == 0 && rangeID)
299 Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
301 // What the? Oh, man. No free pages
302 if(giPhysRangeFree[rangeID] == 0) {
303 RELEASE(&glPhysicalPages);
306 Warning(" MM_AllocPhysRange: Out of free pages");
308 "Out of memory (unable to fulfil request for %i pages), zero remaining",
314 // Check if there is enough in the range
315 if(giPhysRangeFree[rangeID] >= Num)
317 // Do a cheap scan, scanning upwards from the first free page in
320 addr = giPhysRangeFirst[ rangeID ];
321 while( addr < giPhysRangeLast[ rangeID ] )
323 // Check the super bitmap
324 if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
327 addr &= (1 << (6+6)) - 1;
330 // Check page block (64 pages)
331 if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
334 addr &= (1 << (12+6)) - 1;
337 // Check individual page
338 if( gaMainBitmap[addr >> 6] & (1 << (addr & 63)) ) {
348 // If we don't find a contiguous block, nFree will not be equal
349 // to Num, so we set it to zero and do the expensive lookup.
350 if(nFree != Num) nFree = 0;
355 // Oops. ok, let's do an expensive check (scan down the list
356 // until a free range is found)
358 addr = giPhysRangeLast[ rangeID ];
360 RELEASE(&glPhysicalPages);
364 "Out of memory (unable to fulfil request for %i pages)",
369 Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
371 // Mark pages as allocated
373 for( i = 0; i < Num; i++ )
375 gaiPageReferences[addr >> 6] |= 1 << (addr & 63);
377 if(addr >> 32) rangeID = MM_PHYS_MAX;
378 else if(addr >> 24) rangeID = MM_PHYS_32BIT;
379 else if(addr >> 20) rangeID = MM_PHYS_24BIT;
380 else if(addr >> 16) rangeID = MM_PHYS_20BIT;
381 else if(addr >> 0) rangeID = MM_PHYS_16BIT;
382 giPhysRangeFree[ rangeID ] --;
385 Num += addr & (64-1);
387 Num = (Num + (64-1)) & ~(64-1);
388 for( i = 0; i < Num/64; i++ )
390 if( gaMainBitmap[ addr >> 6 ] == -1 )
391 gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
394 RELEASE(&glPhysicalPages);
399 * \brief Allocate a single physical page, with no preference as to address
402 tPAddr MM_AllocPhys(void)
404 return MM_AllocPhysRange(1, -1);
408 * \brief Reference a physical page
410 void MM_RefPhys(tPAddr PAddr)
412 Uint64 page = PAddr >> 12;
414 if( PAddr >> 12 > giMaxPhysPage ) return ;
416 if( gaMainBitmap[ page >> 6 ] & (1 << (page&63)) )
419 gaMultiBitmap[ page >> 6 ] |= 1 << (page&63);
420 gaiPageReferences[ page ] ++;
425 gaMainBitmap[page >> 6] |= 1 << (page&63);
426 if( gaMainBitmap[page >> 6 ] == -1 )
427 gaSuperBitmap[page>> 12] |= 1 << ((page >> 6) & 63);
432 * \brief Dereference a physical page
434 void MM_DerefPhys(tPAddr PAddr)
436 Uint64 page = PAddr >> 12;
438 if( PAddr >> 12 > giMaxPhysPage ) return ;
440 if( gaMultiBitmap[ page >> 6 ] & (1 << (page&63)) ) {
441 gaiPageReferences[ page ] --;
442 if( gaiPageReferences[ page ] == 1 )
443 gaMultiBitmap[ page >> 6 ] &= ~(1 << (page&63));
444 if( gaiPageReferences[ page ] == 0 )
445 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
448 gaMainBitmap[ page >> 6 ] &= ~(1 << (page&63));
450 if(gaMainBitmap[ page >> 6 ] == 0) {
451 gaSuperBitmap[page >> 12] &= ~(1 << ((page >> 6) & 63));