18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/alpha/boot/bootp.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1997 Jay Estabrook 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is used for creating a bootp file for the Linux/AXP kernel 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based significantly on the arch/alpha/boot/main.c of Linus Torvalds 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <generated/utsrelease.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/console.h> 188c2ecf20Sopenharmony_ci#include <asm/hwrpb.h> 198c2ecf20Sopenharmony_ci#include <asm/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <stdarg.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "ksize.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciextern unsigned long switch_to_osf_pal(unsigned long nr, 268c2ecf20Sopenharmony_ci struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, 278c2ecf20Sopenharmony_ci unsigned long *vptb); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciextern void move_stack(unsigned long new_stack); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct hwrpb_struct *hwrpb = INIT_HWRPB; 328c2ecf20Sopenharmony_cistatic struct pcb_struct pcb_va[1]; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * Find a physical address of a virtual object.. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * This is easy using the virtual page table address. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline void * 418c2ecf20Sopenharmony_cifind_pa(unsigned long *vptb, void *ptr) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned long address = (unsigned long) ptr; 448c2ecf20Sopenharmony_ci unsigned long result; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci result = vptb[address >> 13]; 478c2ecf20Sopenharmony_ci result >>= 32; 488c2ecf20Sopenharmony_ci result <<= 13; 498c2ecf20Sopenharmony_ci result |= address & 0x1fff; 508c2ecf20Sopenharmony_ci return (void *) result; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * This function moves into OSF/1 pal-code, and has a temporary 558c2ecf20Sopenharmony_ci * PCB for that. The kernel proper should replace this PCB with 568c2ecf20Sopenharmony_ci * the real one as soon as possible. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * The page table muckery in here depends on the fact that the boot 598c2ecf20Sopenharmony_ci * code has the L1 page table identity-map itself in the second PTE 608c2ecf20Sopenharmony_ci * in the L1 page table. Thus the L1-page is virtually addressable 618c2ecf20Sopenharmony_ci * itself (through three levels) at virtual address 0x200802000. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define VPTB ((unsigned long *) 0x200000000) 658c2ecf20Sopenharmony_ci#define L1 ((unsigned long *) 0x200802000) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid 688c2ecf20Sopenharmony_cipal_init(void) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned long i, rev; 718c2ecf20Sopenharmony_ci struct percpu_struct * percpu; 728c2ecf20Sopenharmony_ci struct pcb_struct * pcb_pa; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Create the dummy PCB. */ 758c2ecf20Sopenharmony_ci pcb_va->ksp = 0; 768c2ecf20Sopenharmony_ci pcb_va->usp = 0; 778c2ecf20Sopenharmony_ci pcb_va->ptbr = L1[1] >> 32; 788c2ecf20Sopenharmony_ci pcb_va->asn = 0; 798c2ecf20Sopenharmony_ci pcb_va->pcc = 0; 808c2ecf20Sopenharmony_ci pcb_va->unique = 0; 818c2ecf20Sopenharmony_ci pcb_va->flags = 1; 828c2ecf20Sopenharmony_ci pcb_va->res1 = 0; 838c2ecf20Sopenharmony_ci pcb_va->res2 = 0; 848c2ecf20Sopenharmony_ci pcb_pa = find_pa(VPTB, pcb_va); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * a0 = 2 (OSF) 888c2ecf20Sopenharmony_ci * a1 = return address, but we give the asm the vaddr of the PCB 898c2ecf20Sopenharmony_ci * a2 = physical addr of PCB 908c2ecf20Sopenharmony_ci * a3 = new virtual page table pointer 918c2ecf20Sopenharmony_ci * a4 = KSP (but the asm sets it) 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci srm_printk("Switching to OSF PAL-code .. "); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); 968c2ecf20Sopenharmony_ci if (i) { 978c2ecf20Sopenharmony_ci srm_printk("failed, code %ld\n", i); 988c2ecf20Sopenharmony_ci __halt(); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci percpu = (struct percpu_struct *) 1028c2ecf20Sopenharmony_ci (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); 1038c2ecf20Sopenharmony_ci rev = percpu->pal_revision = percpu->palcode_avail[2]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci srm_printk("Ok (rev %lx)\n", rev); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci tbia(); /* do it directly in case we are SMP */ 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline void 1118c2ecf20Sopenharmony_ciload(unsigned long dst, unsigned long src, unsigned long count) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci memcpy((void *)dst, (void *)src, count); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Start the kernel. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic inline void 1208c2ecf20Sopenharmony_cirunkernel(void) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci __asm__ __volatile__( 1238c2ecf20Sopenharmony_ci "bis %0,%0,$27\n\t" 1248c2ecf20Sopenharmony_ci "jmp ($27)" 1258c2ecf20Sopenharmony_ci : /* no outputs: it doesn't even return */ 1268c2ecf20Sopenharmony_ci : "r" (START_ADDR)); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciextern char _end; 1308c2ecf20Sopenharmony_ci#define KERNEL_ORIGIN \ 1318c2ecf20Sopenharmony_ci ((((unsigned long)&_end) + 511) & ~511) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_civoid 1348c2ecf20Sopenharmony_cistart_kernel(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * Note that this crufty stuff with static and envval 1388c2ecf20Sopenharmony_ci * and envbuf is because: 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * 1. Frequently, the stack is short, and we don't want to overrun; 1418c2ecf20Sopenharmony_ci * 2. Frequently the stack is where we are going to copy the kernel to; 1428c2ecf20Sopenharmony_ci * 3. A certain SRM console required the GET_ENV output to stack. 1438c2ecf20Sopenharmony_ci * ??? A comment in the aboot sources indicates that the GET_ENV 1448c2ecf20Sopenharmony_ci * destination must be quadword aligned. Might this explain the 1458c2ecf20Sopenharmony_ci * behaviour, rather than requiring output to the stack, which 1468c2ecf20Sopenharmony_ci * seems rather far-fetched. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci static long nbytes; 1498c2ecf20Sopenharmony_ci static char envval[256] __attribute__((aligned(8))); 1508c2ecf20Sopenharmony_ci static unsigned long initrd_start; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); 1538c2ecf20Sopenharmony_ci if (INIT_HWRPB->pagesize != 8192) { 1548c2ecf20Sopenharmony_ci srm_printk("Expected 8kB pages, got %ldkB\n", 1558c2ecf20Sopenharmony_ci INIT_HWRPB->pagesize >> 10); 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci if (INIT_HWRPB->vptb != (unsigned long) VPTB) { 1598c2ecf20Sopenharmony_ci srm_printk("Expected vptb at %p, got %p\n", 1608c2ecf20Sopenharmony_ci VPTB, (void *)INIT_HWRPB->vptb); 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci pal_init(); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* The initrd must be page-aligned. See below for the 1668c2ecf20Sopenharmony_ci cause of the magic number 5. */ 1678c2ecf20Sopenharmony_ci initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) | 1688c2ecf20Sopenharmony_ci (PAGE_SIZE-1)) + 1; 1698c2ecf20Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 1708c2ecf20Sopenharmony_ci srm_printk("Initrd positioned at %#lx\n", initrd_start); 1718c2ecf20Sopenharmony_ci#endif 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Move the stack to a safe place to ensure it won't be 1758c2ecf20Sopenharmony_ci * overwritten by kernel image. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci move_stack(initrd_start - PAGE_SIZE); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); 1808c2ecf20Sopenharmony_ci if (nbytes < 0 || nbytes >= sizeof(envval)) { 1818c2ecf20Sopenharmony_ci nbytes = 0; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci envval[nbytes] = '\0'; 1848c2ecf20Sopenharmony_ci srm_printk("Loading the kernel...'%s'\n", envval); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* NOTE: *no* callbacks or printouts from here on out!!! */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* This is a hack, as some consoles seem to get virtual 20000000 (ie 1898c2ecf20Sopenharmony_ci * where the SRM console puts the kernel bootp image) memory 1908c2ecf20Sopenharmony_ci * overlapping physical memory where the kernel wants to be put, 1918c2ecf20Sopenharmony_ci * which causes real problems when attempting to copy the former to 1928c2ecf20Sopenharmony_ci * the latter... :-( 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * So, we first move the kernel virtual-to-physical way above where 1958c2ecf20Sopenharmony_ci * we physically want the kernel to end up, then copy it from there 1968c2ecf20Sopenharmony_ci * to its final resting place... ;-} 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * Sigh... */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 2018c2ecf20Sopenharmony_ci load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE); 2028c2ecf20Sopenharmony_ci#endif 2038c2ecf20Sopenharmony_ci load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); 2048c2ecf20Sopenharmony_ci load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci memset((char*)ZERO_PGE, 0, PAGE_SIZE); 2078c2ecf20Sopenharmony_ci strcpy((char*)ZERO_PGE, envval); 2088c2ecf20Sopenharmony_ci#ifdef INITRD_IMAGE_SIZE 2098c2ecf20Sopenharmony_ci ((long *)(ZERO_PGE+256))[0] = initrd_start; 2108c2ecf20Sopenharmony_ci ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci runkernel(); 2148c2ecf20Sopenharmony_ci} 215