]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/memory.c
kernel: cleanup the software mouse cursor mess
[plan9front.git] / sys / src / 9 / pc / memory.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8
9 enum {
10         MemUPA          = 0,    /* unbacked physical address */
11         MemUMB          = 1,    /* upper memory block (<16MB) */
12         MemRAM          = 2,    /* physical memory */
13         MemACPI         = 3,    /* ACPI tables */
14         MemReserved     = 4,    /* don't allocate */
15
16         KB = 1024,
17 };
18
19 u32int  MemMin;         /* set by l.s */
20
21 void*
22 rampage(void)
23 {
24         uintptr pa;
25         
26         if(conf.mem[0].npage != 0)
27                 return xspanalloc(BY2PG, BY2PG, 0);
28
29         /*
30          * Allocate from the map directly to make page tables.
31          */
32         pa = memmapalloc(-1, BY2PG, BY2PG, MemRAM);
33         if(pa == -1 || cankaddr(pa) == 0)
34                 panic("rampage: out of memory\n");
35         return KADDR(pa);
36 }
37
38 static void
39 mapkzero(uintptr base, uintptr len, int type)
40 {
41         uintptr flags, n;
42
43         if(base < MemMin && base+len > MemMin){
44                 mapkzero(base, MemMin-base, type);
45                 len = base+len-MemMin;
46                 base = MemMin;
47         }
48
49         n = cankaddr(base);
50         if(n == 0)
51                 return;
52         if(len > n)
53                 len = n;
54
55         switch(type){
56         default:
57                 return;
58         case MemRAM:
59                 if(base < MemMin)
60                         return;
61                 flags = PTEWRITE|PTEVALID;
62                 break;
63         case MemUMB:
64                 if(base < MemMin)
65                         punmap(base+KZERO, len);
66                 flags = PTEWRITE|PTEUNCACHED|PTEVALID;
67                 break;
68         }
69 #ifdef PTENOEXEC
70         flags |= PTENOEXEC;
71 #endif
72         pmap(base|flags, base+KZERO, len);
73 }
74
75 static uintptr
76 ebdaseg(void)
77 {
78         uchar *bda;
79
80         if(memcmp(KADDR(0xfffd9), "EISA", 4) != 0)
81                 return 0;
82         bda = KADDR(0x400);
83         return ((bda[0x0f]<<8)|bda[0x0e]) << 4;
84 }
85
86 static uintptr
87 convmemsize(void)
88 {
89         uintptr top;
90         uchar *bda;
91
92         bda = KADDR(0x400);
93         top = ((bda[0x14]<<8) | bda[0x13])*KB;
94
95         if(top < 64*KB || top > 640*KB)
96                 top = 640*KB;   /* sanity */
97
98         /* Reserved for BIOS tables */
99         top -= 1*KB;
100
101         return top;
102 }
103
104 static void
105 lowraminit(void)
106 {
107         uintptr base, pa, len;
108         uchar *p;
109
110         /*
111          * Discover the memory bank information for conventional memory
112          * (i.e. less than 640KB). The base is the first location after the
113          * bootstrap processor MMU information and the limit is obtained from
114          * the BIOS data area.
115          */
116         base = PADDR(CPU0END);
117         pa = convmemsize();
118         if(base < pa)
119                 memmapadd(base, pa-base, MemRAM);
120
121         /* Reserve BIOS tables */
122         memmapadd(pa, 1*KB, MemReserved);
123
124         /* Reserve EBDA */
125         if((pa = ebdaseg()) != 0)
126                 memmapadd(pa, 1*KB, MemReserved);
127         memmapadd(0xA0000-1*KB, 1*KB, MemReserved);
128
129         /* Reserve the VGA frame buffer */
130         umballoc(0xA0000, 128*KB, 0);
131
132         /* Reserve VGA ROM */
133         memmapadd(0xC0000, 64*KB, MemReserved);
134
135         /*
136          * Scan the Upper Memory Blocks (0xD0000->0xF0000) for device BIOS ROMs.
137          * This should start with a two-byte header of 0x55 0xAA, followed by a
138          * byte giving the size of the ROM in 512-byte chunks.
139          * These ROM's must start on a 2KB boundary.
140          */
141         for(p = (uchar*)KADDR(0xD0000); p < (uchar*)KADDR(0xF0000); p += len){
142                 len = 2*KB;
143                 if(p[0] == 0x55 && p[1] == 0xAA){
144                         if(p[2] != 0)
145                                 len = p[2]*512;
146                         memmapadd(PADDR(p), len, MemReserved);
147                         len = ROUND(len, 2*KB);
148                 }
149         }
150
151         /* Reserve BIOS ROM */
152         memmapadd(0xF0000, 64*KB, MemReserved);
153 }
154
155 int
156 checksum(void *v, int n)
157 {
158         uchar *p, s;
159
160         s = 0;
161         p = v;
162         while(n-- > 0)
163                 s += *p++;
164         return s;
165 }
166
167 static void*
168 sigscan(uchar *addr, int len, char *sig, int size, int step)
169 {
170         uchar *e, *p;
171         int sl;
172
173         sl = strlen(sig);
174         e = addr+len-(size > sl ? size : sl);
175         for(p = addr; p <= e; p += step){
176                 if(memcmp(p, sig, sl) != 0)
177                         continue;
178                 if(size && checksum(p, size) != 0)
179                         continue;
180                 return p;
181         }
182         return nil;
183 }
184
185 void*
186 sigsearch(char* signature, int size)
187 {
188         uintptr p;
189         void *r;
190
191         /*
192          * Search for the data structure:
193          * 1) within the first KiB of the Extended BIOS Data Area (EBDA), or
194          * 2) within the last KiB of system base memory if the EBDA segment
195          *    is undefined, or
196          * 3) within the BIOS ROM address space between 0xf0000 and 0xfffff
197          *    (but will actually check 0xe0000 to 0xfffff).
198          */
199         if((p = ebdaseg()) != 0){
200                 if((r = sigscan(KADDR(p), 1*KB, signature, size, 16)) != nil)
201                         return r;
202         }
203         if((r = sigscan(KADDR(convmemsize()), 1*KB, signature, size, 16)) != nil)
204                 return r;
205
206         /* hack for virtualbox: look in KiB below 0xa0000 */
207         if((r = sigscan(KADDR(0xA0000-1*KB), 1*KB, signature, size, 16)) != nil)
208                 return r;
209
210         return sigscan(KADDR(0xE0000), 128*KB, signature, size, 16);
211 }
212
213 void*
214 rsdsearch(void)
215 {
216         static char signature[] = "RSD PTR ";
217         uintptr base, size;
218         uchar *v, *p;
219
220         if((p = sigsearch(signature, 36)) != nil)
221                 return p;
222         if((p = sigsearch(signature, 20)) != nil)
223                 return p;
224
225         for(base = memmapnext(-1, MemACPI); base != -1; base = memmapnext(base, MemACPI)){
226                 size = memmapsize(base, 0);
227                 if(size == 0 || size > 0x7fffffff)
228                         continue;
229                 if((v = vmap(base, size)) != nil){
230                         p = sigscan(v, size, signature, 36, 4);
231                         if(p == nil)
232                                 p = sigscan(v, size, signature, 20, 4);
233                         vunmap(v, size);
234                         if(p != nil)
235                                 return vmap(base + (p - v), 64);
236                 }
237         }
238         return nil;
239 }
240
241 /*
242  * Give out otherwise-unused physical address space
243  * for use in configuring devices.  Note that upaalloc
244  * does not map the physical address into virtual memory.
245  * Call vmap to do that.
246  */
247 ulong
248 upaalloc(ulong pa, ulong size, ulong align)
249 {
250         return (ulong)memmapalloc(pa == -1UL ? -1ULL : (uvlong)pa, size, align, MemUPA);
251 }
252
253 void
254 upafree(ulong pa, ulong size)
255 {
256         memmapfree(pa, size, MemUPA);
257 }
258
259 /*
260  * Allocate memory from the upper memory blocks.
261  */
262 ulong
263 umballoc(ulong pa, ulong size, ulong align)
264 {
265         return (ulong)memmapalloc(pa == -1UL ? -1ULL : (uvlong)pa, size, align, MemUMB);
266 }
267
268 void
269 umbfree(ulong pa, ulong size)
270 {
271         memmapfree(pa, size, MemUMB);
272 }
273
274 static void
275 umbexclude(void)
276 {
277         ulong pa, size;
278         char *op, *p, *rptr;
279
280         if((p = getconf("umbexclude")) == nil)
281                 return;
282
283         while(p && *p != '\0' && *p != '\n'){
284                 op = p;
285                 pa = strtoul(p, &rptr, 0);
286                 if(rptr == nil || rptr == p || *rptr != '-'){
287                         print("umbexclude: invalid argument <%s>\n", op);
288                         break;
289                 }
290                 p = rptr+1;
291
292                 size = strtoul(p, &rptr, 0) - pa + 1;
293                 if(size <= 0){
294                         print("umbexclude: bad range <%s>\n", op);
295                         break;
296                 }
297                 if(rptr != nil && *rptr == ',')
298                         *rptr++ = '\0';
299                 p = rptr;
300
301                 memmapalloc(pa, size, 0, MemUMB);
302         }
303 }
304
305 static int
306 e820scan(void)
307 {
308         uvlong base, top, size;
309         int type;
310         char *s;
311
312         /* passed by bootloader */
313         if((s = getconf("*e820")) == nil)
314                 if((s = getconf("e820")) == nil)
315                         return -1;
316
317         for(;;){
318                 while(*s == ' ')
319                         s++;
320                 if(*s == 0)
321                         break;
322                 type = 1;
323                 if(s[1] == ' '){        /* new format */
324                         type = s[0] - '0';
325                         s += 2;
326                 }
327                 base = strtoull(s, &s, 16);
328                 if(*s != ' ')
329                         break;
330                 top  = strtoull(s, &s, 16);
331                 if(*s != ' ' && *s != 0)
332                         break;
333                 if(base >= top)
334                         continue;
335                 switch(type){
336                 case 1:
337                         memmapadd(base, top - base, MemRAM);
338                         break;
339                 case 3:
340                         memmapadd(base, top - base, MemACPI);
341                         break;
342                 default:
343                         memmapadd(base, top - base, MemReserved);
344                 }
345         }
346
347         for(base = memmapnext(-1, MemRAM); base != -1; base = memmapnext(base, MemRAM)){
348                 size = memmapsize(base, BY2PG) & ~(BY2PG-1);
349                 if(size != 0)
350                         mapkzero(PGROUND(base), size, MemRAM);
351         }
352
353         return 0;
354 }
355
356 static void
357 ramscan(uintptr pa, uintptr top, uintptr chunk)
358 {
359         ulong save, pat, seed, *v, *k0;
360         int i, n, w;
361
362         pa += chunk-1;
363         pa &= ~(chunk-1);
364         top &= ~(chunk-1);
365
366         n = chunk/sizeof(*v);
367         w = BY2PG/sizeof(*v);
368
369         k0 = KADDR(0);
370         save = *k0;
371
372         pat = 0x12345678UL;
373         for(; pa < top; pa += chunk){
374                 /* write pattern */
375                 seed = pat;
376                 if((v = vmap(pa, chunk)) == nil)
377                         continue;
378                 for(i = 0; i < n; i += w){
379                         pat += 0x3141526UL;
380                         v[i] = pat;
381                         *k0 = ~pat;
382                         if(v[i] != pat)
383                                 goto Bad;
384                 }
385                 vunmap(v, chunk);
386
387                 /* verify pattern */
388                 pat = seed;
389                 if((v = vmap(pa, chunk)) == nil)
390                         continue;
391                 for(i = 0; i < n; i += w){
392                         pat += 0x3141526UL;
393                         if(v[i] != pat)
394                                 goto Bad;
395                 }
396                 vunmap(v, chunk);
397
398                 memmapadd(pa, chunk, MemRAM);
399                 mapkzero(pa, chunk, MemRAM);
400                 continue;
401
402         Bad:
403                 vunmap(v, chunk);
404
405                 if(pa+chunk <= 16*MB)
406                         memmapadd(pa, chunk, MemUMB);
407
408                 /*
409                  * If we encounter a chunk of missing memory
410                  * at a sufficiently high offset, call it the end of
411                  * memory.  Otherwise we run the risk of thinking
412                  * that video memory is real RAM.
413                  */
414                 if(pa >= 32*MB)
415                         break;
416         }
417
418         *k0 = save;
419 }
420
421 /*
422  * Sort out initial memory map and discover RAM.
423  */
424 void
425 meminit0(void)
426 {
427         /*
428          * Add the already mapped memory after the kernel.
429          */
430         if(MemMin < PADDR(PGROUND((uintptr)end)))
431                 panic("kernel too big");
432         memmapadd(PADDR(PGROUND((uintptr)end)), MemMin-PADDR(PGROUND((uintptr)end)), MemRAM);
433
434         /*
435          * Memory between KTZERO and end is the kernel itself.
436          */
437         memreserve(PADDR(KTZERO), PADDR(PGROUND((uintptr)end))-PADDR(KTZERO));
438
439         /*
440          * Memory below CPU0END is reserved for the kernel.
441          */
442         memreserve(0, PADDR(CPU0END));
443
444         /*
445          * Addresses below 16MB default to be upper
446          * memory blocks usable for ISA devices.
447          */
448         memmapadd(0, 16*MB, MemUMB);
449
450         /*
451          * Everything between 16MB and 4GB defaults
452          * to unbacked physical addresses usable for
453          * device mappings.
454          */
455         memmapadd(16*MB, (u32int)-16*MB, MemUPA);
456
457         /*
458          * On 386, reserve >= 4G as we have no PAE support.
459          */
460         if(sizeof(void*) == 4)
461                 memmapadd((u32int)-BY2PG, -((uvlong)((u32int)-BY2PG)), MemReserved);
462
463         /*
464          * Discover conventional RAM, ROMs and UMBs.
465          */
466         lowraminit();
467
468         /*
469          * Discover more RAM and map to KZERO.
470          */
471         if(e820scan() < 0)
472                 ramscan(MemMin, -((uintptr)MemMin), 4*MB);
473 }
474
475 /*
476  * Until the memory map is finalized by meminit(),
477  * archinit() should reserve memory of discovered BIOS
478  * and ACPI tables by calling memreserve() to prevent
479  * them from getting allocated and trashed.
480  * This is due to the UEFI and BIOS memory map being
481  * unreliable and sometimes marking these ranges as RAM.
482  */
483 void
484 memreserve(uintptr pa, uintptr size)
485 {
486         assert(conf.mem[0].npage == 0);
487
488         size += (pa & BY2PG-1);
489         size &= ~(BY2PG-1);
490         pa &= ~(BY2PG-1);
491         memmapadd(pa, size, MemReserved);
492 }
493
494 /*
495  * Finalize the memory map:
496  *  (re-)map the upper memory blocks
497  *  allocate all usable ram to the conf.mem[] banks
498  */
499 void
500 meminit(void)
501 {
502         uintptr base, size;
503         Confmem *cm;
504
505         umbexclude();
506         for(base = memmapnext(-1, MemUMB); base != -1; base = memmapnext(base, MemUMB)){
507                 size = memmapsize(base, BY2PG) & ~(BY2PG-1);
508                 if(size != 0)
509                         mapkzero(PGROUND(base), size, MemUMB);
510         }
511
512         cm = &conf.mem[0];
513         for(base = memmapnext(-1, MemRAM); base != -1; base = memmapnext(base, MemRAM)){
514                 size = memmapsize(base, BY2PG) & ~(BY2PG-1);
515                 if(size == 0)
516                         continue;
517                 cm->base = memmapalloc(base, size, BY2PG, MemRAM);
518                 if(cm->base == -1)
519                         continue;
520                 base = cm->base;
521                 cm->npage = size/BY2PG;
522                 if(++cm >= &conf.mem[nelem(conf.mem)])
523                         break;
524         }
525
526         if(0) memmapdump();
527 }