diff options
Diffstat (limited to 'arch/alpha/boot/main.c')
-rw-r--r-- | arch/alpha/boot/main.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/arch/alpha/boot/main.c b/arch/alpha/boot/main.c new file mode 100644 index 000000000000..78c9b0b6eea7 --- /dev/null +++ b/arch/alpha/boot/main.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/main.c | ||
3 | * | ||
4 | * Copyright (C) 1994, 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file is the bootloader for the Linux/AXP kernel | ||
7 | */ | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/string.h> | ||
10 | #include <linux/version.h> | ||
11 | #include <linux/mm.h> | ||
12 | |||
13 | #include <asm/system.h> | ||
14 | #include <asm/console.h> | ||
15 | #include <asm/hwrpb.h> | ||
16 | #include <asm/pgtable.h> | ||
17 | |||
18 | #include <stdarg.h> | ||
19 | |||
20 | #include "ksize.h" | ||
21 | |||
22 | extern int vsprintf(char *, const char *, va_list); | ||
23 | extern unsigned long switch_to_osf_pal(unsigned long nr, | ||
24 | struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, | ||
25 | unsigned long *vptb); | ||
26 | struct hwrpb_struct *hwrpb = INIT_HWRPB; | ||
27 | static struct pcb_struct pcb_va[1]; | ||
28 | |||
29 | /* | ||
30 | * Find a physical address of a virtual object.. | ||
31 | * | ||
32 | * This is easy using the virtual page table address. | ||
33 | */ | ||
34 | |||
35 | static inline void * | ||
36 | find_pa(unsigned long *vptb, void *ptr) | ||
37 | { | ||
38 | unsigned long address = (unsigned long) ptr; | ||
39 | unsigned long result; | ||
40 | |||
41 | result = vptb[address >> 13]; | ||
42 | result >>= 32; | ||
43 | result <<= 13; | ||
44 | result |= address & 0x1fff; | ||
45 | return (void *) result; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * This function moves into OSF/1 pal-code, and has a temporary | ||
50 | * PCB for that. The kernel proper should replace this PCB with | ||
51 | * the real one as soon as possible. | ||
52 | * | ||
53 | * The page table muckery in here depends on the fact that the boot | ||
54 | * code has the L1 page table identity-map itself in the second PTE | ||
55 | * in the L1 page table. Thus the L1-page is virtually addressable | ||
56 | * itself (through three levels) at virtual address 0x200802000. | ||
57 | */ | ||
58 | |||
59 | #define VPTB ((unsigned long *) 0x200000000) | ||
60 | #define L1 ((unsigned long *) 0x200802000) | ||
61 | |||
62 | void | ||
63 | pal_init(void) | ||
64 | { | ||
65 | unsigned long i, rev; | ||
66 | struct percpu_struct * percpu; | ||
67 | struct pcb_struct * pcb_pa; | ||
68 | |||
69 | /* Create the dummy PCB. */ | ||
70 | pcb_va->ksp = 0; | ||
71 | pcb_va->usp = 0; | ||
72 | pcb_va->ptbr = L1[1] >> 32; | ||
73 | pcb_va->asn = 0; | ||
74 | pcb_va->pcc = 0; | ||
75 | pcb_va->unique = 0; | ||
76 | pcb_va->flags = 1; | ||
77 | pcb_va->res1 = 0; | ||
78 | pcb_va->res2 = 0; | ||
79 | pcb_pa = find_pa(VPTB, pcb_va); | ||
80 | |||
81 | /* | ||
82 | * a0 = 2 (OSF) | ||
83 | * a1 = return address, but we give the asm the vaddr of the PCB | ||
84 | * a2 = physical addr of PCB | ||
85 | * a3 = new virtual page table pointer | ||
86 | * a4 = KSP (but the asm sets it) | ||
87 | */ | ||
88 | srm_printk("Switching to OSF PAL-code .. "); | ||
89 | |||
90 | i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); | ||
91 | if (i) { | ||
92 | srm_printk("failed, code %ld\n", i); | ||
93 | __halt(); | ||
94 | } | ||
95 | |||
96 | percpu = (struct percpu_struct *) | ||
97 | (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); | ||
98 | rev = percpu->pal_revision = percpu->palcode_avail[2]; | ||
99 | |||
100 | srm_printk("Ok (rev %lx)\n", rev); | ||
101 | |||
102 | tbia(); /* do it directly in case we are SMP */ | ||
103 | } | ||
104 | |||
105 | static inline long openboot(void) | ||
106 | { | ||
107 | char bootdev[256]; | ||
108 | long result; | ||
109 | |||
110 | result = callback_getenv(ENV_BOOTED_DEV, bootdev, 255); | ||
111 | if (result < 0) | ||
112 | return result; | ||
113 | return callback_open(bootdev, result & 255); | ||
114 | } | ||
115 | |||
116 | static inline long close(long dev) | ||
117 | { | ||
118 | return callback_close(dev); | ||
119 | } | ||
120 | |||
121 | static inline long load(long dev, unsigned long addr, unsigned long count) | ||
122 | { | ||
123 | char bootfile[256]; | ||
124 | extern char _end; | ||
125 | long result, boot_size = &_end - (char *) BOOT_ADDR; | ||
126 | |||
127 | result = callback_getenv(ENV_BOOTED_FILE, bootfile, 255); | ||
128 | if (result < 0) | ||
129 | return result; | ||
130 | result &= 255; | ||
131 | bootfile[result] = '\0'; | ||
132 | if (result) | ||
133 | srm_printk("Boot file specification (%s) not implemented\n", | ||
134 | bootfile); | ||
135 | return callback_read(dev, count, addr, boot_size/512 + 1); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Start the kernel. | ||
140 | */ | ||
141 | static void runkernel(void) | ||
142 | { | ||
143 | __asm__ __volatile__( | ||
144 | "bis %1,%1,$30\n\t" | ||
145 | "bis %0,%0,$26\n\t" | ||
146 | "ret ($26)" | ||
147 | : /* no outputs: it doesn't even return */ | ||
148 | : "r" (START_ADDR), | ||
149 | "r" (PAGE_SIZE + INIT_STACK)); | ||
150 | } | ||
151 | |||
152 | void start_kernel(void) | ||
153 | { | ||
154 | long i; | ||
155 | long dev; | ||
156 | int nbytes; | ||
157 | char envval[256]; | ||
158 | |||
159 | srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); | ||
160 | if (INIT_HWRPB->pagesize != 8192) { | ||
161 | srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10); | ||
162 | return; | ||
163 | } | ||
164 | pal_init(); | ||
165 | dev = openboot(); | ||
166 | if (dev < 0) { | ||
167 | srm_printk("Unable to open boot device: %016lx\n", dev); | ||
168 | return; | ||
169 | } | ||
170 | dev &= 0xffffffff; | ||
171 | srm_printk("Loading vmlinux ..."); | ||
172 | i = load(dev, START_ADDR, KERNEL_SIZE); | ||
173 | close(dev); | ||
174 | if (i != KERNEL_SIZE) { | ||
175 | srm_printk("Failed (%lx)\n", i); | ||
176 | return; | ||
177 | } | ||
178 | |||
179 | nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); | ||
180 | if (nbytes < 0) { | ||
181 | nbytes = 0; | ||
182 | } | ||
183 | envval[nbytes] = '\0'; | ||
184 | strcpy((char*)ZERO_PGE, envval); | ||
185 | |||
186 | srm_printk(" Ok\nNow booting the kernel\n"); | ||
187 | runkernel(); | ||
188 | for (i = 0 ; i < 0x100000000 ; i++) | ||
189 | /* nothing */; | ||
190 | __halt(); | ||
191 | } | ||