162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/alpha/boot/bootp.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1997 Jay Estabrook 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is used for creating a bootp file for the Linux/AXP kernel 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * based significantly on the arch/alpha/boot/main.c of Linus Torvalds 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <generated/utsrelease.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/console.h> 1862306a36Sopenharmony_ci#include <asm/hwrpb.h> 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/stdarg.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "ksize.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciextern unsigned long switch_to_osf_pal(unsigned long nr, 2662306a36Sopenharmony_ci struct pcb_struct *pcb_va, struct pcb_struct *pcb_pa, 2762306a36Sopenharmony_ci unsigned long *vptb); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciextern void move_stack(unsigned long new_stack); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct hwrpb_struct *hwrpb = INIT_HWRPB; 3262306a36Sopenharmony_cistatic struct pcb_struct pcb_va[1]; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Find a physical address of a virtual object.. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * This is easy using the virtual page table address. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic inline void * 4162306a36Sopenharmony_cifind_pa(unsigned long *vptb, void *ptr) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned long address = (unsigned long) ptr; 4462306a36Sopenharmony_ci unsigned long result; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci result = vptb[address >> 13]; 4762306a36Sopenharmony_ci result >>= 32; 4862306a36Sopenharmony_ci result <<= 13; 4962306a36Sopenharmony_ci result |= address & 0x1fff; 5062306a36Sopenharmony_ci return (void *) result; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * This function moves into OSF/1 pal-code, and has a temporary 5562306a36Sopenharmony_ci * PCB for that. The kernel proper should replace this PCB with 5662306a36Sopenharmony_ci * the real one as soon as possible. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * The page table muckery in here depends on the fact that the boot 5962306a36Sopenharmony_ci * code has the L1 page table identity-map itself in the second PTE 6062306a36Sopenharmony_ci * in the L1 page table. Thus the L1-page is virtually addressable 6162306a36Sopenharmony_ci * itself (through three levels) at virtual address 0x200802000. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define VPTB ((unsigned long *) 0x200000000) 6562306a36Sopenharmony_ci#define L1 ((unsigned long *) 0x200802000) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid 6862306a36Sopenharmony_cipal_init(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci unsigned long i, rev; 7162306a36Sopenharmony_ci struct percpu_struct * percpu; 7262306a36Sopenharmony_ci struct pcb_struct * pcb_pa; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Create the dummy PCB. */ 7562306a36Sopenharmony_ci pcb_va->ksp = 0; 7662306a36Sopenharmony_ci pcb_va->usp = 0; 7762306a36Sopenharmony_ci pcb_va->ptbr = L1[1] >> 32; 7862306a36Sopenharmony_ci pcb_va->asn = 0; 7962306a36Sopenharmony_ci pcb_va->pcc = 0; 8062306a36Sopenharmony_ci pcb_va->unique = 0; 8162306a36Sopenharmony_ci pcb_va->flags = 1; 8262306a36Sopenharmony_ci pcb_va->res1 = 0; 8362306a36Sopenharmony_ci pcb_va->res2 = 0; 8462306a36Sopenharmony_ci pcb_pa = find_pa(VPTB, pcb_va); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * a0 = 2 (OSF) 8862306a36Sopenharmony_ci * a1 = return address, but we give the asm the vaddr of the PCB 8962306a36Sopenharmony_ci * a2 = physical addr of PCB 9062306a36Sopenharmony_ci * a3 = new virtual page table pointer 9162306a36Sopenharmony_ci * a4 = KSP (but the asm sets it) 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci srm_printk("Switching to OSF PAL-code .. "); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); 9662306a36Sopenharmony_ci if (i) { 9762306a36Sopenharmony_ci srm_printk("failed, code %ld\n", i); 9862306a36Sopenharmony_ci __halt(); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci percpu = (struct percpu_struct *) 10262306a36Sopenharmony_ci (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); 10362306a36Sopenharmony_ci rev = percpu->pal_revision = percpu->palcode_avail[2]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci srm_printk("Ok (rev %lx)\n", rev); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci tbia(); /* do it directly in case we are SMP */ 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic inline void 11162306a36Sopenharmony_ciload(unsigned long dst, unsigned long src, unsigned long count) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci memcpy((void *)dst, (void *)src, count); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Start the kernel. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic inline void 12062306a36Sopenharmony_cirunkernel(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci __asm__ __volatile__( 12362306a36Sopenharmony_ci "bis %0,%0,$27\n\t" 12462306a36Sopenharmony_ci "jmp ($27)" 12562306a36Sopenharmony_ci : /* no outputs: it doesn't even return */ 12662306a36Sopenharmony_ci : "r" (START_ADDR)); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciextern char _end; 13062306a36Sopenharmony_ci#define KERNEL_ORIGIN \ 13162306a36Sopenharmony_ci ((((unsigned long)&_end) + 511) & ~511) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_civoid 13462306a36Sopenharmony_cistart_kernel(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Note that this crufty stuff with static and envval 13862306a36Sopenharmony_ci * and envbuf is because: 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * 1. Frequently, the stack is short, and we don't want to overrun; 14162306a36Sopenharmony_ci * 2. Frequently the stack is where we are going to copy the kernel to; 14262306a36Sopenharmony_ci * 3. A certain SRM console required the GET_ENV output to stack. 14362306a36Sopenharmony_ci * ??? A comment in the aboot sources indicates that the GET_ENV 14462306a36Sopenharmony_ci * destination must be quadword aligned. Might this explain the 14562306a36Sopenharmony_ci * behaviour, rather than requiring output to the stack, which 14662306a36Sopenharmony_ci * seems rather far-fetched. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci static long nbytes; 14962306a36Sopenharmony_ci static char envval[256] __attribute__((aligned(8))); 15062306a36Sopenharmony_ci static unsigned long initrd_start; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); 15362306a36Sopenharmony_ci if (INIT_HWRPB->pagesize != 8192) { 15462306a36Sopenharmony_ci srm_printk("Expected 8kB pages, got %ldkB\n", 15562306a36Sopenharmony_ci INIT_HWRPB->pagesize >> 10); 15662306a36Sopenharmony_ci return; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci if (INIT_HWRPB->vptb != (unsigned long) VPTB) { 15962306a36Sopenharmony_ci srm_printk("Expected vptb at %p, got %p\n", 16062306a36Sopenharmony_ci VPTB, (void *)INIT_HWRPB->vptb); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci pal_init(); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* The initrd must be page-aligned. See below for the 16662306a36Sopenharmony_ci cause of the magic number 5. */ 16762306a36Sopenharmony_ci initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) | 16862306a36Sopenharmony_ci (PAGE_SIZE-1)) + 1; 16962306a36Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 17062306a36Sopenharmony_ci srm_printk("Initrd positioned at %#lx\n", initrd_start); 17162306a36Sopenharmony_ci#endif 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Move the stack to a safe place to ensure it won't be 17562306a36Sopenharmony_ci * overwritten by kernel image. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci move_stack(initrd_start - PAGE_SIZE); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); 18062306a36Sopenharmony_ci if (nbytes < 0 || nbytes >= sizeof(envval)) { 18162306a36Sopenharmony_ci nbytes = 0; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci envval[nbytes] = '\0'; 18462306a36Sopenharmony_ci srm_printk("Loading the kernel...'%s'\n", envval); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* NOTE: *no* callbacks or printouts from here on out!!! */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* This is a hack, as some consoles seem to get virtual 20000000 (ie 18962306a36Sopenharmony_ci * where the SRM console puts the kernel bootp image) memory 19062306a36Sopenharmony_ci * overlapping physical memory where the kernel wants to be put, 19162306a36Sopenharmony_ci * which causes real problems when attempting to copy the former to 19262306a36Sopenharmony_ci * the latter... :-( 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * So, we first move the kernel virtual-to-physical way above where 19562306a36Sopenharmony_ci * we physically want the kernel to end up, then copy it from there 19662306a36Sopenharmony_ci * to its final resting place... ;-} 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Sigh... */ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 20162306a36Sopenharmony_ci load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE); 20262306a36Sopenharmony_ci#endif 20362306a36Sopenharmony_ci load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); 20462306a36Sopenharmony_ci load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci memset((char*)ZERO_PGE, 0, PAGE_SIZE); 20762306a36Sopenharmony_ci strcpy((char*)ZERO_PGE, envval); 20862306a36Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 20962306a36Sopenharmony_ci ((long *)(ZERO_PGE+256))[0] = initrd_start; 21062306a36Sopenharmony_ci ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; 21162306a36Sopenharmony_ci#endif 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci runkernel(); 21462306a36Sopenharmony_ci} 215