diff options
Diffstat (limited to 'package/aboot/src/utils.c')
-rw-r--r-- | package/aboot/src/utils.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/package/aboot/src/utils.c b/package/aboot/src/utils.c new file mode 100644 index 000000000..e2f559c30 --- /dev/null +++ b/package/aboot/src/utils.c @@ -0,0 +1,292 @@ +#include <linux/kernel.h> + +#include "hwrpb.h" +#include <linux/version.h> +#include "system.h" + +#include "string.h" + +#include <stdarg.h> +#include <errno.h> + +#include "aboot.h" +#include "cons.h" + +unsigned long free_mem_ptr = 0; + + +int printf(const char *fmt, ...) +{ + static char buf[1024]; + va_list args; + long len, num_lf; + char *src, *dst; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + + /* count number of linefeeds in string: */ + + num_lf = 0; + for (src = buf; *src; ++src) { + if (*src == '\n') { + ++num_lf; + } + } + + if (num_lf) { + /* expand each linefeed into carriage-return/linefeed: */ + for (dst = src + num_lf; src >= buf; ) { + if (*src == '\n') { + *dst-- = '\r'; + } + *dst-- = *src--; + } + } + return cons_puts(buf, len + num_lf); +} + + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ +struct pcb_struct *find_pa(unsigned long *vptb, struct pcb_struct *pcb) +{ + unsigned long address = (unsigned long) pcb; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (struct pcb_struct *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + * + * As we don't want it there anyway, we also move the L1 self-map + * up as high as we can, so that the last entry in the L1 page table + * maps the page tables. + * + * As a result, the OSF/1 pal-code will instead use a virtual page table + * map located at 0xffffffe00000000. + */ +#define pcb_va ((struct pcb_struct *) 0x20000000) +#define old_vptb (0x0000000200000000UL) +#define new_vptb (0xfffffffe00000000UL) +void pal_init(void) +{ + unsigned long i, rev, sum; + unsigned long *L1, *l; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Find the level 1 page table and duplicate it in high memory */ + L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ + L1[1023] = L1[1]; + + percpu = (struct percpu_struct *) (INIT_HWRPB->processor_offset + + (unsigned long) INIT_HWRPB), + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va); + printf("aboot: switching to OSF/1 PALcode"); + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the virtual addr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but we give it 0, asm sets it) + */ + i = switch_to_osf_pal( + 2, + pcb_va, + pcb_pa, + new_vptb, + 0); + if (i) { + printf("---failed, code %ld\n", i); + halt(); + } + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + INIT_HWRPB->vptb = new_vptb; + + /* update checksum: */ + sum = 0; + for (l = (unsigned long *) INIT_HWRPB; l < (unsigned long *) &INIT_HWRPB->chksum; ++l) + sum += *l; + INIT_HWRPB->chksum = sum; + + printf(" version %ld.%ld\n", (rev >> 8) & 0xff, rev & 0xff); + /* remove the old virtual page-table mapping */ + L1[1] = 0; + tbia(); +} + +int check_memory(unsigned long start, unsigned long size) +{ + unsigned long phys_start, start_pfn, end_pfn; + struct memclust_struct *cluster; + struct memdesc_struct *memdesc; + int i; + + /* + * Get the physical address start. + * If 43-bit superpage is being used (VA<63:41> = 0x7ffffe) + * then the "correct" translation across all implementations is to + * sign extend the VA from bit 40. Othewise, assume it's already a + * physical address. + */ + phys_start = start; + if (((long)phys_start >> 41) == -2) + phys_start = (long)((start) << (64-41)) >> (64-41); + start_pfn = phys_start >> PAGE_SHIFT; + end_pfn = (phys_start + size - 1) >> PAGE_SHIFT; + + memdesc = (struct memdesc_struct *) + (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + for (cluster = memdesc->cluster, i = 0; + i < memdesc->numclusters; + i++, cluster++) { + if ((cluster->start_pfn > end_pfn) || + ((cluster->start_pfn + cluster->numpages) <= start_pfn)) + continue; /* no overlap */ + + /* + * This cluster overlaps the memory we're checking, check + * the usage: + * bit 0 is console/PAL reserved + * bit 1 is non-volatile + * If either is set, it's a problem, return -EBUSY + */ + if (cluster->usage & 3) + return -EBUSY; /* reserved */ + + /* + * It's not reserved, take it out of what we're checking + */ + if (cluster->start_pfn <= end_pfn) + end_pfn = cluster->start_pfn - 1; + if ((cluster->start_pfn + cluster->numpages) > start_pfn) + start_pfn = cluster->start_pfn + cluster->numpages; + + if (end_pfn < start_pfn) + return 0; /* all found, ok */ + } + + /* no conflict, but not all memory found */ + return -ENOMEM; +} + +unsigned long memory_end(void) +{ + int i; + unsigned long high = 0; + struct memclust_struct *cluster; + struct memdesc_struct *memdesc; + + memdesc = (struct memdesc_struct *) + (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + cluster = memdesc->cluster; + for (i = memdesc->numclusters; i > 0; i--, cluster++) { + unsigned long tmp; + + if (cluster->usage != 0) { + /* this is a PAL or NVRAM cluster (not for the OS) */ + continue; + } + + tmp = (cluster->start_pfn + cluster->numpages) << page_shift; + if (tmp > high) { + high = tmp; + } + } + return page_offset + high; +} + + +static void error(char *x) +{ + printf("%s\n", x); + _longjmp(jump_buffer, 1); +} + + +void unzip_error(char *x) +{ + printf("\nunzip: "); + error(x); +} + + +void *malloc(size_t size) +{ + if (!free_mem_ptr) { + free_mem_ptr = memory_end(); + } + + free_mem_ptr = (free_mem_ptr - size) & ~(sizeof(long) - 1); + if ((char*) free_mem_ptr <= dest_addr + INIT_HWRPB->pagesize) { + error("\nout of memory"); + } + return (void*) free_mem_ptr; +} + + +void free(void *where) +{ + /* don't care */ +} + + +void +getline (char *buf, int maxlen) +{ + int len=0; + char c; + + do { + c = cons_getchar(); + switch (c) { + case 0: + case 10: + case 13: + break; + case 8: + case 127: + if (len > 0) { + --len; + cons_putchar(8); + cons_putchar(' '); + cons_putchar(8); + } + break; + + default: + if (len < maxlen-1 && c >= ' ') { + buf[len] = c; + len++; + cons_putchar(c); + } + break; + } + } while (c != 13 && c != 10); + buf[len] = 0; +} |