]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/memory.c
perms
[plan9front.git] / sys / src / boot / pc / memory.c
1 /*
2  * Size memory and create the kernel page-tables on the fly while doing so.
3  * Called from main(), this code should only be run by the bootstrap processor.
4  */
5 #include "u.h"
6 #include "lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11
12 #define MEMDEBUG        0
13
14 #define PDX(va)         ((((ulong)(va))>>22) & 0x03FF)
15 #define PTX(va)         ((((ulong)(va))>>12) & 0x03FF)
16
17 enum {
18         MemUPA          = 0,            /* unbacked physical address */
19         MemRAM          = 1,            /* physical memory */
20         MemUMB          = 2,            /* upper memory block (<16MB) */
21         NMemType        = 3,
22
23         KB              = 1024,
24
25         MemMinMB        = 4,            /* minimum physical memory (<=4MB) */
26         MemMaxMB        = 768,          /* maximum physical memory to check */
27
28         NMemBase        = 10,
29 };
30
31 typedef struct {
32         int     size;
33         ulong   addr;
34 } Map;
35
36 typedef struct {
37         char*   name;
38         Map*    map;
39         Map*    mapend;
40
41         Lock;
42 } RMap;
43
44 static Map mapupa[8];
45 static RMap rmapupa = {
46         "unallocated unbacked physical memory",
47         mapupa,
48         &mapupa[7],
49 };
50
51 static Map xmapupa[8];
52 static RMap xrmapupa = {
53         "unbacked physical memory",
54         xmapupa,
55         &xmapupa[7],
56 };
57
58 static Map mapram[8];
59 static RMap rmapram = {
60         "physical memory",
61         mapram,
62         &mapram[7],
63 };
64
65 static Map mapumb[64];
66 static RMap rmapumb = {
67         "upper memory block",
68         mapumb,
69         &mapumb[63],
70 };
71
72 static Map mapumbrw[8];
73 static RMap rmapumbrw = {
74         "UMB device memory",
75         mapumbrw,
76         &mapumbrw[7],
77 };
78
79 void
80 memdebug(void)
81 {
82         Map *mp;
83         ulong maxpa, maxpa1, maxpa2;
84
85         if(MEMDEBUG == 0)
86                 return;
87
88         maxpa = (nvramread(0x18)<<8)|nvramread(0x17);
89         maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30);
90         maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15);
91         print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n",
92                 maxpa, MB+maxpa*KB, maxpa1, maxpa2);
93
94         for(mp = rmapram.map; mp->size; mp++)
95                 print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
96         for(mp = rmapumb.map; mp->size; mp++)
97                 print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
98         for(mp = rmapumbrw.map; mp->size; mp++)
99                 print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
100         for(mp = rmapupa.map; mp->size; mp++)
101                 print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
102 }
103
104 void
105 mapfree(RMap* rmap, ulong addr, ulong size)
106 {
107         Map *mp;
108         ulong t;
109
110         if(size == 0)
111                 return;
112
113         lock(rmap);
114         for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
115                 ;
116
117         if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
118                 (mp-1)->size += size;
119                 if(addr+size == mp->addr){
120                         (mp-1)->size += mp->size;
121                         while(mp->size){
122                                 mp++;
123                                 (mp-1)->addr = mp->addr;
124                                 (mp-1)->size = mp->size;
125                         }
126                 }
127         }
128         else{
129                 if(addr+size == mp->addr && mp->size){
130                         mp->addr -= size;
131                         mp->size += size;
132                 }
133                 else do{
134                         if(mp >= rmap->mapend){
135                                 print("mapfree: %s: losing 0x%luX, %lud\n",
136                                         rmap->name, addr, size);
137                                 break;
138                         }
139                         t = mp->addr;
140                         mp->addr = addr;
141                         addr = t;
142                         t = mp->size;
143                         mp->size = size;
144                         mp++;
145                 }while(size = t);
146         }
147         unlock(rmap);
148 }
149
150 ulong
151 mapalloc(RMap* rmap, ulong addr, int size, int align)
152 {
153         Map *mp;
154         ulong maddr, oaddr;
155
156         lock(rmap);
157         for(mp = rmap->map; mp->size; mp++){
158                 maddr = mp->addr;
159
160                 if(addr){
161                         /*
162                          * A specific address range has been given:
163                          *   if the current map entry is greater then
164                          *   the address is not in the map;
165                          *   if the current map entry does not overlap
166                          *   the beginning of the requested range then
167                          *   continue on to the next map entry;
168                          *   if the current map entry does not entirely
169                          *   contain the requested range then the range
170                          *   is not in the map.
171                          */
172                         if(maddr > addr)
173                                 break;
174                         if(mp->size < addr - maddr)     /* maddr+mp->size < addr, but no overflow */
175                                 continue;
176                         if(addr - maddr > mp->size - size)      /* addr+size > maddr+mp->size, but no overflow */
177                                 break;
178                         maddr = addr;
179                 }
180
181                 if(align > 0)
182                         maddr = ((maddr+align-1)/align)*align;
183                 if(mp->addr+mp->size-maddr < size)
184                         continue;
185
186                 oaddr = mp->addr;
187                 mp->addr = maddr+size;
188                 mp->size -= maddr-oaddr+size;
189                 if(mp->size == 0){
190                         do{
191                                 mp++;
192                                 (mp-1)->addr = mp->addr;
193                         }while((mp-1)->size = mp->size);
194                 }
195
196                 unlock(rmap);
197                 if(oaddr != maddr)
198                         mapfree(rmap, oaddr, maddr-oaddr);
199
200                 return maddr;
201         }
202         unlock(rmap);
203
204         return 0;
205 }
206
207 static void
208 umbscan(void)
209 {
210         uchar *p;
211
212         /*
213          * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces
214          * which aren't used; they can be used later for devices which
215          * want to allocate some virtual address space.
216          * Check for two things:
217          * 1) device BIOS ROM. This should start with a two-byte header
218          *    of 0x55 0xAA, followed by a byte giving the size of the ROM
219          *    in 512-byte chunks. These ROM's must start on a 2KB boundary.
220          * 2) device memory. This is read-write.
221          * There are some assumptions: there's VGA memory at 0xA0000 and
222          * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature
223          * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up
224          * for grabs; check anyway.
225          */
226         p = KADDR(0xD0000);     /*RSC: changed from 0xC0000 */
227         while(p < (uchar*)KADDR(0xE0000)){
228                 if (p[0] == 0x55 && p[1] == 0xAA) {
229                         /* Skip p[2] chunks of 512 bytes.  Test for 0x55 AA before
230                              poking obtrusively, or else the Thinkpad X20 dies when
231                              setting up the cardbus (PB) */
232                         p += p[2] * 512;
233                         continue;
234                 }
235
236                 p[0] = 0xCC;
237                 p[2*KB-1] = 0xCC;
238                 if(p[0] != 0xCC || p[2*KB-1] != 0xCC){
239                         p[0] = 0x55;
240                         p[1] = 0xAA;
241                         p[2] = 4;
242                         if(p[0] == 0x55 && p[1] == 0xAA){
243                                 p += p[2]*512;
244                                 continue;
245                         }
246                         if(p[0] == 0xFF && p[1] == 0xFF)
247                                 mapfree(&rmapumb, PADDR(p), 2*KB);
248                 }
249                 else
250                         mapfree(&rmapumbrw, PADDR(p), 2*KB);
251                 p += 2*KB;
252         }
253
254         p = KADDR(0xE0000);
255         if(p[0] != 0x55 || p[1] != 0xAA){
256                 p[0] = 0xCC;
257                 p[64*KB-1] = 0xCC;
258                 if(p[0] != 0xCC && p[64*KB-1] != 0xCC)
259                         mapfree(&rmapumb, PADDR(p), 64*KB);
260         }
261 }
262
263
264 void
265 meminit(ulong)
266 {
267         /* A hack to initialize unbacked physical memory.  It's assumed PCI space is assigned by 
268              the BIOS in the 0xF0000000 range and 9load never needs more than 0x2000... to run. These
269              values leave ample space for memory allocations for uninitialized PCI cards (e.g. cardbus 
270              cards).  (pb) */
271         ulong maxmem = 0x40000000;
272
273         umbscan();
274         mapfree(&rmapupa, maxmem, 0x00000000-maxmem);
275         if(MEMDEBUG)
276                 memdebug();
277 }
278
279 ulong
280 umbmalloc(ulong addr, int size, int align)
281 {
282         ulong a;
283
284         if(a = mapalloc(&rmapumb, addr, size, align))
285                 return (ulong)KADDR(a);
286
287         return 0;
288 }
289
290 void
291 umbfree(ulong addr, int size)
292 {
293         mapfree(&rmapumb, PADDR(addr), size);
294 }
295
296 ulong
297 umbrwmalloc(ulong addr, int size, int align)
298 {
299         ulong a;
300         uchar *p;
301
302         if(a = mapalloc(&rmapumbrw, addr, size, align))
303                 return(ulong)KADDR(a);
304
305         /*
306          * Perhaps the memory wasn't visible before
307          * the interface is initialised, so try again.
308          */
309         if((a = umbmalloc(addr, size, align)) == 0)
310                 return 0;
311         p = (uchar*)a;
312         p[0] = 0xCC;
313         p[size-1] = 0xCC;
314         if(p[0] == 0xCC && p[size-1] == 0xCC)
315                 return a;
316         umbfree(a, size);
317
318         return 0;
319 }
320
321 void
322 umbrwfree(ulong addr, int size)
323 {
324         mapfree(&rmapumbrw, PADDR(addr), size);
325 }
326
327 ulong*
328 mmuwalk(ulong* pdb, ulong va, int level, int create)
329 {
330         ulong pa, *table;
331
332         /*
333          * Walk the page-table pointed to by pdb and return a pointer
334          * to the entry for virtual address va at the requested level.
335          * If the entry is invalid and create isn't requested then bail
336          * out early. Otherwise, for the 2nd level walk, allocate a new
337          * page-table page and register it in the 1st level.
338          */
339         table = &pdb[PDX(va)];
340         if(!(*table & PTEVALID) && create == 0)
341                 return 0;
342
343         switch(level){
344
345         default:
346                 return 0;
347
348         case 1:
349                 return table;
350
351         case 2:
352                 if(*table & PTESIZE)
353                         panic("mmuwalk2: va 0x%ux entry 0x%ux", va, *table);
354                 if(!(*table & PTEVALID)){
355                         pa = PADDR(ialloc(BY2PG, BY2PG));
356                         *table = pa|PTEWRITE|PTEVALID;
357                 }
358                 table = KADDR(PPN(*table));
359
360                 return &table[PTX(va)];
361         }
362 }
363
364 static Lock mmukmaplock;
365
366 ulong
367 mmukmap(ulong pa, ulong va, int size)
368 {
369         ulong pae, *table, *pdb, pgsz, *pte, x;
370         int pse, sync;
371         extern int cpuidax, cpuiddx;
372
373         pdb = KADDR(getcr3());
374         if((cpuiddx & 0x08) && (getcr4() & 0x10))
375                 pse = 1;
376         else
377                 pse = 0;
378         sync = 0;
379
380         pa = PPN(pa);
381         if(va == 0)
382                 va = (ulong)KADDR(pa);
383         else
384                 va = PPN(va);
385
386         pae = pa + size;
387         lock(&mmukmaplock);
388         while(pa < pae){
389                 table = &pdb[PDX(va)];
390                 /*
391                  * Possibly already mapped.
392                  */
393                 if(*table & PTEVALID){
394                         if(*table & PTESIZE){
395                                 /*
396                                  * Big page. Does it fit within?
397                                  * If it does, adjust pgsz so the correct end can be
398                                  * returned and get out.
399                                  * If not, adjust pgsz up to the next 4MB boundary
400                                  * and continue.
401                                  */
402                                 x = PPN(*table);
403                                 if(x != pa)
404                                         panic("mmukmap1: pa 0x%ux  entry 0x%ux",
405                                                 pa, *table);
406                                 x += 4*MB;
407                                 if(pae <= x){
408                                         pa = pae;
409                                         break;
410                                 }
411                                 pgsz = x - pa;
412                                 pa += pgsz;
413                                 va += pgsz;
414
415                                 continue;
416                         }
417                         else{
418                                 /*
419                                  * Little page. Walk to the entry.
420                                  * If the entry is valid, set pgsz and continue.
421                                  * If not, make it so, set pgsz, sync and continue.
422                                  */
423                                 pte = mmuwalk(pdb, va, 2, 0);
424                                 if(pte && *pte & PTEVALID){
425                                         x = PPN(*pte);
426                                         if(x != pa)
427                                                 panic("mmukmap2: pa 0x%ux entry 0x%ux",
428                                                         pa, *pte);
429                                         pgsz = BY2PG;
430                                         pa += pgsz;
431                                         va += pgsz;
432                                         sync++;
433
434                                         continue;
435                                 }
436                         }
437                 }
438
439                 /*
440                  * Not mapped. Check if it can be mapped using a big page -
441                  * starts on a 4MB boundary, size >= 4MB and processor can do it.
442                  * If not a big page, walk the walk, talk the talk.
443                  * Sync is set.
444                  */
445                 if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){
446                         *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID;
447                         pgsz = 4*MB;
448                 }
449                 else{
450                         pte = mmuwalk(pdb, va, 2, 1);
451                         *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
452                         pgsz = BY2PG;
453                 }
454                 pa += pgsz;
455                 va += pgsz;
456                 sync++;
457         }
458         unlock(&mmukmaplock);
459
460         /*
461          * If something was added
462          * then need to sync up.
463          */
464         if(sync)
465                 putcr3(PADDR(pdb));
466
467         return pa;
468 }
469
470 ulong
471 upamalloc(ulong addr, int size, int align)
472 {
473         ulong ae, a;
474
475         USED(align);
476
477         if((a = mapalloc(&rmapupa, addr, size, align)) == 0){
478                 memdebug();
479                 return 0;
480         }
481
482         /*
483          * This is a travesty, but they all are.
484          */
485         ae = mmukmap(a, 0, size);
486
487         /*
488          * Should check here that it was all delivered
489          * and put it back and barf if not.
490          */
491         USED(ae);
492
493         /*
494          * Be very careful this returns a PHYSICAL address.
495          */
496         return a;
497 }
498
499 void
500 upafree(ulong pa, int size)
501 {
502         USED(pa, size);
503 }
504