1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * arch/alpha/boot/bootp.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1997 Jay Estabrook
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * This file is used for creating a bootp file for the Linux/AXP kernel
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * based significantly on the arch/alpha/boot/main.c of Linus Torvalds
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds #include <linux/kernel.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
131da177e4SLinus Torvalds #include <linux/string.h>
14273b281fSSam Ravnborg #include <generated/utsrelease.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds #include <asm/console.h>
181da177e4SLinus Torvalds #include <asm/hwrpb.h>
191da177e4SLinus Torvalds #include <asm/io.h>
201da177e4SLinus Torvalds
21*e07f68d5SAl Viro #include <linux/stdarg.h>
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds #include "ksize.h"
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds extern unsigned long switch_to_osf_pal(unsigned long nr,
261da177e4SLinus Torvalds struct pcb_struct *pcb_va, struct pcb_struct *pcb_pa,
271da177e4SLinus Torvalds unsigned long *vptb);
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds extern void move_stack(unsigned long new_stack);
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds struct hwrpb_struct *hwrpb = INIT_HWRPB;
321da177e4SLinus Torvalds static struct pcb_struct pcb_va[1];
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds /*
351da177e4SLinus Torvalds * Find a physical address of a virtual object..
361da177e4SLinus Torvalds *
371da177e4SLinus Torvalds * This is easy using the virtual page table address.
381da177e4SLinus Torvalds */
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds static inline void *
find_pa(unsigned long * vptb,void * ptr)411da177e4SLinus Torvalds find_pa(unsigned long *vptb, void *ptr)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds unsigned long address = (unsigned long) ptr;
441da177e4SLinus Torvalds unsigned long result;
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds result = vptb[address >> 13];
471da177e4SLinus Torvalds result >>= 32;
481da177e4SLinus Torvalds result <<= 13;
491da177e4SLinus Torvalds result |= address & 0x1fff;
501da177e4SLinus Torvalds return (void *) result;
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds /*
541da177e4SLinus Torvalds * This function moves into OSF/1 pal-code, and has a temporary
551da177e4SLinus Torvalds * PCB for that. The kernel proper should replace this PCB with
561da177e4SLinus Torvalds * the real one as soon as possible.
571da177e4SLinus Torvalds *
581da177e4SLinus Torvalds * The page table muckery in here depends on the fact that the boot
591da177e4SLinus Torvalds * code has the L1 page table identity-map itself in the second PTE
601da177e4SLinus Torvalds * in the L1 page table. Thus the L1-page is virtually addressable
611da177e4SLinus Torvalds * itself (through three levels) at virtual address 0x200802000.
621da177e4SLinus Torvalds */
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds #define VPTB ((unsigned long *) 0x200000000)
651da177e4SLinus Torvalds #define L1 ((unsigned long *) 0x200802000)
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds void
pal_init(void)681da177e4SLinus Torvalds pal_init(void)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds unsigned long i, rev;
711da177e4SLinus Torvalds struct percpu_struct * percpu;
721da177e4SLinus Torvalds struct pcb_struct * pcb_pa;
731da177e4SLinus Torvalds
741da177e4SLinus Torvalds /* Create the dummy PCB. */
751da177e4SLinus Torvalds pcb_va->ksp = 0;
761da177e4SLinus Torvalds pcb_va->usp = 0;
771da177e4SLinus Torvalds pcb_va->ptbr = L1[1] >> 32;
781da177e4SLinus Torvalds pcb_va->asn = 0;
791da177e4SLinus Torvalds pcb_va->pcc = 0;
801da177e4SLinus Torvalds pcb_va->unique = 0;
811da177e4SLinus Torvalds pcb_va->flags = 1;
821da177e4SLinus Torvalds pcb_va->res1 = 0;
831da177e4SLinus Torvalds pcb_va->res2 = 0;
841da177e4SLinus Torvalds pcb_pa = find_pa(VPTB, pcb_va);
851da177e4SLinus Torvalds
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds * a0 = 2 (OSF)
881da177e4SLinus Torvalds * a1 = return address, but we give the asm the vaddr of the PCB
891da177e4SLinus Torvalds * a2 = physical addr of PCB
901da177e4SLinus Torvalds * a3 = new virtual page table pointer
911da177e4SLinus Torvalds * a4 = KSP (but the asm sets it)
921da177e4SLinus Torvalds */
931da177e4SLinus Torvalds srm_printk("Switching to OSF PAL-code .. ");
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB);
961da177e4SLinus Torvalds if (i) {
971da177e4SLinus Torvalds srm_printk("failed, code %ld\n", i);
981da177e4SLinus Torvalds __halt();
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds percpu = (struct percpu_struct *)
1021da177e4SLinus Torvalds (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB);
1031da177e4SLinus Torvalds rev = percpu->pal_revision = percpu->palcode_avail[2];
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds srm_printk("Ok (rev %lx)\n", rev);
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds tbia(); /* do it directly in case we are SMP */
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds static inline void
load(unsigned long dst,unsigned long src,unsigned long count)1111da177e4SLinus Torvalds load(unsigned long dst, unsigned long src, unsigned long count)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds memcpy((void *)dst, (void *)src, count);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds /*
1171da177e4SLinus Torvalds * Start the kernel.
1181da177e4SLinus Torvalds */
1191da177e4SLinus Torvalds static inline void
runkernel(void)1201da177e4SLinus Torvalds runkernel(void)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds __asm__ __volatile__(
1231da177e4SLinus Torvalds "bis %0,%0,$27\n\t"
1241da177e4SLinus Torvalds "jmp ($27)"
1251da177e4SLinus Torvalds : /* no outputs: it doesn't even return */
1261da177e4SLinus Torvalds : "r" (START_ADDR));
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds extern char _end;
1301da177e4SLinus Torvalds #define KERNEL_ORIGIN \
1311da177e4SLinus Torvalds ((((unsigned long)&_end) + 511) & ~511)
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds void
start_kernel(void)1341da177e4SLinus Torvalds start_kernel(void)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds /*
1371da177e4SLinus Torvalds * Note that this crufty stuff with static and envval
1381da177e4SLinus Torvalds * and envbuf is because:
1391da177e4SLinus Torvalds *
1401da177e4SLinus Torvalds * 1. Frequently, the stack is short, and we don't want to overrun;
1411da177e4SLinus Torvalds * 2. Frequently the stack is where we are going to copy the kernel to;
1421da177e4SLinus Torvalds * 3. A certain SRM console required the GET_ENV output to stack.
1431da177e4SLinus Torvalds * ??? A comment in the aboot sources indicates that the GET_ENV
1441da177e4SLinus Torvalds * destination must be quadword aligned. Might this explain the
1451da177e4SLinus Torvalds * behaviour, rather than requiring output to the stack, which
1461da177e4SLinus Torvalds * seems rather far-fetched.
1471da177e4SLinus Torvalds */
1481da177e4SLinus Torvalds static long nbytes;
1491da177e4SLinus Torvalds static char envval[256] __attribute__((aligned(8)));
1501da177e4SLinus Torvalds static unsigned long initrd_start;
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
1531da177e4SLinus Torvalds if (INIT_HWRPB->pagesize != 8192) {
1541da177e4SLinus Torvalds srm_printk("Expected 8kB pages, got %ldkB\n",
1551da177e4SLinus Torvalds INIT_HWRPB->pagesize >> 10);
1561da177e4SLinus Torvalds return;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds if (INIT_HWRPB->vptb != (unsigned long) VPTB) {
1591da177e4SLinus Torvalds srm_printk("Expected vptb at %p, got %p\n",
1601da177e4SLinus Torvalds VPTB, (void *)INIT_HWRPB->vptb);
1611da177e4SLinus Torvalds return;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds pal_init();
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds /* The initrd must be page-aligned. See below for the
1661da177e4SLinus Torvalds cause of the magic number 5. */
1671da177e4SLinus Torvalds initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) |
1681da177e4SLinus Torvalds (PAGE_SIZE-1)) + 1;
1691da177e4SLinus Torvalds #ifdef INITRD_IMAGE_SIZE
1701da177e4SLinus Torvalds srm_printk("Initrd positioned at %#lx\n", initrd_start);
1711da177e4SLinus Torvalds #endif
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds /*
1741da177e4SLinus Torvalds * Move the stack to a safe place to ensure it won't be
1751da177e4SLinus Torvalds * overwritten by kernel image.
1761da177e4SLinus Torvalds */
1771da177e4SLinus Torvalds move_stack(initrd_start - PAGE_SIZE);
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval));
1801da177e4SLinus Torvalds if (nbytes < 0 || nbytes >= sizeof(envval)) {
1811da177e4SLinus Torvalds nbytes = 0;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds envval[nbytes] = '\0';
1841da177e4SLinus Torvalds srm_printk("Loading the kernel...'%s'\n", envval);
1851da177e4SLinus Torvalds
1861da177e4SLinus Torvalds /* NOTE: *no* callbacks or printouts from here on out!!! */
1871da177e4SLinus Torvalds
1881da177e4SLinus Torvalds /* This is a hack, as some consoles seem to get virtual 20000000 (ie
1891da177e4SLinus Torvalds * where the SRM console puts the kernel bootp image) memory
1901da177e4SLinus Torvalds * overlapping physical memory where the kernel wants to be put,
1911da177e4SLinus Torvalds * which causes real problems when attempting to copy the former to
1921da177e4SLinus Torvalds * the latter... :-(
1931da177e4SLinus Torvalds *
1941da177e4SLinus Torvalds * So, we first move the kernel virtual-to-physical way above where
1951da177e4SLinus Torvalds * we physically want the kernel to end up, then copy it from there
1961da177e4SLinus Torvalds * to its final resting place... ;-}
1971da177e4SLinus Torvalds *
1981da177e4SLinus Torvalds * Sigh... */
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds #ifdef INITRD_IMAGE_SIZE
2011da177e4SLinus Torvalds load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE);
2021da177e4SLinus Torvalds #endif
2031da177e4SLinus Torvalds load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE);
2041da177e4SLinus Torvalds load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE);
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds memset((char*)ZERO_PGE, 0, PAGE_SIZE);
2071da177e4SLinus Torvalds strcpy((char*)ZERO_PGE, envval);
2081da177e4SLinus Torvalds #ifdef INITRD_IMAGE_SIZE
2091da177e4SLinus Torvalds ((long *)(ZERO_PGE+256))[0] = initrd_start;
2101da177e4SLinus Torvalds ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE;
2111da177e4SLinus Torvalds #endif
2121da177e4SLinus Torvalds
2131da177e4SLinus Torvalds runkernel();
2141da177e4SLinus Torvalds }
215