File: /usr/src/linux/arch/ppc/boot/prep/misc.c
1 /*
2 * BK Id: SCCS/s.misc.c 1.18 07/30/01 17:19:40 trini
3 *
4 * arch/ppc/boot/prep/misc.c
5 *
6 * Adapted for PowerPC by Gary Thomas
7 *
8 * Rewritten by Cort Dougan (cort@cs.nmt.edu)
9 * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
10 */
11
12 #include <linux/types.h>
13 #include <asm/residual.h>
14 #include <linux/config.h>
15 #include <linux/threads.h>
16 #include <linux/elf.h>
17 #include <linux/pci_ids.h>
18 #include <asm/page.h>
19 #include <asm/processor.h>
20 #include <asm/bootinfo.h>
21 #include <asm/mmu.h>
22 #include <asm/byteorder.h>
23 #include "nonstdio.h"
24 #include "zlib.h"
25
26 /*
27 * Please send me load/board info and such data for hardware not
28 * listed here so I can keep track since things are getting tricky
29 * with the different load addrs with different firmware. This will
30 * help to avoid breaking the load/boot process.
31 * -- Cort
32 */
33 char *avail_ram;
34 char *end_avail;
35 extern char _end[];
36
37 #ifdef CONFIG_CMDLINE
38 #define CMDLINE CONFIG_CMDLINE
39 #else
40 #define CMDLINE "";
41 #endif
42 char cmd_preset[] = CMDLINE;
43 char cmd_buf[256];
44 char *cmd_line = cmd_buf;
45
46 int keyb_present = 1; /* keyboard controller is present by default */
47 RESIDUAL hold_resid_buf;
48 RESIDUAL *hold_residual = &hold_resid_buf;
49 unsigned long initrd_start = 0, initrd_end = 0;
50 char *zimage_start;
51 int zimage_size;
52
53 #if defined(CONFIG_SERIAL_CONSOLE)
54 unsigned long com_port;
55 #endif /* CONFIG_SERIAL_CONSOLE */
56 #ifdef CONFIG_VGA_CONSOLE
57 char *vidmem = (char *)0xC00B8000;
58 int lines = 25, cols = 80;
59 int orig_x, orig_y = 24;
60 #endif /* CONFIG_VGA_CONSOLE */
61
62 extern int CRT_tstc(void);
63 extern void of_init(void *handler);
64 extern int of_finddevice(const char *device_specifier, int *phandle);
65 extern int of_getprop(int phandle, const char *name, void *buf, int buflen,
66 int *size);
67 extern int vga_init(unsigned char *ISA_mem);
68 extern void gunzip(void *, int, unsigned char *, int *);
69
70 extern void _put_HID0(unsigned int val);
71 extern void _put_MSR(unsigned int val);
72 extern unsigned int _get_HID0(void);
73 extern unsigned int _get_MSR(void);
74 extern unsigned long serial_init(int chan);
75
76 void
77 writel(unsigned int val, unsigned int address)
78 {
79 /* Ensure I/O operations complete */
80 __asm__ volatile("eieio");
81 *(unsigned int *)address = cpu_to_le32(val);
82 }
83
84 unsigned int
85 readl(unsigned int address)
86 {
87 /* Ensure I/O operations complete */
88 __asm__ volatile("eieio");
89 return le32_to_cpu(*(unsigned int *)address);
90 }
91
92 #define PCI_CFG_ADDR(dev,off) ((0x80<<24) | (dev<<8) | (off&0xfc))
93 #define PCI_CFG_DATA(off) (0x80000cfc+(off&3))
94
95 static void
96 pci_read_config_32(unsigned char devfn,
97 unsigned char offset,
98 unsigned int *val)
99 {
100 writel(PCI_CFG_ADDR(devfn,offset), 0x80000cf8);
101 *val = readl(PCI_CFG_DATA(offset));
102 return;
103 }
104
105 #ifdef CONFIG_VGA_CONSOLE
106 void
107 scroll(void)
108 {
109 int i;
110
111 memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
112 for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
113 vidmem[i] = ' ';
114 }
115 #endif /* CONFIG_VGA_CONSOLE */
116
117 /*
118 * This routine is used to control the second processor on the
119 * Motorola dual processor platforms.
120 */
121 void
122 park_cpus(void)
123 {
124 volatile void (*go)(RESIDUAL *, int, int, char *, int);
125 unsigned int i;
126 volatile unsigned long *smp_iar = &(hold_residual->VitalProductData.SmpIar);
127
128 /* Wait for indication to continue. If the kernel
129 was not compiled with SMP support then the second
130 processor will spin forever here makeing the kernel
131 multiprocessor safe. */
132 while (*smp_iar == 0) {
133 for (i=0; i < 512; i++);
134 }
135
136 (unsigned long)go = hold_residual->VitalProductData.SmpIar;
137 go(hold_residual, 0, 0, cmd_line, sizeof(cmd_preset));
138 }
139
140 unsigned long
141 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
142 RESIDUAL *residual, void *OFW_interface)
143 {
144 int timer;
145 extern unsigned long start;
146 char *cp, ch;
147 unsigned long TotalMemory;
148 unsigned long orig_MSR;
149 int dev_handle;
150 int mem_info[2];
151 int res, size;
152 unsigned char board_type;
153 unsigned char base_mod;
154 int start_multi = 0;
155 unsigned int pci_viddid, pci_did, tulip_pci_base, tulip_base;
156
157 /*
158 * IBM's have the MMU on, so we have to disable it or
159 * things get really unhappy in the kernel when
160 * trying to setup the BATs with the MMU on
161 * -- Cort
162 */
163 flush_instruction_cache();
164 _put_HID0(_get_HID0() & ~0x0000C000);
165 _put_MSR((orig_MSR = _get_MSR()) & ~0x0030);
166
167 #if defined(CONFIG_SERIAL_CONSOLE)
168 com_port = serial_init(0);
169 #endif /* CONFIG_SERIAL_CONSOLE */
170 #if defined(CONFIG_VGA_CONSOLE)
171 vga_init((unsigned char *)0xC0000000);
172 #endif /* CONFIG_VGA_CONSOLE */
173
174 if (residual)
175 {
176 /* Is this Motorola PPCBug? */
177 if ((1 & residual->VitalProductData.FirmwareSupports) &&
178 (1 == residual->VitalProductData.FirmwareSupplier)) {
179 board_type = inb(0x800) & 0xF0;
180
181 /*
182 * Reset the onboard 21x4x Ethernet
183 * Motorola Ethernet is at IDSEL 14 (devfn 0x70)
184 */
185 pci_read_config_32(0x70, 0x00, &pci_viddid);
186 pci_did = (pci_viddid & 0xffff0000) >> 16;
187 /* Be sure we've really found a 21x4x chip */
188 if (((pci_viddid & 0xffff) == PCI_VENDOR_ID_DEC) &&
189 ((pci_did == PCI_DEVICE_ID_DEC_TULIP_FAST) ||
190 (pci_did == PCI_DEVICE_ID_DEC_TULIP) ||
191 (pci_did == PCI_DEVICE_ID_DEC_TULIP_PLUS) ||
192 (pci_did == PCI_DEVICE_ID_DEC_21142)))
193 {
194 pci_read_config_32(0x70,
195 0x10,
196 &tulip_pci_base);
197 /* Get the physical base address */
198 tulip_base =
199 (tulip_pci_base & ~0x03UL) + 0x80000000;
200 /* Strobe the 21x4x reset bit in CSR0 */
201 writel(0x1, tulip_base);
202 }
203
204 /* If this is genesis 2 board then check for no
205 * keyboard controller and more than one processor.
206 */
207 if (board_type == 0xe0) {
208 base_mod = inb(0x803);
209 /* if a MVME2300/2400 or a Sitka then no keyboard */
210 if((base_mod == 0xFA) || (base_mod == 0xF9) ||
211 (base_mod == 0xE1)) {
212 keyb_present = 0; /* no keyboard */
213 }
214 }
215 /* If this is a multiprocessor system then
216 * park the other processor so that the
217 * kernel knows where to find them.
218 */
219 if (residual->MaxNumCpus > 1) {
220 start_multi = 1;
221 }
222 }
223 memcpy(hold_residual,residual,sizeof(RESIDUAL));
224 } else {
225 /* Assume 32M in the absence of more info... */
226 TotalMemory = 0x02000000;
227 /*
228 * This is a 'best guess' check. We want to make sure
229 * we don't try this on a PReP box without OF
230 * -- Cort
231 */
232 while (OFW_interface && ((unsigned long)OFW_interface < 0x10000000) )
233 {
234 /* The MMU needs to be on when we call OFW */
235 _put_MSR(orig_MSR);
236 of_init(OFW_interface);
237
238 /* get handle to memory description */
239 res = of_finddevice("/memory@0",
240 &dev_handle);
241 // puthex(res); puts("\n");
242 if (res) break;
243
244 /* get the info */
245 // puts("get info = ");
246 res = of_getprop(dev_handle,
247 "reg",
248 mem_info,
249 sizeof(mem_info),
250 &size);
251 // puthex(res); puts(", info = "); puthex(mem_info[0]);
252 // puts(" "); puthex(mem_info[1]); puts("\n");
253 if (res) break;
254
255 TotalMemory = mem_info[1];
256 break;
257 }
258 hold_residual->TotalMemory = TotalMemory;
259 residual = hold_residual;
260 /* Turn MMU back off */
261 _put_MSR(orig_MSR & ~0x0030);
262 }
263
264 if (start_multi) {
265 hold_residual->VitalProductData.SmpIar = 0;
266 hold_residual->Cpus[1].CpuState = CPU_GOOD_FW;
267 residual->VitalProductData.SmpIar = (unsigned long)park_cpus;
268 residual->Cpus[1].CpuState = CPU_GOOD;
269 hold_residual->VitalProductData.Reserved5 = 0xdeadbeef;
270 }
271
272 /* assume the chunk below 8M is free */
273 end_avail = (char *)0x00800000;
274
275 /* tell the user where we were loaded at and where we
276 * were relocated to for debugging this process
277 */
278 puts("loaded at: "); puthex(load_addr);
279 puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
280 if ( (unsigned long)load_addr != (unsigned long)&start )
281 {
282 puts("relocated to: "); puthex((unsigned long)&start);
283 puts(" ");
284 puthex((unsigned long)((unsigned long)&start + (4*num_words)));
285 puts("\n");
286 }
287
288 if ( residual )
289 {
290 puts("board data at: "); puthex((unsigned long)residual);
291 puts(" ");
292 puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL)));
293 puts("\n");
294 puts("relocated to: ");
295 puthex((unsigned long)hold_residual);
296 puts(" ");
297 puthex((unsigned long)((unsigned long)hold_residual + sizeof(RESIDUAL)));
298 puts("\n");
299 }
300
301 /* we have to subtract 0x10000 here to correct for objdump including the
302 size of the elf header which we strip -- Cort */
303 zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
304 zimage_size = ZIMAGE_SIZE;
305
306 if ( INITRD_OFFSET )
307 initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
308 else
309 initrd_start = 0;
310 initrd_end = INITRD_SIZE + initrd_start;
311
312 /*
313 * Find a place to stick the zimage and initrd and
314 * relocate them if we have to. -- Cort
315 */
316 avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
317 puts("zimage at: "); puthex((unsigned long)zimage_start);
318 puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
319 if ( (unsigned long)zimage_start <= 0x00800000 )
320 {
321 memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
322 zimage_start = (char *)avail_ram;
323 puts("relocated to: "); puthex((unsigned long)zimage_start);
324 puts(" ");
325 puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
326 puts("\n");
327
328 /* relocate initrd */
329 if ( initrd_start )
330 {
331 puts("initrd at: "); puthex(initrd_start);
332 puts(" "); puthex(initrd_end); puts("\n");
333 avail_ram = (char *)PAGE_ALIGN(
334 (unsigned long)zimage_size+(unsigned long)zimage_start);
335 memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
336 initrd_start = (unsigned long)avail_ram;
337 initrd_end = initrd_start + INITRD_SIZE;
338 puts("relocated to: "); puthex(initrd_start);
339 puts(" "); puthex(initrd_end); puts("\n");
340 }
341 } else if ( initrd_start ) {
342 puts("initrd at: "); puthex(initrd_start);
343 puts(" "); puthex(initrd_end); puts("\n");
344 }
345
346 avail_ram = (char *)0x00400000;
347 end_avail = (char *)0x00800000;
348 puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" ");
349 puthex((unsigned long)end_avail); puts("\n");
350
351 if (keyb_present)
352 CRT_tstc(); /* Forces keyboard to be initialized */
353
354 puts("\nLinux/PPC load: ");
355 timer = 0;
356 cp = cmd_line;
357 memcpy (cmd_line, cmd_preset, sizeof(cmd_preset));
358 while ( *cp ) putc(*cp++);
359 while (timer++ < 5*1000) {
360 if (tstc()) {
361 while ((ch = getc()) != '\n' && ch != '\r') {
362 /* Test for backspace/delete */
363 if (ch == '\b' || ch == '\177') {
364 if (cp != cmd_line) {
365 cp--;
366 puts("\b \b");
367 }
368 /* Test for ^x/^u (and wipe the line) */
369 } else if (ch == '\030' || ch == '\025') {
370 while (cp != cmd_line) {
371 cp--;
372 puts("\b \b");
373 }
374 } else {
375 *cp++ = ch;
376 putc(ch);
377 }
378 }
379 break; /* Exit 'timer' loop */
380 }
381 udelay(1000); /* 1 msec */
382 }
383 *cp = 0;
384 puts("\n");
385
386 /* mappings on early boot can only handle 16M */
387 if ( (int)(cmd_line[0]) > (16<<20))
388 puts("cmd_line located > 16M\n");
389 if ( (int)hold_residual > (16<<20))
390 puts("hold_residual located > 16M\n");
391 if ( initrd_start > (16<<20))
392 puts("initrd_start located > 16M\n");
393
394 puts("Uncompressing Linux...");
395
396 gunzip(0, 0x400000, zimage_start, &zimage_size);
397 puts("done.\n");
398
399 {
400 struct bi_record *rec;
401
402 rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size)+(1<<20)-1,(1<<20));
403
404 rec->tag = BI_FIRST;
405 rec->size = sizeof(struct bi_record);
406 rec = (struct bi_record *)((unsigned long)rec + rec->size);
407
408 rec->tag = BI_BOOTLOADER_ID;
409 memcpy( (void *)rec->data, "prepboot", 9);
410 rec->size = sizeof(struct bi_record) + 8 + 1;
411 rec = (struct bi_record *)((unsigned long)rec + rec->size);
412
413 rec->tag = BI_MACHTYPE;
414 rec->data[0] = _MACH_prep;
415 rec->data[1] = 0;
416 rec->size = sizeof(struct bi_record) + 2 * sizeof(unsigned long);
417 rec = (struct bi_record *)((unsigned long)rec + rec->size);
418
419 rec->tag = BI_CMD_LINE;
420 memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1);
421 rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1;
422 rec = (struct bi_record *)((ulong)rec + rec->size);
423
424 rec->tag = BI_LAST;
425 rec->size = sizeof(struct bi_record);
426 rec = (struct bi_record *)((unsigned long)rec + rec->size);
427 }
428 puts("Now booting the kernel\n");
429 return (unsigned long)hold_residual;
430 }
431
432 /*
433 * PCI/ISA I/O support
434 */
435 unsigned long
436 local_to_PCI(unsigned long addr)
437 {
438 return (addr | 0x80000000);
439 }
440