source: rtems/bsps/powerpc/motorola_powerpc/bootloader/mm.c @ 03e1d837

5
Last change on this file since 03e1d837 was 03e1d837, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 24, 2018 at 5:06:36 AM

bsps/powerpc: Move bootloader to bsps

This bootloader is only used by the motorola_powerpc BSP.

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 27.7 KB
Line 
1/*
2 *  mm.c -- Crude memory management for early boot.
3 *
4 *  Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es
5 *
6 *  Modified to compile in RTEMS development environment
7 *  by Eric Valette
8 *
9 *  Copyright (C) 1999 Eric Valette. valette@crf.canon.fr
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.org/license/LICENSE.
14 */
15
16/* This code is a crude memory manager for early boot for LinuxPPC.
17 * As such, it does not try to perform many optimiztions depending
18 * on the processor, it only uses features which are common to
19 * all processors (no BATs...).
20 *
21 * On PreP platorms (the only ones on which it works for now),
22 * it maps 1:1 all RAM/ROM and I/O space as claimed by the
23 * residual data. The holes between these areas can be virtually
24 * remapped to any of these, since for some functions it is very handy
25 * to have virtually contiguous but physically discontiguous memory.
26 *
27 * Physical memory allocation is also very crude, since it's only
28 * designed to manage a small number of large chunks. For valloc/vfree
29 * and palloc/pfree, the unit of allocation is the 4kB page.
30 *
31 * The salloc/sfree has been added after tracing gunzip and seeing
32 * how it performed a very large number of small allocations.
33 * For these the unit of allocation is 8 bytes (the s stands for
34 * small or subpage). This memory is cleared when allocated.
35 *
36 */
37
38#include <rtems/bspIo.h>
39
40#include <sys/types.h>
41#include <libcpu/spr.h>
42#include "bootldr.h"
43#include <libcpu/mmu.h>
44#include <limits.h>
45
46/* to align the pointer to the (next) page boundary */
47#define PAGE_ALIGN(addr)        (((addr)+PAGE_SIZE-1)&PAGE_MASK)
48
49extern void (tlb_handlers)(void);
50extern void (_handler_glue)(void);
51
52/* We use our own kind of simple memory areas for the loader, but
53 * we want to avoid potential clashes with kernel includes.
54 * Here a map maps contiguous areas from base to end,
55 * the firstpte entry corresponds to physical address and has the low
56 * order bits set for caching and permission.
57 */
58
59typedef struct _map {
60        struct _map *next;
61        u_long base;
62        u_long end;
63        u_long firstpte;
64} map;
65
66/* The LSB of the firstpte entries on map lists other than mappings
67 * are constants which can be checked for debugging. All these constants
68 * have bit of weight 4 set, this bit is zero in the mappings list entries.
69 * Actually firstpte&7 value is:
70 * - 0 or 1 should not happen
71 * - 2 for RW actual virtual->physical mappings
72 * - 3 for RO actual virtual->physical mappings
73 * - 6 for free areas to be suballocated by salloc
74 * - 7 for salloc'ated areas
75 * - 4 or 5 for all others, in this case firtpte & 63 is
76 *   - 4 for unused maps (on the free list)
77 *   - 12 for free physical memory
78 *   - 13 for physical memory in use
79 *   - 20 for free virtual address space
80 *   - 21 for allocated virtual address space
81 *   - 28 for physical memory space suballocated by salloc
82 *   - 29 for physical memory that can't be freed
83 */
84
85#define MAP_FREE_SUBS 6
86#define MAP_USED_SUBS 7
87
88#define MAP_FREE 4
89#define MAP_FREE_PHYS 12
90#define MAP_USED_PHYS 13
91#define MAP_FREE_VIRT 20
92#define MAP_USED_VIRT 21
93#define MAP_SUBS_PHYS 28
94#define MAP_PERM_PHYS 29
95
96SPR_RW(SDR1);
97SPR_RO(DSISR);
98SPR_RO(PPC_DAR);
99
100/* We need a few statically allocated free maps to bootstrap the
101 * memory managment */
102static map free_maps[4] = {{free_maps+1, 0, 0, MAP_FREE},
103                           {free_maps+2, 0, 0, MAP_FREE},
104                           {free_maps+3, 0, 0, MAP_FREE},
105                           {NULL, 0, 0, MAP_FREE}};
106struct _mm_private {
107        void *sdr1;
108        u_long hashmask;
109        map *freemaps;     /* Pool of unused map structs */
110        map *mappings;     /* Sorted list of virtual->physical mappings */
111        map *physavail;    /* Unallocated physical address space */
112        map *physused;     /* Allocated physical address space */
113        map *physperm;     /* Permanently allocated physical space */
114        map *virtavail;    /* Unallocated virtual address space */
115        map *virtused;     /* Allocated virtual address space */
116        map *sallocfree;   /* Free maps for salloc */
117        map *sallocused;   /* Used maps for salloc */
118        map *sallocphys;   /* Physical areas used by salloc */
119        u_int hashcnt;     /* Used to cycle in PTEG when they overflow */
120} mm_private = {hashmask: 0xffc0,
121                freemaps: free_maps+0};
122
123/* A simplified hash table entry declaration */
124typedef struct _hash_entry {
125        int key;
126        u_long rpn;
127} hash_entry;
128
129void print_maps(map *, const char *);
130
131/* The handler used for all exceptions although for now it is only
132 * designed to properly handle MMU interrupts to fill the hash table.
133 */
134void _handler(int vec, ctxt *p) {
135        map *area;
136        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
137        u_long vaddr, cause;
138        if (vec==4 || vec==7) { /* ISI exceptions are different */
139                vaddr = p->nip;
140                cause = p->msr;
141        } else { /* Valid for DSI and alignment exceptions */
142                vaddr = _read_PPC_DAR();
143                cause = _read_DSISR();
144        }
145
146        if (vec==3 || vec==4) {
147                /* Panic if the fault is not PTE not found. */
148                if (!(cause & 0x40000000)) {
149                        MMUon();
150                        printk("\nPanic: vector=%x, cause=%lx\n", vec, cause);
151                        hang("Memory protection violation at ", vaddr, p);
152                }
153
154                for(area=mm->mappings; area; area=area->next) {
155                        if(area->base<=vaddr && vaddr<=area->end) break;
156                }
157
158                if (area) {
159                        u_long hash, vsid, rpn;
160                        hash_entry volatile *hte, *_hte1;
161                        u_int i, alt=0, flushva;
162
163                        vsid = _read_SR((void *)vaddr);
164                        rpn = (vaddr&PAGE_MASK)-area->base+area->firstpte;
165                        hash = vsid<<6;
166                        hash ^= (vaddr>>(PAGE_SHIFT-6))&0x3fffc0;
167                        hash &= mm->hashmask;
168                        /* Find an empty entry in the PTEG, else
169                         * replace a random one.
170                         */
171                        hte = (hash_entry *) ((u_long)(mm->sdr1)+hash);
172                        for (i=0; i<8; i++) {
173                                if (hte[i].key>=0) goto found;
174                        }
175                        hash ^= mm->hashmask;
176                        alt = 0x40; _hte1 = hte;
177                        hte = (hash_entry *) ((u_long)(mm->sdr1)+hash);
178
179                        for (i=0; i<8; i++) {
180                                if (hte[i].key>=0) goto found;
181                        }
182                        alt = 0;
183                        hte = _hte1;
184                        /* Chose a victim entry and replace it. There might be
185                         * better policies to choose the victim, but in a boot
186                         * loader we want simplicity as long as it works.
187                         *
188                         * We would not need to invalidate the TLB entry since
189                         * the mapping is still valid. But this would be a mess
190                         * when unmapping so we make sure that the TLB is a
191                         * subset of the hash table under all circumstances.
192                         */
193                        i = mm->hashcnt;
194                        mm->hashcnt = (mm->hashcnt+1)%8;
195                        /* Note that the hash is already complemented here ! */
196                        flushva = (~(hash<<9)^((hte[i].key)<<5)) &0x3ff000;
197                        if (hte[i].key&0x40) flushva^=0x3ff000;
198                        flushva |= ((hte[i].key<<21)&0xf0000000)
199                          | ((hte[i].key<<22)&0x0fc00000);
200                        hte[i].key=0;
201                        asm volatile("sync; tlbie %0; sync" : : "r" (flushva));
202                found:
203                        hte[i].rpn = rpn;
204                        asm volatile("eieio": : );
205                        hte[i].key = 0x80000000|(vsid<<7)|alt|
206                          ((vaddr>>22)&0x3f);
207                        return;
208                } else {
209                        MMUon();
210                        printk("\nPanic: vector=%x, cause=%lx\n", vec, cause);
211                        hang("\nInvalid memory access attempt at ", vaddr, p);
212                }
213        } else {
214          MMUon();
215          printk(
216                "\nPanic: vector=%d, dsisr=%lx, faultaddr =%lx, "
217                  "msr=%lx opcode=%x\n", vec,
218                 cause, p->nip, p->msr, * ((unsigned int*) p->nip) );
219          if (vec == 7) {
220            unsigned int* ptr = ((unsigned int*) p->nip) - 4 * 10;
221            for (; ptr <= (((unsigned int*) p->nip) + 4 * 10); ptr ++)
222              printk("Hexdecimal code at address %p = %x\n", ptr, *ptr);
223          }
224          hang("Program or alignment exception at ", vaddr, p);
225        }
226}
227
228/* Generic routines for map handling.
229 */
230
231static inline
232void free_map(map *p) {
233        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
234        if (!p) return;
235        p->next=mm->freemaps;
236        mm->freemaps=p;
237        p->firstpte=MAP_FREE;
238}
239
240/* Sorted insertion in linked list */
241static
242int insert_map(map **head, map *p) {
243        map *q = *head;
244        if (!p) return 0;
245        if (q && (q->base < p->base)) {
246                for(;q->next && q->next->base<p->base; q = q->next);
247                if ((q->end >= p->base) ||
248                    (q->next && p->end>=q->next->base)) {
249                        free_map(p);
250                        printk("Overlapping areas!\n");
251                        return 1;
252                }
253                p->next = q->next;
254                q->next = p;
255        } else { /* Insert at head */
256                if (q && (p->end >= q->base)) {
257                        free_map(p);
258                        printk("Overlapping areas!\n");
259                        return 1;
260                }
261                p->next = q;
262                *head = p;
263        }
264        return 0;
265}
266
267/* Removal from linked list */
268
269static
270map *remove_map(map **head, map *p) {
271        map *q = *head;
272
273        if (!p || !q) return NULL;
274        if (q==p) {
275                *head = q->next;
276                return p;
277        }
278        for(;q && q->next!=p; q=q->next);
279        if (q) {
280                q->next=p->next;
281                return p;
282        } else {
283                return NULL;
284        }
285}
286
287static
288map *remove_map_at(map **head, void * vaddr) {
289        map *p, *q = *head;
290
291        if (!vaddr || !q) return NULL;
292        if (q->base==(u_long)vaddr) {
293                *head = q->next;
294                return q;
295        }
296        while (q->next && q->next->base != (u_long)vaddr) q=q->next;
297        p=q->next;
298        if (p) q->next=p->next;
299        return p;
300}
301
302static inline
303map * alloc_map_page(void) {
304        map *from, *p;
305        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
306
307        /* printk("Allocating new map page !"); */
308        /* Get the highest page */
309        for (from=mm->physavail; from && from->next; from=from->next);
310        if (!from) return NULL;
311
312        from->end -= PAGE_SIZE;
313
314        mm->freemaps = (map *) (from->end+1);
315
316        for(p=mm->freemaps; p<mm->freemaps+PAGE_SIZE/sizeof(map)-1; p++) {
317                p->next = p+1;
318                p->firstpte = MAP_FREE;
319        }
320        (p-1)->next=0;
321
322        /* Take the last one as pointer to self and insert
323         * the map into the permanent map list.
324         */
325
326        p->firstpte = MAP_PERM_PHYS;
327        p->base=(u_long) mm->freemaps;
328        p->end = p->base+PAGE_SIZE-1;
329
330        insert_map(&mm->physperm, p);
331
332        if (from->end+1 == from->base)
333                free_map(remove_map(&mm->physavail, from));
334
335        return mm->freemaps;
336}
337
338static
339map * alloc_map(void) {
340        map *p;
341        struct _mm_private * mm = (struct _mm_private *) bd->mm_private;
342
343        p = mm->freemaps;
344        if (!p) {
345                p=alloc_map_page();
346        }
347
348        if(p) mm->freemaps=p->next;
349
350        return p;
351}
352
353static
354void coalesce_maps(map *p) {
355        while(p) {
356                if (p->next && (p->end+1 == p->next->base)) {
357                        map *q=p->next;
358                        p->end=q->end;
359                        p->next=q->next;
360                        free_map(q);
361                } else {
362                        p = p->next;
363                }
364        }
365}
366
367/* These routines are used to find the free memory zones to avoid
368 * overlapping destructive copies when initializing.
369 * They work from the top because of the way we want to boot.
370 * In the following the term zone refers to the memory described
371 * by one or several contiguous so called segments in the
372 * residual data.
373 */
374#define STACK_PAGES 2
375static inline u_long
376find_next_zone(RESIDUAL *res, u_long lowpage, u_long flags) {
377        u_long i, newmin=0, size=0;
378        for(i=0; i<res->ActualNumMemSegs; i++) {
379                if (res->Segs[i].Usage & flags
380                    && res->Segs[i].BasePage<lowpage
381                    && res->Segs[i].BasePage>newmin) {
382                        newmin=res->Segs[i].BasePage;
383                        size=res->Segs[i].PageCount;
384                }
385        }
386        return newmin+size;
387}
388
389static inline u_long
390find_zone_start(RESIDUAL *res, u_long highpage, u_long flags) {
391        u_long i;
392        int progress;
393        do {
394                progress=0;
395                for (i=0; i<res->ActualNumMemSegs; i++) {
396                        if ( (res->Segs[i].BasePage+res->Segs[i].PageCount
397                              == highpage)
398                             && res->Segs[i].Usage & flags) {
399                                highpage=res->Segs[i].BasePage;
400                                progress=1;
401                        }
402                }
403        } while(progress);
404        return highpage;
405}
406
407/* The Motorola NT firmware does not provide any setting in the residual
408 * data about memory segment usage. The following table provides enough
409 * info so that this bootloader can work.
410 */
411MEM_MAP seg_fix[] = {
412    { 0x2000, 0xFFF00, 0x00100 },
413    { 0x0020, 0x02000, 0x7E000 },
414    { 0x0008, 0x00800, 0x00168 },
415    { 0x0004, 0x00000, 0x00005 },
416    { 0x0001, 0x006F1, 0x0010F },
417    { 0x0002, 0x006AD, 0x00044 },
418    { 0x0010, 0x00005, 0x006A8 },
419    { 0x0010, 0x00968, 0x00698 },
420    { 0x0800, 0xC0000, 0x3F000 },
421    { 0x0600, 0xBF800, 0x00800 },
422    { 0x0500, 0x81000, 0x3E800 },
423    { 0x0480, 0x80800, 0x00800 },
424    { 0x0440, 0x80000, 0x00800 } };
425
426/* The Motorola NT firmware does not set up all required info in the residual
427 * data. This routine changes some things in a way that the bootloader and
428 * linux are happy.
429 */
430static void
431fix_residual( RESIDUAL *res )
432{
433#if 0
434    PPC_DEVICE *hostbridge;
435#endif
436    int i;
437
438    /* Missing memory segment information */
439    res->ActualNumMemSegs = sizeof(seg_fix)/sizeof(MEM_MAP);
440    for (i=0; i<res->ActualNumMemSegs; i++) {
441        res->Segs[i].Usage = seg_fix[i].Usage;
442        res->Segs[i].BasePage = seg_fix[i].BasePage;
443        res->Segs[i].PageCount = seg_fix[i].PageCount;
444    }
445    /* The following should be fixed in the current version of the
446     * kernel and of the bootloader.
447     */
448#if 0
449    /* PPCBug has this zero */
450    res->VitalProductData.CacheLineSize = 0;
451    /* Motorola NT firmware sets TimeBaseDivisor to 0 */
452    if ( res->VitalProductData.TimeBaseDivisor == 0 ) {
453        res->VitalProductData.TimeBaseDivisor = 4000;
454    }
455
456    /* Motorola NT firmware records the PCIBridge as a "PCIDEVICE" and
457     * sets "PCIBridgeDirect". This bootloader and linux works better if
458     * BusId = "PROCESSORDEVICE" and Interface = "PCIBridgeIndirect".
459     */
460    hostbridge=residual_find_device(PCIDEVICE, NULL,
461                                        BridgeController,
462                                        PCIBridge, -1, 0);
463    if (hostbridge) {
464        hostbridge->DeviceId.BusId = PROCESSORDEVICE;
465        hostbridge->DeviceId.Interface = PCIBridgeIndirect;
466    }
467#endif
468}
469
470/* This routine is the first C code called with very little stack space!
471 * Its goal is to find where the boot image can be moved. This will
472 * be the highest address with enough room.
473 */
474int early_setup(u_long image_size) {
475        register RESIDUAL *res = bd->residual;
476        u_long minpages = PAGE_ALIGN(image_size)>>PAGE_SHIFT;
477
478        if ( residual_fw_is_qemu( res ) ) {
479                /* save command-line - QEMU firmware sets R6/R7 to
480                 * commandline start/end (NON-PReP STD)
481                 */
482                int len = bd->r7 - bd->r6;
483                if ( len > 0 ) {
484                        if ( len > sizeof(bd->cmd_line) - 1 )
485                                len = sizeof(bd->cmd_line) - 1;
486                        codemove(bd->cmd_line, bd->r6, len, bd->cache_lsize);
487                        bd->cmd_line[len] = 0;
488                }
489        }
490
491        /* Fix residual if we are loaded by Motorola NT firmware */
492        if ( res && res->VitalProductData.FirmwareSupplier == 0x10000 )
493            fix_residual( res );
494
495        /* FIXME: if OF we should do something different */
496        if( !bd->of_entry && res &&
497           res->ResidualLength <= sizeof(RESIDUAL) && res->Version == 0 ) {
498                u_long lowpage=ULONG_MAX, highpage;
499                u_long imghigh=0, stkhigh=0;
500                /* Find the highest and large enough contiguous zone
501                   consisting of free and BootImage sections. */
502                /* Find 3 free areas of memory, one for the main image, one
503                 * for the stack (STACK_PAGES), and page one to put the map
504                 * structures. They are allocated from the top of memory.
505                 * In most cases the stack will be put just below the image.
506                 */
507                while((highpage =
508                       find_next_zone(res, lowpage, BootImage|Free))) {
509                        lowpage=find_zone_start(res, highpage, BootImage|Free);
510                        if ((highpage-lowpage)>minpages &&
511                            highpage>imghigh) {
512                                imghigh=highpage;
513                                highpage -=minpages;
514                        }
515                        if ((highpage-lowpage)>STACK_PAGES &&
516                            highpage>stkhigh) {
517                                stkhigh=highpage;
518                                highpage-=STACK_PAGES;
519                        }
520                }
521
522                bd->image = (void *)((imghigh-minpages)<<PAGE_SHIFT);
523                bd->stack=(void *) (stkhigh<<PAGE_SHIFT);
524
525                /* The code mover is put at the lowest possible place
526                 * of free memory. If this corresponds to the loaded boot
527                 * partition image it does not matter because it overrides
528                 * the unused part of it (x86 code).
529                 */
530                bd->mover=(void *) (lowpage<<PAGE_SHIFT);
531
532                /* Let us flush the caches in all cases. After all it should
533                 * not harm even on 601 and we don't care about performance.
534                 * Right now it's easy since all processors have a line size
535                 * of 32 bytes. Once again residual data has proved unreliable.
536                 */
537                bd->cache_lsize = 32;
538        }
539        /* For now we always assume that it's succesful, we should
540         * handle better the case of insufficient memory.
541         */
542        return 0;
543}
544
545void * valloc(u_long size) {
546        map *p, *q;
547        struct _mm_private * mm = (struct _mm_private *) bd->mm_private;
548
549        if (size==0) return NULL;
550        size=PAGE_ALIGN(size)-1;
551        for (p=mm->virtavail; p; p=p->next) {
552                if (p->base+size <= p->end) break;
553        }
554        if(!p) return NULL;
555        q=alloc_map();
556        q->base=p->base;
557        q->end=q->base+size;
558        q->firstpte=MAP_USED_VIRT;
559        insert_map(&mm->virtused, q);
560        if (q->end==p->end) free_map(remove_map(&mm->virtavail, p));
561        else p->base += size+1;
562        return (void *)q->base;
563}
564
565static
566void vflush(map *virtmap) {
567        struct _mm_private * mm = (struct _mm_private *) bd->mm_private;
568        u_long i, limit=(mm->hashmask>>3)+8;
569        hash_entry volatile *p=(hash_entry *) mm->sdr1;
570
571        /* PTE handling is simple since the processor never update
572         * the entries. Writable pages always have the C bit set and
573         * all valid entries have the R bit set. From the processor
574         * point of view the hash table is read only.
575         */
576        for (i=0; i<limit; i++) {
577                if (p[i].key<0) {
578                        u_long va;
579                        va = ((i<<9)^((p[i].key)<<5)) &0x3ff000;
580                        if (p[i].key&0x40) va^=0x3ff000;
581                        va |= ((p[i].key<<21)&0xf0000000)
582                          | ((p[i].key<<22)&0x0fc00000);
583                        if (va>=virtmap->base && va<=virtmap->end) {
584                                p[i].key=0;
585                                asm volatile("sync; tlbie %0; sync" : :
586                                             "r" (va));
587                        }
588                }
589        }
590}
591
592void vfree(void *vaddr) {
593        map *physmap, *virtmap; /* Actual mappings pertaining to this vm */
594        struct _mm_private * mm = (struct _mm_private *) bd->mm_private;
595
596        /* Flush memory queues */
597        asm volatile("sync": : : "memory");
598
599        virtmap = remove_map_at(&mm->virtused, vaddr);
600        if (!virtmap) return;
601
602        /* Remove mappings corresponding to virtmap */
603        for (physmap=mm->mappings; physmap; ) {
604                map *nextmap=physmap->next;
605                if (physmap->base>=virtmap->base
606                    && physmap->base<virtmap->end) {
607                        free_map(remove_map(&mm->mappings, physmap));
608                }
609                physmap=nextmap;
610        }
611
612        vflush(virtmap);
613
614        virtmap->firstpte= MAP_FREE_VIRT;
615        insert_map(&mm->virtavail, virtmap);
616        coalesce_maps(mm->virtavail);
617}
618
619void vunmap(void *vaddr) {
620        map *physmap, *virtmap; /* Actual mappings pertaining to this vm */
621        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
622
623        /* Flush memory queues */
624        asm volatile("sync": : : "memory");
625
626        /* vaddr must be within one of the vm areas in use and
627         * then must correspond to one of the physical areas
628         */
629        for (virtmap=mm->virtused; virtmap; virtmap=virtmap->next) {
630                if (virtmap->base<=(u_long)vaddr &&
631                    virtmap->end>=(u_long)vaddr) break;
632        }
633        if (!virtmap) return;
634
635        physmap = remove_map_at(&mm->mappings, vaddr);
636        if(!physmap) return;
637        vflush(physmap);
638        free_map(physmap);
639}
640
641int vmap(void *vaddr, u_long p, u_long size) {
642        map *q;
643        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
644
645        size=PAGE_ALIGN(size);
646        if(!size) return 1;
647        /* Check that the requested area fits in one vm image */
648        for (q=mm->virtused; q; q=q->next) {
649                if ((q->base <= (u_long)vaddr) &&
650                    (q->end>=(u_long)vaddr+size -1)) break;
651        }
652        if (!q) return 1;
653        q= alloc_map();
654        if (!q) return 1;
655        q->base = (u_long)vaddr;
656        q->end = (u_long)vaddr+size-1;
657        q->firstpte = p;
658        return insert_map(&mm->mappings, q);
659}
660
661static
662void create_identity_mappings(int type, int attr) {
663        u_long lowpage=ULONG_MAX, highpage;
664        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
665        RESIDUAL * res=bd->residual;
666
667        while((highpage = find_next_zone(res, lowpage, type))) {
668                map *p;
669                lowpage=find_zone_start(res, highpage, type);
670                p=alloc_map();
671                /* Do not map page 0 to catch null pointers */
672                lowpage = lowpage ? lowpage : 1;
673                p->base=lowpage<<PAGE_SHIFT;
674                p->end=(highpage<<PAGE_SHIFT)-1;
675                p->firstpte = (lowpage<<PAGE_SHIFT)|attr;
676                insert_map(&mm->mappings, p);
677        }
678}
679
680static inline
681void add_free_map(u_long base, u_long end) {
682        map *q=NULL;
683        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
684
685        if (base<end) q=alloc_map();
686        if (!q) return;
687        q->base=base;
688        q->end=end-1;
689        q->firstpte=MAP_FREE_VIRT;
690        insert_map(&mm->virtavail, q);
691}
692
693static inline
694void create_free_vm(void) {
695        map *p;
696        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
697
698        u_long vaddr=PAGE_SIZE; /* Never map vaddr 0 */
699        for(p=mm->mappings; p; p=p->next) {
700                add_free_map(vaddr, p->base);
701                vaddr=p->end+1;
702        }
703        /* Special end of memory case */
704        if (vaddr) add_free_map(vaddr,0);
705}
706
707/* Memory management initialization.
708 * Set up the mapping lists.
709 */
710
711static inline
712void add_perm_map(u_long start, u_long size) {
713        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
714        map *p=alloc_map();
715        p->base = start;
716        p->end = start + size - 1;
717        p->firstpte = MAP_PERM_PHYS;
718        insert_map(& mm->physperm , p);
719}
720
721void mm_init(u_long image_size)
722{
723        u_long lowpage=ULONG_MAX, highpage;
724        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
725        RESIDUAL * res=bd->residual;
726        int i;
727        map *p;
728
729        /* The checks are simplified by the fact that the image
730         * and stack area are always allocated at the upper end
731         * of a free block.
732         */
733        while((highpage = find_next_zone(res, lowpage, BootImage|Free))) {
734                lowpage=find_zone_start(res, highpage, BootImage|Free);
735                if ( ( ((u_long)bd->image+PAGE_ALIGN(image_size))>>PAGE_SHIFT)
736                     == highpage) {
737                        highpage=(u_long)(bd->image)>>PAGE_SHIFT;
738                        add_perm_map((u_long)bd->image, image_size);
739                }
740                if ( (( u_long)bd->stack>>PAGE_SHIFT) == highpage) {
741                        highpage -= STACK_PAGES;
742                        add_perm_map(highpage<<PAGE_SHIFT,
743                                     STACK_PAGES*PAGE_SIZE);
744                }
745                /* Protect the interrupt handlers that we need ! */
746                if (lowpage<2) lowpage=2;
747                /* Check for the special case of full area! */
748                if (highpage>lowpage) {
749                        p = alloc_map();
750                        p->base = lowpage<<PAGE_SHIFT;
751                        p->end = (highpage<<PAGE_SHIFT)-1;
752                        p->firstpte=MAP_FREE_PHYS;
753                        insert_map(&mm->physavail, p);
754                }
755        }
756
757        /* Allocate the hash table */
758        mm->sdr1=__palloc(0x10000, PA_PERM|16);
759        _write_SDR1((u_long)mm->sdr1);
760        memset(mm->sdr1, 0, 0x10000);
761        mm->hashmask = 0xffc0;
762
763        /* Setup the segment registers as we want them */
764        for (i=0; i<16; i++) _write_SR(i, (void *)(i<<28));
765        /* Create the maps for the physical memory, firwmarecode does not
766         * seem to be necessary. ROM is mapped read-only to reduce the risk
767         * of reprogramming it because it's often Flash and some are
768         * amazingly easy to overwrite.
769         */
770        create_identity_mappings(BootImage|Free|FirmwareCode|FirmwareHeap|
771                                 FirmwareStack, PTE_RAM);
772        create_identity_mappings(SystemROM, PTE_ROM);
773        create_identity_mappings(IOMemory|SystemIO|SystemRegs|
774                                 PCIAddr|PCIConfig|ISAAddr, PTE_IO);
775
776        create_free_vm();
777
778        /* Install our own MMU and trap handlers. */
779        codemove((void *) 0x300, _handler_glue, 0x100, bd->cache_lsize);
780        codemove((void *) 0x400, _handler_glue, 0x100, bd->cache_lsize);
781        codemove((void *) 0x600, _handler_glue, 0x100, bd->cache_lsize);
782        codemove((void *) 0x700, _handler_glue, 0x100, bd->cache_lsize);
783}
784
785void * salloc(u_long size) {
786        map *p, *q;
787        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
788
789        if (size==0) return NULL;
790
791        size = (size+7)&~7;
792
793        for (p=mm->sallocfree; p; p=p->next) {
794                if (p->base+size <= p->end) break;
795        }
796        if(!p) {
797                void *m;
798                m = __palloc(size, PA_SUBALLOC);
799                p = alloc_map();
800                if (!m && !p) return NULL;
801                p->base = (u_long) m;
802                p->firstpte = MAP_FREE_SUBS;
803                p->end = (u_long)m+PAGE_ALIGN(size)-1;
804                insert_map(&mm->sallocfree, p);
805                coalesce_maps(mm->sallocfree);
806                coalesce_maps(mm->sallocphys);
807        };
808        q=alloc_map();
809        q->base=p->base;
810        q->end=q->base+size-1;
811        q->firstpte=MAP_USED_SUBS;
812        insert_map(&mm->sallocused, q);
813        if (q->end==p->end) free_map(remove_map(&mm->sallocfree, p));
814        else p->base += size;
815        memset((void *)q->base, 0, size);
816        return (void *)q->base;
817}
818
819void sfree(void *p) {
820        map *q;
821        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
822
823        q=remove_map_at(&mm->sallocused, p);
824        if (!q) return;
825        q->firstpte=MAP_FREE_SUBS;
826        insert_map(&mm->sallocfree, q);
827        coalesce_maps(mm->sallocfree);
828}
829
830/* first/last area fit, flags is a power of 2 indicating the required
831 * alignment. The algorithms are stupid because we expect very little
832 * fragmentation of the areas, if any. The unit of allocation is the page.
833 * The allocation is by default performed from higher addresses down,
834 * unless flags&PA_LOW is true.
835 */
836
837void * __palloc(u_long size, int flags)
838{
839        u_long mask = ((1<<(flags&PA_ALIGN_MASK))-1);
840        map *newmap, *frommap, *p, *splitmap=0;
841        map **queue;
842        u_long qflags;
843        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
844
845        /* Asking for a size which is not a multiple of the alignment
846           is likely to be an error. */
847
848        if (size & mask) return NULL;
849        size = PAGE_ALIGN(size);
850        if(!size) return NULL;
851
852        if (flags&PA_SUBALLOC) {
853                queue = &mm->sallocphys;
854                qflags = MAP_SUBS_PHYS;
855        } else if (flags&PA_PERM) {
856                queue = &mm->physperm;
857                qflags = MAP_PERM_PHYS;
858        } else {
859                queue = &mm->physused;
860                qflags = MAP_USED_PHYS;
861        }
862        /* We need to allocate that one now so no two allocations may attempt
863         * to take the same memory simultaneously. Alloc_map_page does
864         * not call back here to avoid infinite recursion in alloc_map.
865         */
866
867        if (mask&PAGE_MASK) {
868                splitmap=alloc_map();
869                if (!splitmap) return NULL;
870        }
871
872        for (p=mm->physavail, frommap=NULL; p; p=p->next) {
873                u_long high = p->end;
874                u_long limit  = ((p->base+mask)&~mask) + size-1;
875                if (high>=limit && ((p->base+mask)&~mask)+size>p->base) {
876                        frommap = p;
877                        if (flags&PA_LOW) break;
878                }
879        }
880
881        if (!frommap) {
882                if (splitmap) free_map(splitmap);
883                return NULL;
884        }
885
886        newmap=alloc_map();
887
888        if (flags&PA_LOW) {
889                newmap->base = (frommap->base+mask)&~mask;
890        } else {
891                newmap->base = (frommap->end +1 - size) & ~mask;
892        }
893
894        newmap->end = newmap->base+size-1;
895        newmap->firstpte = qflags;
896
897        /* Add a fragment if we don't allocate until the end. */
898
899        if (splitmap) {
900                splitmap->base=newmap->base+size;
901                splitmap->end=frommap->end;
902                splitmap->firstpte= MAP_FREE_PHYS;
903                frommap->end=newmap->base-1;
904        } else if (flags & PA_LOW) {
905                frommap->base=newmap->base+size;
906        } else {
907                frommap->end=newmap->base-1;
908        }
909
910        /* Remove a fragment if it becomes empty. */
911        if (frommap->base == frommap->end+1) {
912                free_map(remove_map(&mm->physavail, frommap));
913        }
914
915        if (splitmap) {
916                if (splitmap->base == splitmap->end+1) {
917                        free_map(remove_map(&mm->physavail, splitmap));
918                } else {
919                        insert_map(&mm->physavail, splitmap);
920                }
921        }
922
923        insert_map(queue, newmap);
924        return (void *) newmap->base;
925
926}
927
928void pfree(void * p) {
929        map *q;
930        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
931        q=remove_map_at(&mm->physused, p);
932        if (!q) return;
933        q->firstpte=MAP_FREE_PHYS;
934        insert_map(&mm->physavail, q);
935        coalesce_maps(mm->physavail);
936}
937
938#ifdef DEBUG
939/* Debugging functions */
940void print_maps(map *chain, const char *s) {
941        map *p;
942        printk("%s",s);
943        for(p=chain; p; p=p->next) {
944                printk("    %08lx-%08lx: %08lx\n",
945                       p->base, p->end, p->firstpte);
946        }
947}
948
949void print_all_maps(const char * s) {
950        u_long freemaps;
951        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
952        map *free;
953        printk("%s",s);
954        print_maps(mm->mappings, "  Currently defined mappings:\n");
955        print_maps(mm->physavail, "  Currently available physical areas:\n");
956        print_maps(mm->physused, "  Currently used physical areas:\n");
957        print_maps(mm->virtavail, "  Currently available virtual areas:\n");
958        print_maps(mm->virtused, "  Currently used virtual areas:\n");
959        print_maps(mm->physperm, "  Permanently used physical areas:\n");
960        print_maps(mm->sallocphys, "  Physical memory used for salloc:\n");
961        print_maps(mm->sallocfree, "  Memory available for salloc:\n");
962        print_maps(mm->sallocused, "  Memory allocated through salloc:\n");
963        for (freemaps=0, free=mm->freemaps; free; freemaps++, free=free->next);
964        printk("  %ld free maps.\n", freemaps);
965}
966
967void print_hash_table(void) {
968        struct _mm_private *mm = (struct _mm_private *) bd->mm_private;
969        hash_entry *p=(hash_entry *) mm->sdr1;
970        u_int i, valid=0;
971        for (i=0; i<((mm->hashmask)>>3)+8; i++) {
972                if (p[i].key<0) valid++;
973        }
974        printk("%u valid hash entries on pass 1.\n", valid);
975        valid = 0;
976        for (i=0; i<((mm->hashmask)>>3)+8; i++) {
977                if (p[i].key<0) valid++;
978        }
979        printk("%u valid hash entries on pass 2.\n"
980               "     vpn:rpn_attr, p/s, pteg.i\n", valid);
981        for (i=0; i<((mm->hashmask)>>3)+8; i++) {
982                if (p[i].key<0) {
983                        u_int pteg=(i>>3);
984                        u_long vpn;
985                        vpn = (pteg^((p[i].key)>>7)) &0x3ff;
986                        if (p[i].key&0x40) vpn^=0x3ff;
987                        vpn |= ((p[i].key<<9)&0xffff0000)
988                          | ((p[i].key<<10)&0xfc00);
989                        printk("%08lx:%08lx, %s, %5d.%d\n",
990                               vpn,  p[i].rpn, p[i].key&0x40 ? "sec" : "pri",
991                               pteg, i%8);
992                }
993        }
994}
995
996#endif
Note: See TracBrowser for help on using the repository browser.