Fixes to 64-bit port, now working on physical memory allocation
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_phys.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Physical Memory Manager
5  */
6 #include <acess.h>
7 #include <mboot.h>
8 #include <mm_virt.h>
9
10 enum eMMPhys_Ranges
11 {
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
17         NUM_MM_PHYS_RANGES
18 };
19
20 // === IMPORTS ===
21 extern void     gKernelEnd;
22
23 // === GLOBALS ===
24 tSpinlock       glPhysicalPages;
25 Uint64  *gaSuperBitmap; // 1 bit = 64 Pages
26 Uint32  *gaiPageReferences = (void*)MM_PAGE_COUNTS;     // Reference Counts
27 tPAddr  giFirstFreePage;        // First possibly free page
28 Uint64  giPhysRangeFree[NUM_MM_PHYS_RANGES];    // Number of free pages in each range
29 Uint64  giPhysRangeFirst[NUM_MM_PHYS_RANGES];   // First free page in each range
30 Uint64  giPhysRangeLast[NUM_MM_PHYS_RANGES];    // Last free page in each range
31 Uint64  giMaxPhysPage = 0;      // Maximum Physical page
32
33 // === CODE ===
34 void MM_InitPhys_Multiboot(tMBoot_Info *MBoot)
35 {
36         tMBoot_MMapEnt  *mmapStart;
37         tMBoot_MMapEnt  *ent;
38         Uint64  maxAddr = 0;
39          int    numPages;
40         
41         Log("MM_InitPhys_Multiboot: (MBoot=%p)", MBoot);
42         
43         // Scan the physical memory map
44         // Looking for the top of physical memory
45         mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr );
46         Log(" MM_InitPhys_Multiboot: mmapStart = %p", mmapStart);
47         ent = mmapStart;
48         while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength )
49         {
50                 // Adjust for the size of the entry
51                 ent->Size += 4;
52                 Log(" MM_InitPhys_Multiboot: ent={Type:%i,Base:0x%x,Length:%x",
53                         ent->Type, ent->Base, ent->Length);
54                 
55                 // If entry is RAM and is above `maxAddr`, change `maxAddr`
56                 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
57                         maxAddr = ent->Base + ent->Length;
58                 
59                 // Go to next entry
60                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
61         }
62         
63         // Did we find a valid end?
64         if(maxAddr == 0) {
65                 // No, darn, let's just use the HighMem hack
66                 giMaxPhysPage = (MBoot->HighMem >> 2) + 256;    // HighMem is a kByte value
67         }
68         else {
69                 // Goodie, goodie gumdrops
70                 giMaxPhysPage = maxAddr >> 12;
71         }
72         Log(" MM_InitPhys_Multiboot: giMaxPhysPage = 0x%x", giMaxPhysPage);
73         
74         // Find a contigous section of memory to hold it in
75         // - Starting from the end of the kernel
76         // - We also need a region for the super bitmap
77         numPages = (giMaxPhysPage + 7) * sizeof(*gaiPageReferences);
78         numPages = (numPages + 0xFFF) >> 12;
79         Log(" MM_InitPhys_Multiboot: numPages = %i", numPages);
80         if(maxAddr == 0)
81         {
82                 // Ok, naieve allocation, just put it after the kernel
83                 tVAddr  vaddr = MM_PAGE_COUNTS;
84                 tPAddr  paddr = (tPAddr)&gKernelEnd - KERNEL_BASE;
85                 while(numPages --)
86                 {
87                         MM_Map(vaddr, paddr);
88                         vaddr += 0x1000;
89                         paddr += 0x1000;
90                 }
91                 // Allocate the super bitmap
92                 gaSuperBitmap = (void*) MM_MapHWPages(
93                         paddr,
94                         ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12
95                         );
96         }
97         // Scan for a nice range
98         else
99         {
100                 
101         }
102         
103         // Fill the bitmaps
104         // - initialise to one, then clear the avaliable areas
105         memset(gaSuperBitmap, -1, (giMaxPhysPage+64*8-1)/(64*8));
106         memset(gaiPageReferences, -1, giMaxPhysPage*sizeof(*gaiPageReferences));
107         // - Clear all Type=1 areas
108         for(
109                 ent = mmapStart;
110                 (Uint)ent < (Uint)mmapStart + MBoot->MMapLength;
111                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size )
112                 )
113         {
114                 // Check if the type is RAM
115                 if(ent->Type != 1)      continue;
116                 // Clear the range
117                 memset(
118                         &gaiPageReferences[ ent->Base >> 12 ],
119                         0,
120                         (ent->Size>>12)*sizeof(*gaiPageReferences)
121                         );
122         }
123 }
124
125 /**
126  * \brief Allocate a contiguous range of physical pages with a maximum
127  *        bit size of \a Bits
128  * \param Num   Number of pages to allocate
129  * \param Bits  Maximum size of the physical address
130  * \note If \a Bits is <= 0, any sized address is used (with preference
131  *       to higher addresses)
132  */
133 tPAddr MM_AllocPhysRange(int Num, int Bits)
134 {
135         tPAddr  addr;
136          int    rangeID;
137          int    nFree = 0, i;
138         
139         Log("MM_AllocPhysRange: (Num=%i,Bits=%i)", Num, Bits);
140         
141         if( Bits <= 0 ) // Speedup for the common case
142                 rangeID = MM_PHYS_MAX;
143         else if( Bits > 32 )
144                 rangeID = MM_PHYS_MAX;
145         else if( Bits > 24 )
146                 rangeID = MM_PHYS_32BIT;
147         else if( Bits > 20 )
148                 rangeID = MM_PHYS_24BIT;
149         else if( Bits > 16 )
150                 rangeID = MM_PHYS_20BIT;
151         else
152                 rangeID = MM_PHYS_16BIT;
153         
154         Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
155         
156         LOCK(&glPhysicalPages);
157         Log(" MM_AllocPhysRange: i has lock");
158         
159         // Check if the range actually has any free pages
160         while(giPhysRangeFree[rangeID] == 0 && rangeID)
161                 rangeID --;
162         
163         Log(" MM_AllocPhysRange: rangeID = %i", rangeID);
164         
165         // What the? Oh, man. No free pages
166         if(giPhysRangeFree[rangeID] == 0) {
167                 RELEASE(&glPhysicalPages);
168                 // TODO: Page out
169                 // ATM. Just Warning
170                 Warning(" MM_AllocPhysRange: Out of free pages");
171                 Log_Warning("Arch",
172                         "Out of memory (unable to fulfil request for %i pages), zero remaining",
173                         Num
174                         );
175                 return 0;
176         }
177         
178         // Check if there is enough in the range
179         if(giPhysRangeFree[rangeID] >= Num)
180         {
181                 // Do a cheap scan, scanning upwards from the first free page in
182                 // the range
183                 nFree = 1;
184                 addr = giPhysRangeFirst[ rangeID ];
185                 while( addr < giPhysRangeLast[ rangeID ] )
186                 {
187                         // Check the super bitmap
188                         if( gaSuperBitmap[addr >> (6+6)] == -1 ) {
189                                 nFree = 0;
190                                 addr += 1 << (6+6);
191                                 addr &= (1 << (6+6)) - 1;
192                                 continue;
193                         }
194                         // Check page block (64 pages)
195                         if( gaSuperBitmap[addr >> (6+6)] & (1 << (addr>>6)&63)) {
196                                 nFree = 0;
197                                 addr += 1 << (12+6);
198                                 addr &= (1 << (12+6)) - 1;
199                                 continue;
200                         }
201                         // Check individual page
202                         if( gaiPageReferences[addr] ) {
203                                 nFree = 0;
204                                 addr ++;
205                                 continue;
206                         }
207                         nFree ++;
208                         addr ++;
209                         if(nFree == Num)
210                                 break;
211                 }
212                 // If we don't find a contiguous block, nFree will not be equal
213                 // to Num, so we set it to zero and do the expensive lookup.
214                 if(nFree != Num)        nFree = 0;
215         }
216         
217         if( !nFree )
218         {
219                 // Oops. ok, let's do an expensive check (scan down the list
220                 // until a free range is found)
221                 nFree = 1;
222                 addr = giPhysRangeLast[ rangeID ];
223                 // TODO
224                 RELEASE(&glPhysicalPages);
225                 // TODO: Page out
226                 // ATM. Just Warning
227                 Log_Warning("Arch",
228                         "Out of memory (unable to fulfil request for %i pages)",
229                         Num
230                         );
231                 return 0;
232         }
233         Log(" MM_AllocPhysRange: nFree = %i, addr = 0x%08x", nFree, addr);
234         
235         // Mark pages as allocated
236         addr -= Num;
237         for( i = 0; i < Num; i++ )
238         {
239                 gaiPageReferences[addr] = 1;
240                 
241                      if(addr >> 32)     rangeID = MM_PHYS_MAX;
242                 else if(addr >> 24)     rangeID = MM_PHYS_32BIT;
243                 else if(addr >> 20)     rangeID = MM_PHYS_24BIT;
244                 else if(addr >> 16)     rangeID = MM_PHYS_20BIT;
245                 else if(addr >> 0)      rangeID = MM_PHYS_16BIT;
246                 giPhysRangeFree[ rangeID ] --;
247         }
248         // Fill super bitmap
249         Num += addr & (64-1);
250         addr &= ~(64-1);
251         Num = (Num + (64-1)) & ~(64-1);
252         for( i = 0; i < Num/64; i++ )
253         {
254                  int    j, bFull = 1;
255                 for( j = 0; j < 64; j ++ ) {
256                         if( gaiPageReferences[addr+i*64+j] ) {
257                                 bFull = 0;
258                                 break;
259                         }
260                 }
261                 if( bFull )
262                         gaSuperBitmap[addr>>12] |= 1 << ((addr >> 6) & 64);
263         }
264         
265         RELEASE(&glPhysicalPages);
266         return addr << 12;
267 }
268
269 /**
270  * \brief Allocate a single physical page, with no preference as to address
271  *        size.
272  */
273 tPAddr MM_AllocPhys(void)
274 {
275         return MM_AllocPhysRange(1, -1);
276 }
277
278 /**
279  * \brief Reference a physical page
280  */
281 void MM_RefPhys(tPAddr PAddr)
282 {
283          int    bIsFull, j;
284         if( PAddr >> 12 > giMaxPhysPage )       return ;
285         gaiPageReferences[ PAddr >> 12 ] ++;
286         
287         bIsFull = 1;
288         for( j = 0; j < 64; j++ ) {
289                 if( gaiPageReferences[ PAddr >> 12 ] == 0 ) {
290                         bIsFull = 0;
291                         break;
292                 }
293         }
294         if( bIsFull )
295                 gaSuperBitmap[PAddr >> 24] |= 1 << ((PAddr >> 18) & 64);
296 }
297
298 /**
299  * \brief Dereference a physical page
300  */
301 void MM_DerefPhys(tPAddr PAddr)
302 {
303         if( PAddr >> 12 > giMaxPhysPage )       return ;
304         gaiPageReferences[ PAddr >> 12 ] --;
305         if( gaiPageReferences[ PAddr >> 12 ] )
306         {
307                 gaSuperBitmap[PAddr >> 24] &= ~(1 << ((PAddr >> 18) & 64));
308         }
309 }

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