xref: /openbmc/linux/arch/alpha/boot/bootp.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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