summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbcollins <tailor@grayson>2006-06-01 13:19:10 -0400
committerBen Collins <bcollins@ubuntu.com>2006-06-01 13:19:10 -0400
commit6cc6daa8a3895d7c6b3bcc03948f4a8fea077c35 (patch)
tree28e46cc211152474a695a493ca4e550f526260b1
parent59e98dab99308872af81b04efa690abbf198a08a (diff)
downloadsilo-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.c88
-rw-r--r--second/memory.c243
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, &params)) {
@@ -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();