162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/alpha/boot/main.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1994, 1995 Linus Torvalds 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is the bootloader for the Linux/AXP kernel 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <generated/utsrelease.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/console.h> 1662306a36Sopenharmony_ci#include <asm/hwrpb.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/stdarg.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "ksize.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciextern unsigned long switch_to_osf_pal(unsigned long nr, 2362306a36Sopenharmony_ci struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, 2462306a36Sopenharmony_ci unsigned long *vptb); 2562306a36Sopenharmony_cistruct hwrpb_struct *hwrpb = INIT_HWRPB; 2662306a36Sopenharmony_cistatic struct pcb_struct pcb_va[1]; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Find a physical address of a virtual object.. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * This is easy using the virtual page table address. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic inline void * 3562306a36Sopenharmony_cifind_pa(unsigned long *vptb, void *ptr) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned long address = (unsigned long) ptr; 3862306a36Sopenharmony_ci unsigned long result; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci result = vptb[address >> 13]; 4162306a36Sopenharmony_ci result >>= 32; 4262306a36Sopenharmony_ci result <<= 13; 4362306a36Sopenharmony_ci result |= address & 0x1fff; 4462306a36Sopenharmony_ci return (void *) result; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * This function moves into OSF/1 pal-code, and has a temporary 4962306a36Sopenharmony_ci * PCB for that. The kernel proper should replace this PCB with 5062306a36Sopenharmony_ci * the real one as soon as possible. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The page table muckery in here depends on the fact that the boot 5362306a36Sopenharmony_ci * code has the L1 page table identity-map itself in the second PTE 5462306a36Sopenharmony_ci * in the L1 page table. Thus the L1-page is virtually addressable 5562306a36Sopenharmony_ci * itself (through three levels) at virtual address 0x200802000. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define VPTB ((unsigned long *) 0x200000000) 5962306a36Sopenharmony_ci#define L1 ((unsigned long *) 0x200802000) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid 6262306a36Sopenharmony_cipal_init(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci unsigned long i, rev; 6562306a36Sopenharmony_ci struct percpu_struct * percpu; 6662306a36Sopenharmony_ci struct pcb_struct * pcb_pa; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Create the dummy PCB. */ 6962306a36Sopenharmony_ci pcb_va->ksp = 0; 7062306a36Sopenharmony_ci pcb_va->usp = 0; 7162306a36Sopenharmony_ci pcb_va->ptbr = L1[1] >> 32; 7262306a36Sopenharmony_ci pcb_va->asn = 0; 7362306a36Sopenharmony_ci pcb_va->pcc = 0; 7462306a36Sopenharmony_ci pcb_va->unique = 0; 7562306a36Sopenharmony_ci pcb_va->flags = 1; 7662306a36Sopenharmony_ci pcb_va->res1 = 0; 7762306a36Sopenharmony_ci pcb_va->res2 = 0; 7862306a36Sopenharmony_ci pcb_pa = find_pa(VPTB, pcb_va); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * a0 = 2 (OSF) 8262306a36Sopenharmony_ci * a1 = return address, but we give the asm the vaddr of the PCB 8362306a36Sopenharmony_ci * a2 = physical addr of PCB 8462306a36Sopenharmony_ci * a3 = new virtual page table pointer 8562306a36Sopenharmony_ci * a4 = KSP (but the asm sets it) 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci srm_printk("Switching to OSF PAL-code .. "); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); 9062306a36Sopenharmony_ci if (i) { 9162306a36Sopenharmony_ci srm_printk("failed, code %ld\n", i); 9262306a36Sopenharmony_ci __halt(); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci percpu = (struct percpu_struct *) 9662306a36Sopenharmony_ci (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); 9762306a36Sopenharmony_ci rev = percpu->pal_revision = percpu->palcode_avail[2]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci srm_printk("Ok (rev %lx)\n", rev); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci tbia(); /* do it directly in case we are SMP */ 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline long openboot(void) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci char bootdev[256]; 10762306a36Sopenharmony_ci long result; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci result = callback_getenv(ENV_BOOTED_DEV, bootdev, 255); 11062306a36Sopenharmony_ci if (result < 0) 11162306a36Sopenharmony_ci return result; 11262306a36Sopenharmony_ci return callback_open(bootdev, result & 255); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic inline long close(long dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci return callback_close(dev); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic inline long load(long dev, unsigned long addr, unsigned long count) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci char bootfile[256]; 12362306a36Sopenharmony_ci extern char _end; 12462306a36Sopenharmony_ci long result, boot_size = &_end - (char *) BOOT_ADDR; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci result = callback_getenv(ENV_BOOTED_FILE, bootfile, 255); 12762306a36Sopenharmony_ci if (result < 0) 12862306a36Sopenharmony_ci return result; 12962306a36Sopenharmony_ci result &= 255; 13062306a36Sopenharmony_ci bootfile[result] = '\0'; 13162306a36Sopenharmony_ci if (result) 13262306a36Sopenharmony_ci srm_printk("Boot file specification (%s) not implemented\n", 13362306a36Sopenharmony_ci bootfile); 13462306a36Sopenharmony_ci return callback_read(dev, count, (void *)addr, boot_size/512 + 1); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * Start the kernel. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic void runkernel(void) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci __asm__ __volatile__( 14362306a36Sopenharmony_ci "bis %1,%1,$30\n\t" 14462306a36Sopenharmony_ci "bis %0,%0,$26\n\t" 14562306a36Sopenharmony_ci "ret ($26)" 14662306a36Sopenharmony_ci : /* no outputs: it doesn't even return */ 14762306a36Sopenharmony_ci : "r" (START_ADDR), 14862306a36Sopenharmony_ci "r" (PAGE_SIZE + INIT_STACK)); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid start_kernel(void) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci long i; 15462306a36Sopenharmony_ci long dev; 15562306a36Sopenharmony_ci int nbytes; 15662306a36Sopenharmony_ci char envval[256]; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); 15962306a36Sopenharmony_ci if (INIT_HWRPB->pagesize != 8192) { 16062306a36Sopenharmony_ci srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci pal_init(); 16462306a36Sopenharmony_ci dev = openboot(); 16562306a36Sopenharmony_ci if (dev < 0) { 16662306a36Sopenharmony_ci srm_printk("Unable to open boot device: %016lx\n", dev); 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci dev &= 0xffffffff; 17062306a36Sopenharmony_ci srm_printk("Loading vmlinux ..."); 17162306a36Sopenharmony_ci i = load(dev, START_ADDR, KERNEL_SIZE); 17262306a36Sopenharmony_ci close(dev); 17362306a36Sopenharmony_ci if (i != KERNEL_SIZE) { 17462306a36Sopenharmony_ci srm_printk("Failed (%lx)\n", i); 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); 17962306a36Sopenharmony_ci if (nbytes < 0) { 18062306a36Sopenharmony_ci nbytes = 0; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci envval[nbytes] = '\0'; 18362306a36Sopenharmony_ci strcpy((char*)ZERO_PGE, envval); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci srm_printk(" Ok\nNow booting the kernel\n"); 18662306a36Sopenharmony_ci runkernel(); 18762306a36Sopenharmony_ci for (i = 0 ; i < 0x100000000 ; i++) 18862306a36Sopenharmony_ci /* nothing */; 18962306a36Sopenharmony_ci __halt(); 19062306a36Sopenharmony_ci} 191