1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * arch/alpha/boot/bootp.c 4 * 5 * Copyright (C) 1997 Jay Estabrook 6 * 7 * This file is used for creating a bootp file for the Linux/AXP kernel 8 * 9 * based significantly on the arch/alpha/boot/main.c of Linus Torvalds 10 */ 11 #include <linux/kernel.h> 12 #include <linux/slab.h> 13 #include <linux/string.h> 14 #include <generated/utsrelease.h> 15 #include <linux/mm.h> 16 17 #include <asm/console.h> 18 #include <asm/hwrpb.h> 19 #include <asm/pgtable.h> 20 #include <asm/io.h> 21 22 #include <stdarg.h> 23 24 #include "ksize.h" 25 26 extern unsigned long switch_to_osf_pal(unsigned long nr, 27 struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, 28 unsigned long *vptb); 29 30 extern void move_stack(unsigned long new_stack); 31 32 struct hwrpb_struct *hwrpb = INIT_HWRPB; 33 static struct pcb_struct pcb_va[1]; 34 35 /* 36 * Find a physical address of a virtual object.. 37 * 38 * This is easy using the virtual page table address. 39 */ 40 41 static inline void * 42 find_pa(unsigned long *vptb, void *ptr) 43 { 44 unsigned long address = (unsigned long) ptr; 45 unsigned long result; 46 47 result = vptb[address >> 13]; 48 result >>= 32; 49 result <<= 13; 50 result |= address & 0x1fff; 51 return (void *) result; 52 } 53 54 /* 55 * This function moves into OSF/1 pal-code, and has a temporary 56 * PCB for that. The kernel proper should replace this PCB with 57 * the real one as soon as possible. 58 * 59 * The page table muckery in here depends on the fact that the boot 60 * code has the L1 page table identity-map itself in the second PTE 61 * in the L1 page table. Thus the L1-page is virtually addressable 62 * itself (through three levels) at virtual address 0x200802000. 63 */ 64 65 #define VPTB ((unsigned long *) 0x200000000) 66 #define L1 ((unsigned long *) 0x200802000) 67 68 void 69 pal_init(void) 70 { 71 unsigned long i, rev; 72 struct percpu_struct * percpu; 73 struct pcb_struct * pcb_pa; 74 75 /* Create the dummy PCB. */ 76 pcb_va->ksp = 0; 77 pcb_va->usp = 0; 78 pcb_va->ptbr = L1[1] >> 32; 79 pcb_va->asn = 0; 80 pcb_va->pcc = 0; 81 pcb_va->unique = 0; 82 pcb_va->flags = 1; 83 pcb_va->res1 = 0; 84 pcb_va->res2 = 0; 85 pcb_pa = find_pa(VPTB, pcb_va); 86 87 /* 88 * a0 = 2 (OSF) 89 * a1 = return address, but we give the asm the vaddr of the PCB 90 * a2 = physical addr of PCB 91 * a3 = new virtual page table pointer 92 * a4 = KSP (but the asm sets it) 93 */ 94 srm_printk("Switching to OSF PAL-code .. "); 95 96 i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); 97 if (i) { 98 srm_printk("failed, code %ld\n", i); 99 __halt(); 100 } 101 102 percpu = (struct percpu_struct *) 103 (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); 104 rev = percpu->pal_revision = percpu->palcode_avail[2]; 105 106 srm_printk("Ok (rev %lx)\n", rev); 107 108 tbia(); /* do it directly in case we are SMP */ 109 } 110 111 static inline void 112 load(unsigned long dst, unsigned long src, unsigned long count) 113 { 114 memcpy((void *)dst, (void *)src, count); 115 } 116 117 /* 118 * Start the kernel. 119 */ 120 static inline void 121 runkernel(void) 122 { 123 __asm__ __volatile__( 124 "bis %0,%0,$27\n\t" 125 "jmp ($27)" 126 : /* no outputs: it doesn't even return */ 127 : "r" (START_ADDR)); 128 } 129 130 extern char _end; 131 #define KERNEL_ORIGIN \ 132 ((((unsigned long)&_end) + 511) & ~511) 133 134 void 135 start_kernel(void) 136 { 137 /* 138 * Note that this crufty stuff with static and envval 139 * and envbuf is because: 140 * 141 * 1. Frequently, the stack is short, and we don't want to overrun; 142 * 2. Frequently the stack is where we are going to copy the kernel to; 143 * 3. A certain SRM console required the GET_ENV output to stack. 144 * ??? A comment in the aboot sources indicates that the GET_ENV 145 * destination must be quadword aligned. Might this explain the 146 * behaviour, rather than requiring output to the stack, which 147 * seems rather far-fetched. 148 */ 149 static long nbytes; 150 static char envval[256] __attribute__((aligned(8))); 151 static unsigned long initrd_start; 152 153 srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); 154 if (INIT_HWRPB->pagesize != 8192) { 155 srm_printk("Expected 8kB pages, got %ldkB\n", 156 INIT_HWRPB->pagesize >> 10); 157 return; 158 } 159 if (INIT_HWRPB->vptb != (unsigned long) VPTB) { 160 srm_printk("Expected vptb at %p, got %p\n", 161 VPTB, (void *)INIT_HWRPB->vptb); 162 return; 163 } 164 pal_init(); 165 166 /* The initrd must be page-aligned. See below for the 167 cause of the magic number 5. */ 168 initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) | 169 (PAGE_SIZE-1)) + 1; 170 #ifdef INITRD_IMAGE_SIZE 171 srm_printk("Initrd positioned at %#lx\n", initrd_start); 172 #endif 173 174 /* 175 * Move the stack to a safe place to ensure it won't be 176 * overwritten by kernel image. 177 */ 178 move_stack(initrd_start - PAGE_SIZE); 179 180 nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); 181 if (nbytes < 0 || nbytes >= sizeof(envval)) { 182 nbytes = 0; 183 } 184 envval[nbytes] = '\0'; 185 srm_printk("Loading the kernel...'%s'\n", envval); 186 187 /* NOTE: *no* callbacks or printouts from here on out!!! */ 188 189 /* This is a hack, as some consoles seem to get virtual 20000000 (ie 190 * where the SRM console puts the kernel bootp image) memory 191 * overlapping physical memory where the kernel wants to be put, 192 * which causes real problems when attempting to copy the former to 193 * the latter... :-( 194 * 195 * So, we first move the kernel virtual-to-physical way above where 196 * we physically want the kernel to end up, then copy it from there 197 * to its final resting place... ;-} 198 * 199 * Sigh... */ 200 201 #ifdef INITRD_IMAGE_SIZE 202 load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE); 203 #endif 204 load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); 205 load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); 206 207 memset((char*)ZERO_PGE, 0, PAGE_SIZE); 208 strcpy((char*)ZERO_PGE, envval); 209 #ifdef INITRD_IMAGE_SIZE 210 ((long *)(ZERO_PGE+256))[0] = initrd_start; 211 ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; 212 #endif 213 214 runkernel(); 215 } 216