diff options
author | bcollins <tailor@grayson> | 2006-06-01 13:19:10 -0400 |
---|---|---|
committer | Ben Collins <bcollins@ubuntu.com> | 2006-06-01 13:19:10 -0400 |
commit | 6cc6daa8a3895d7c6b3bcc03948f4a8fea077c35 (patch) | |
tree | 28e46cc211152474a695a493ca4e550f526260b1 | |
parent | 59e98dab99308872af81b04efa690abbf198a08a (diff) | |
download | silo-6cc6daa8a3895d7c6b3bcc03948f4a8fea077c35.tar.gz |
[silo @ 111]
Support for booting kernels on sparc64 to alternate memory locations (IOW,
not phys_base). This also gets us the bonus of loading kernels as large as
8Megs (well, 0x7fc000).#
-rw-r--r-- | second/main.c | 88 | ||||
-rw-r--r-- | second/memory.c | 243 |
2 files changed, 267 insertions, 64 deletions
diff --git a/second/main.c b/second/main.c index defe0f9..b90867a 100644 --- a/second/main.c +++ b/second/main.c @@ -1107,7 +1107,7 @@ int bootmain (void) "Type [prompath;]part/path_to_image [parameters] on the prompt\n" "E.g. /iommu/sbus/espdma/esp/sd@3,0;4/vmlinux root=/dev/sda4\n" "or 2/vmlinux.live (to load vmlinux.live from 2nd partition of boot disk)\n"); - +try_again: isfile = 0; /* RC = 0 invalid file or not an executable */ while (!isfile) { switch (get_params (&device, &part, &kname, &proll, ¶ms)) { @@ -1160,11 +1160,36 @@ int bootmain (void) ret_offset = 0x4000; } else { - image_base = (unsigned char *) 0x4000; - if (!load_file (device, part, kname, image_base, - (unsigned char *) &_start, &image_len, - load_cmd == CMD_LS ? LOADFILE_LS : LOADFILE_GZIP, 0)) { + char *image_end = (char *)&_start; + + image_base = (char *)0x4000; + + /* See if we can use some extra memory for the kernel */ + if (!load_cmd) { + unsigned int size; + char *mem; + + size = 0x800000; + mem = image_memory_find(size); + + if (!mem) { + size = 0x400000; + mem = image_memory_find(size); + } + + if (mem) { + image_base = mem; + image_end = image_base + size - 0x4000; + } + } + + if (!load_file (device, part, kname, image_base, image_end, + &image_len, load_cmd == CMD_LS ? LOADFILE_LS : LOADFILE_GZIP, 0)) { printf ("\nImage not found.... try again\n"); + + if (!load_cmd) + image_memory_release(); + continue; } @@ -1179,8 +1204,8 @@ int bootmain (void) continue; } - isfile = parse_executable (image_base, image_len, &off, &len, - &ret_offset, kname); + isfile = parse_executable (image_base, image_len, &off, &len, + &ret_offset, kname); } } @@ -1190,9 +1215,37 @@ int bootmain (void) params_device = sol_params; } else if (!other) { params_device = 0; - memcpy (image_base, image_base + off, len); + + memcpy (image_base, image_base + off, len); + p = find_linux_HdrS (image_base, image_len); + if (p && *(unsigned short *)(p + 8) < 0x300 && image_base != (char *)0x4000) { + /* Kernel doesn't support being loaded to other than + * phys_base, so let's try to copy it down there. */ + if ((unsigned int)&_start - 0x4000 < len) { + /* Fuck, can't do that */ + printf("Your kernel cannot fit into the memory destination. This\n" + "can be resolved by recompiling the kernel with more devices\n" + "built as modules, or upgrading your kernel to one that\n" + "supports being loaded to higher memory areas (currently\n" + "2.6.3+ or 2.4.26+).\n"); + goto try_again; + } + + printf("Kernel doesn't support loading to high memory, relocating..."); + + /* Ok, it fits, so copy it down there */ + memcpy ((char *)0x4000, image_base, len); + image_base = (char *)0x4000; + + /* Readjust some things */ + ret_offset = 0x4000; + p = find_linux_HdrS (image_base, image_len); + + printf("done.\n"); + } + if (p) { unsigned int linux_version = *(unsigned int *)(p + 4); @@ -1235,8 +1288,11 @@ int bootmain (void) params = s2; } } - if (*(unsigned short *)(p + 8) >= 0x202) - kernel_params = (char *)(*(unsigned int *)(p + 36)&0x3fffff); + + if (*(unsigned short *)(p + 8) >= 0x202) { + kernel_params = (char *)((*(unsigned int *)(p + 36) - 0x400000) + + (image_base - 0x4000)); + } /* Some UltraAX machines have /dev/fd1 floppies only. */ if (floppyswap) { @@ -1245,6 +1301,7 @@ int bootmain (void) for (s1 = params; (s1 = strstr(s1, "root=/dev/fd0")) != NULL; s1 += 13) s1[12] = '1'; } + if (initrd_string) { char *q, *r, *initrd_device, *initrd_kname, *initrd_limit, *initrd_cur, c; char *string; @@ -1295,10 +1352,10 @@ int bootmain (void) printf("Loaded initial ramdisk (%d bytes at 0x%x)...\n", (unsigned int)initrd_cur - (unsigned int)initrd_start, initrd_start); if (statusok) { - extern unsigned long sun4u_initrd_pa; + extern unsigned long long sun4u_initrd_phys; extern unsigned long sun4m_initrd_pa; if (architecture == sun4u) - *(unsigned int *)(p + 16) = ((unsigned int)sun4u_initrd_pa + 0x400000); + *(unsigned int *)(p + 16) = (unsigned int)sun4u_initrd_phys; else if (sun4m_initrd_pa) *(unsigned int *)(p + 16) = ((unsigned int)sun4m_initrd_pa); else @@ -1311,10 +1368,10 @@ int bootmain (void) if (initrd_kname) { if (!initrd_device) initrd_device = initrd_defdevice; if (load_file (initrd_device, initrd_partno, initrd_kname, (unsigned char *) 0x300000, (unsigned char *) LARGE_RELOC, 0, 0, initrd_lenfunc)) { - extern unsigned long sun4u_initrd_pa; + extern unsigned long long sun4u_initrd_phys; extern unsigned long sun4m_initrd_pa; if (architecture == sun4u) - *(unsigned int *)(p + 16) = ((unsigned int)sun4u_initrd_pa + 0x400000); + *(unsigned int *)(p + 16) = (unsigned int)sun4u_initrd_phys; else if (sun4m_initrd_pa) *(unsigned int *)(p + 16) = ((unsigned int)sun4m_initrd_pa); else @@ -1363,12 +1420,15 @@ int bootmain (void) prom_getchar (); printf ("\n"); } + memory_release(); + if (other && reboot) { strcpy (sol_params, params_device); strcat (sol_params, " "); strcat (sol_params, params); prom_reboot(sol_params); } + return ret_offset; } diff --git a/second/memory.c b/second/memory.c index 74bb324..291b97d 100644 --- a/second/memory.c +++ b/second/memory.c @@ -21,6 +21,14 @@ #include <silo.h> +#define IMAGE_TLB_ENTRY 63 +#define INITRD_TLB_ENTRY 62 + +#define INITRD_VIRT_ADDR 0x50000000 +#define IMAGE_VIRT_ADDR 0x40000000 + +static char *sun4u_memory_find (unsigned int len, int is_kernel); + struct linux_prom_registers prom_reg_memlist[64]; struct linux_mlist_v0 prom_phys_avail[64]; @@ -191,19 +199,23 @@ inline void sun4m_set_direct (unsigned long l, unsigned long set) #ifndef TLB_TAG_ACCESS #define TLB_TAG_ACCESS 0x30 #endif + #ifndef ASI_DMMU #define ASI_DMMU 0x58 #define ASI_DTLB_DATA_ACCESS 0x5d #endif -unsigned long sun4u_initrd_pa; +#ifndef ASI_IMMU +#define ASI_IMMU 0x50 +#define ASI_ITLB_DATA_ACCESS 0x55 +#endif + unsigned long sun4m_initrd_pa; unsigned long sun4m_initrd_va; char *memory_find (int len) { register struct linux_mlist_v0 *mlist; - unsigned long long sun4u_memory_base; char *beg = 0, *start; int l = 0, num; unsigned long totalmem = 0; @@ -265,44 +277,99 @@ char *memory_find (int len) mlist = mlist->theres_more; } } else { - int n, node, i; - struct p1275_mem { unsigned long long pa; unsigned long long len; } *p; - unsigned long long phys_base, base, b; + return sun4u_memory_find((len + 0x1fff) & ~0x2000, 0); + } +not_found: + return (char *)0; +} + +static unsigned long long sun4u_image_virt, sun4u_image_len, sun4u_image_phys; +static unsigned long long sun4u_initrd_virt, sun4u_initrd_len; +unsigned long long sun4u_initrd_phys; + +/* This might look all weird, but we use the claim/release methods to + * avoid having to traverse the physical memory ourselves and track what + * we use. We let OBP do that for us. */ +static char *sun4u_memory_find (unsigned int len, int is_kernel) +{ + int n, node, i; + struct p1275_mem { + unsigned long long phys; + unsigned long long size; + } *p = (struct p1275_mem *)0; + unsigned int virt = (is_kernel ? IMAGE_VIRT_ADDR : INITRD_VIRT_ADDR); + unsigned int tlb_entry = (is_kernel ? IMAGE_TLB_ENTRY : INITRD_TLB_ENTRY); + unsigned long long phys = 0, phys_base; p = (struct p1275_mem *)malloc(2048); - node = prom_finddevice("/memory"); - if (prom_getproperty(node, "reg", (char *)p, 2048) == -1) { - free (p); - printf("Could not get reg property\n"); - return (char *)0; - } - phys_base = p[0].pa; - n = prom_getproplen(node, "available"); - if (!n || n == -1 || prom_getproperty(node, "available", (char *)p, 2048) == -1) { - free (p); - printf("Could not get available property\n"); - return (char *)0; - } - base = 0; - b = phys_base + 0x100000000ULL - 0x400000ULL; + + node = prom_finddevice("/memory"); + + if (prom_getproperty(node, "reg", (char *)p, 2048) == -1) { + free (p); + printf("Could not get reg property\n"); + return (char *)0; + } + + phys_base = p[0].phys; + + n = prom_getproplen(node, "available"); + + if (!n || n == -1 || prom_getproperty(node, "available", (char *)p, 2048) == -1) { + free (p); + printf("Could not get available property\n"); + return (char *)0; + } + + phys = 0; n /= sizeof(*p); - len += 8192; - for (i = 0; i < n; i++) { - /* if we've got more ram than god, pretend it's 32M */ - if (p[i].len >= 0x100000000ULL) - p[i].len = 0x020000000ULL; - if (p[i].pa + p[i].len <= b && - p[i].pa >= base && - p[i].len >= len) - base = (p[i].pa + p[i].len - len + 7) & ~7; - } - free (p); - if (base >= b || base < phys_base + 0x400000ULL) { - printf("Could not find any available memory for initial ramdisk\n"); - return (char *)0; - } - sun4u_memory_base = base & ~0x3fffffULL; - sun4u_initrd_pa = base - phys_base; + + for (i = 0; i < n; i++) { + /* Do not mess with first 4 Megs of memory */ + if (p[i].phys == phys_base) { + if (p[i].size <= 0x400000) + continue; + p[i].phys += 0x400000; + p[i].size -= 0x400000; + } + + /* Make sure initrd doesn't overwrite kernel */ + if (!is_kernel && p[i].phys == sun4u_image_phys) { + if (p[i].size <= sun4u_image_len) + continue; + p[i].phys += sun4u_image_len; + p[i].size -= sun4u_image_len; + } + + if (p[i].size >= len) { + phys = p[i].phys; + break; + } + } + + free (p); + + if (!phys) { + printf("Could not find any available memory\n"); + return (char *)0; + } + + if (prom_map(PROM_MAP_DEFAULT, (unsigned long long)len, virt, phys) == -1) { + printf("Could not map memory\n"); + return (char *)0; + } + + if (is_kernel) { + sun4u_image_len = len; + sun4u_image_virt = virt; + sun4u_image_phys = phys; + phys += 0x4000ULL; + virt += 0x4000; + } else { + sun4u_initrd_len = len; + sun4u_initrd_virt = virt; + } + __asm __volatile("\n\ sethi %%hi(0xe0000000), %%g1\n\ ldx [%3], %%g2\n\ @@ -317,29 +384,105 @@ char *memory_find (int len) flush %0\n\ membar #Sync\n\ wrpr %%g1, %%pil\n\ - " : : "r" (0x40000000), "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU), - "r" (&sun4u_memory_base), "r" (63 << 3), "i" (ASI_DTLB_DATA_ACCESS) : "g1", "g2"); - return (char *)0x40000000 + ((long)base & 0x3fffffUL); - } -not_found: - return (char *)0; + " : : "r" (virt), "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU), + "r" (&phys), "r" (tlb_entry << 3), + "i" (ASI_DTLB_DATA_ACCESS) : "g1", "g2"); + + if (is_kernel) { + __asm __volatile("\n\ + sethi %%hi(0xe0000000), %%g1\n\ + ldx [%3], %%g2\n\ + sllx %%g1, 32, %%g1\n\ + or %%g2, 0x77, %%g2\n\ + or %%g2, %%g1, %%g2\n\ + rdpr %%pil, %%g1\n\ + wrpr 15, %%pil\n\ + stxa %0, [%1] %2\n\ + stxa %%g2, [%4] %5\n\ + membar #Sync\n\ + flush %0\n\ + membar #Sync\n\ + wrpr %%g1, %%pil\n\ + " : : "r" (virt), "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), + "r" (&phys), "r" (tlb_entry << 3), + "i" (ASI_ITLB_DATA_ACCESS) : "g1", "g2"); + } + + return (char *)virt; } -void memory_release(void) +static void sun4u_memory_release(int is_kernel) { - if (architecture == sun4u) { + unsigned long long virt, len; + unsigned int tlb_entry = (is_kernel ? IMAGE_TLB_ENTRY : INITRD_TLB_ENTRY); + + if (is_kernel) { + virt = sun4u_image_virt; + len = sun4u_image_len; + } else { + virt = sun4u_initrd_virt; + len = sun4u_initrd_len; + } + + if (!len) + return; + + + prom_unmap(len, virt); + __asm __volatile("\n\ rdpr %%pil, %%g1\n\ - wrpr 16, %%pil\n\ + wrpr 15, %%pil\n\ stxa %%g0, [%0] %1\n\ - stxa %%g0, [%2] %3\n\ membar #Sync\n\ - flush %4\n\ + stxa %%g0, [%2] %3\n\ membar #Sync\n\ wrpr %%g1, %%pil\n\ " : : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU), - "r" (63 << 3), "i" (ASI_DTLB_DATA_ACCESS), - "r" (memory_release) : "g1"); + "r" (tlb_entry << 3), + "i" (ASI_DTLB_DATA_ACCESS) : "g1"); + + if (is_kernel) { + __asm __volatile("\n\ + rdpr %%pil, %%g1\n\ + wrpr 15, %%pil\n\ + stxa %%g0, [%0] %1\n\ + membar #Sync\n\ + stxa %%g0, [%2] %3\n\ + membar #Sync\n\ + wrpr %%g1, %%pil\n\ + " : : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), + "r" (tlb_entry << 3), + "i" (ASI_ITLB_DATA_ACCESS) : "g1"); + } + + if (is_kernel) + sun4u_image_len = 0; + else + sun4u_initrd_len = 0; +} + +char *image_memory_find (unsigned int len) +{ + /* This only works for sparc64 */ + if (architecture != sun4u) + return (char *)0; + + return sun4u_memory_find(len, 1); +} + +void image_memory_release(void) +{ + if (architecture != sun4u) + return; + + sun4u_memory_release(1); +} + +void memory_release(void) +{ + if (architecture == sun4u) { + sun4u_memory_release(0); } else if (sun4m_initrd_pa) { unsigned long lev1; lev1 = sun4m_get_lev1(); |