Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powermac/bootx_init.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Early boot support code for BootX bootloader
4
*
5
* Copyright (C) 2005 Ben. Herrenschmidt ([email protected])
6
*/
7
8
#include <linux/kernel.h>
9
#include <linux/string.h>
10
#include <linux/init.h>
11
#include <linux/of_fdt.h>
12
#include <generated/utsrelease.h>
13
#include <asm/sections.h>
14
#include <asm/prom.h>
15
#include <asm/page.h>
16
#include <asm/bootx.h>
17
#include <asm/btext.h>
18
#include <asm/io.h>
19
#include <asm/setup.h>
20
21
#undef DEBUG
22
#define SET_BOOT_BAT
23
24
#ifdef DEBUG
25
#define DBG(fmt...) do { bootx_printf(fmt); } while(0)
26
#else
27
#define DBG(fmt...) do { } while(0)
28
#endif
29
30
extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);
31
32
static unsigned long __initdata bootx_dt_strbase;
33
static unsigned long __initdata bootx_dt_strend;
34
static unsigned long __initdata bootx_node_chosen;
35
static boot_infos_t * __initdata bootx_info;
36
static char __initdata bootx_disp_path[256];
37
38
/* Is boot-info compatible ? */
39
#define BOOT_INFO_IS_COMPATIBLE(bi) \
40
((bi)->compatible_version <= BOOT_INFO_VERSION)
41
#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2)
42
#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4)
43
44
#ifdef CONFIG_BOOTX_TEXT
45
static void __init bootx_printf(const char *format, ...)
46
{
47
const char *p, *q, *s;
48
va_list args;
49
unsigned long v;
50
51
va_start(args, format);
52
for (p = format; *p != 0; p = q) {
53
for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
54
;
55
if (q > p)
56
btext_drawtext(p, q - p);
57
if (*q == 0)
58
break;
59
if (*q == '\n') {
60
++q;
61
btext_flushline();
62
btext_drawstring("\r\n");
63
btext_flushline();
64
continue;
65
}
66
++q;
67
if (*q == 0)
68
break;
69
switch (*q) {
70
case 's':
71
++q;
72
s = va_arg(args, const char *);
73
if (s == NULL)
74
s = "<NULL>";
75
btext_drawstring(s);
76
break;
77
case 'x':
78
++q;
79
v = va_arg(args, unsigned long);
80
btext_drawhex(v);
81
break;
82
}
83
}
84
va_end(args);
85
}
86
#else /* CONFIG_BOOTX_TEXT */
87
static void __init bootx_printf(const char *format, ...) {}
88
#endif /* CONFIG_BOOTX_TEXT */
89
90
static void * __init bootx_early_getprop(unsigned long base,
91
unsigned long node,
92
char *prop)
93
{
94
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
95
u32 *ppp = &np->properties;
96
97
while(*ppp) {
98
struct bootx_dt_prop *pp =
99
(struct bootx_dt_prop *)(base + *ppp);
100
101
if (strcmp((char *)((unsigned long)pp->name + base),
102
prop) == 0) {
103
return (void *)((unsigned long)pp->value + base);
104
}
105
ppp = &pp->next;
106
}
107
return NULL;
108
}
109
110
#define dt_push_token(token, mem) \
111
do { \
112
*(mem) = ALIGN(*(mem),4); \
113
*((u32 *)*(mem)) = token; \
114
*(mem) += 4; \
115
} while(0)
116
117
static unsigned long __init bootx_dt_find_string(char *str)
118
{
119
char *s, *os;
120
121
s = os = (char *)bootx_dt_strbase;
122
s += 4;
123
while (s < (char *)bootx_dt_strend) {
124
if (strcmp(s, str) == 0)
125
return s - os;
126
s += strlen(s) + 1;
127
}
128
return 0;
129
}
130
131
static void __init bootx_dt_add_prop(char *name, void *data, int size,
132
unsigned long *mem_end)
133
{
134
unsigned long soff = bootx_dt_find_string(name);
135
if (data == NULL)
136
size = 0;
137
if (soff == 0) {
138
bootx_printf("WARNING: Can't find string index for <%s>\n",
139
name);
140
return;
141
}
142
if (size > 0x20000) {
143
bootx_printf("WARNING: ignoring large property ");
144
bootx_printf("%s length 0x%x\n", name, size);
145
return;
146
}
147
dt_push_token(OF_DT_PROP, mem_end);
148
dt_push_token(size, mem_end);
149
dt_push_token(soff, mem_end);
150
151
/* push property content */
152
if (size && data) {
153
memcpy((void *)*mem_end, data, size);
154
*mem_end = ALIGN(*mem_end + size, 4);
155
}
156
}
157
158
static void __init bootx_add_chosen_props(unsigned long base,
159
unsigned long *mem_end)
160
{
161
u32 val;
162
163
bootx_dt_add_prop("linux,bootx", NULL, 0, mem_end);
164
165
if (bootx_info->kernelParamsOffset) {
166
char *args = (char *)((unsigned long)bootx_info) +
167
bootx_info->kernelParamsOffset;
168
bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end);
169
}
170
if (bootx_info->ramDisk) {
171
val = ((unsigned long)bootx_info) + bootx_info->ramDisk;
172
bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end);
173
val += bootx_info->ramDiskSize;
174
bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end);
175
}
176
if (strlen(bootx_disp_path))
177
bootx_dt_add_prop("linux,stdout-path", bootx_disp_path,
178
strlen(bootx_disp_path) + 1, mem_end);
179
}
180
181
static void __init bootx_add_display_props(unsigned long base,
182
unsigned long *mem_end,
183
int has_real_node)
184
{
185
boot_infos_t *bi = bootx_info;
186
u32 tmp;
187
188
if (has_real_node) {
189
bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end);
190
bootx_dt_add_prop("linux,opened", NULL, 0, mem_end);
191
} else
192
bootx_dt_add_prop("linux,bootx-noscreen", NULL, 0, mem_end);
193
194
tmp = bi->dispDeviceDepth;
195
bootx_dt_add_prop("linux,bootx-depth", &tmp, 4, mem_end);
196
tmp = bi->dispDeviceRect[2] - bi->dispDeviceRect[0];
197
bootx_dt_add_prop("linux,bootx-width", &tmp, 4, mem_end);
198
tmp = bi->dispDeviceRect[3] - bi->dispDeviceRect[1];
199
bootx_dt_add_prop("linux,bootx-height", &tmp, 4, mem_end);
200
tmp = bi->dispDeviceRowBytes;
201
bootx_dt_add_prop("linux,bootx-linebytes", &tmp, 4, mem_end);
202
tmp = (u32)bi->dispDeviceBase;
203
if (tmp == 0)
204
tmp = (u32)bi->logicalDisplayBase;
205
tmp += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes;
206
tmp += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8);
207
bootx_dt_add_prop("linux,bootx-addr", &tmp, 4, mem_end);
208
}
209
210
static void __init bootx_dt_add_string(char *s, unsigned long *mem_end)
211
{
212
unsigned int l = strlen(s) + 1;
213
memcpy((void *)*mem_end, s, l);
214
bootx_dt_strend = *mem_end = *mem_end + l;
215
}
216
217
static void __init bootx_scan_dt_build_strings(unsigned long base,
218
unsigned long node,
219
unsigned long *mem_end)
220
{
221
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
222
u32 *cpp, *ppp = &np->properties;
223
unsigned long soff;
224
char *namep;
225
226
/* Keep refs to known nodes */
227
namep = np->full_name ? (char *)(base + np->full_name) : NULL;
228
if (namep == NULL) {
229
bootx_printf("Node without a full name !\n");
230
namep = "";
231
}
232
DBG("* strings: %s\n", namep);
233
234
if (!strcmp(namep, "/chosen")) {
235
DBG(" detected /chosen ! adding properties names !\n");
236
bootx_dt_add_string("linux,bootx", mem_end);
237
bootx_dt_add_string("linux,stdout-path", mem_end);
238
bootx_dt_add_string("linux,initrd-start", mem_end);
239
bootx_dt_add_string("linux,initrd-end", mem_end);
240
bootx_dt_add_string("bootargs", mem_end);
241
bootx_node_chosen = node;
242
}
243
if (node == bootx_info->dispDeviceRegEntryOffset) {
244
DBG(" detected display ! adding properties names !\n");
245
bootx_dt_add_string("linux,boot-display", mem_end);
246
bootx_dt_add_string("linux,opened", mem_end);
247
strscpy(bootx_disp_path, namep, sizeof(bootx_disp_path));
248
}
249
250
/* get and store all property names */
251
while (*ppp) {
252
struct bootx_dt_prop *pp =
253
(struct bootx_dt_prop *)(base + *ppp);
254
255
namep = pp->name ? (char *)(base + pp->name) : NULL;
256
if (namep == NULL || strcmp(namep, "name") == 0)
257
goto next;
258
/* get/create string entry */
259
soff = bootx_dt_find_string(namep);
260
if (soff == 0)
261
bootx_dt_add_string(namep, mem_end);
262
next:
263
ppp = &pp->next;
264
}
265
266
/* do all our children */
267
cpp = &np->child;
268
while(*cpp) {
269
np = (struct bootx_dt_node *)(base + *cpp);
270
bootx_scan_dt_build_strings(base, *cpp, mem_end);
271
cpp = &np->sibling;
272
}
273
}
274
275
static void __init bootx_scan_dt_build_struct(unsigned long base,
276
unsigned long node,
277
unsigned long *mem_end)
278
{
279
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
280
u32 *cpp, *ppp = &np->properties;
281
char *namep, *p, *ep, *lp;
282
int l;
283
284
dt_push_token(OF_DT_BEGIN_NODE, mem_end);
285
286
/* get the node's full name */
287
namep = np->full_name ? (char *)(base + np->full_name) : NULL;
288
if (namep == NULL)
289
namep = "";
290
l = strlen(namep);
291
292
DBG("* struct: %s\n", namep);
293
294
/* Fixup an Apple bug where they have bogus \0 chars in the
295
* middle of the path in some properties, and extract
296
* the unit name (everything after the last '/').
297
*/
298
memcpy((void *)*mem_end, namep, l + 1);
299
namep = (char *)*mem_end;
300
for (lp = p = namep, ep = namep + l; p < ep; p++) {
301
if (*p == '/')
302
lp = namep;
303
else if (*p != 0)
304
*lp++ = *p;
305
}
306
*lp = 0;
307
*mem_end = ALIGN((unsigned long)lp + 1, 4);
308
309
/* get and store all properties */
310
while (*ppp) {
311
struct bootx_dt_prop *pp =
312
(struct bootx_dt_prop *)(base + *ppp);
313
314
namep = pp->name ? (char *)(base + pp->name) : NULL;
315
/* Skip "name" */
316
if (namep == NULL || !strcmp(namep, "name"))
317
goto next;
318
/* Skip "bootargs" in /chosen too as we replace it */
319
if (node == bootx_node_chosen && !strcmp(namep, "bootargs"))
320
goto next;
321
322
/* push property head */
323
bootx_dt_add_prop(namep,
324
pp->value ? (void *)(base + pp->value): NULL,
325
pp->length, mem_end);
326
next:
327
ppp = &pp->next;
328
}
329
330
if (node == bootx_node_chosen) {
331
bootx_add_chosen_props(base, mem_end);
332
if (bootx_info->dispDeviceRegEntryOffset == 0)
333
bootx_add_display_props(base, mem_end, 0);
334
}
335
else if (node == bootx_info->dispDeviceRegEntryOffset)
336
bootx_add_display_props(base, mem_end, 1);
337
338
/* do all our children */
339
cpp = &np->child;
340
while(*cpp) {
341
np = (struct bootx_dt_node *)(base + *cpp);
342
bootx_scan_dt_build_struct(base, *cpp, mem_end);
343
cpp = &np->sibling;
344
}
345
346
dt_push_token(OF_DT_END_NODE, mem_end);
347
}
348
349
static unsigned long __init bootx_flatten_dt(unsigned long start)
350
{
351
boot_infos_t *bi = bootx_info;
352
unsigned long mem_start, mem_end;
353
struct boot_param_header *hdr;
354
unsigned long base;
355
u64 *rsvmap;
356
357
/* Start using memory after the big blob passed by BootX, get
358
* some space for the header
359
*/
360
mem_start = mem_end = ALIGN(((unsigned long)bi) + start, 4);
361
DBG("Boot params header at: %x\n", mem_start);
362
hdr = (struct boot_param_header *)mem_start;
363
mem_end += sizeof(struct boot_param_header);
364
rsvmap = (u64 *)(ALIGN(mem_end, 8));
365
hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start;
366
mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64);
367
368
/* Get base of tree */
369
base = ((unsigned long)bi) + bi->deviceTreeOffset;
370
371
/* Build string array */
372
DBG("Building string array at: %x\n", mem_end);
373
DBG("Device Tree Base=%x\n", base);
374
bootx_dt_strbase = mem_end;
375
mem_end += 4;
376
bootx_dt_strend = mem_end;
377
bootx_scan_dt_build_strings(base, 4, &mem_end);
378
/* Add some strings */
379
bootx_dt_add_string("linux,bootx-noscreen", &mem_end);
380
bootx_dt_add_string("linux,bootx-depth", &mem_end);
381
bootx_dt_add_string("linux,bootx-width", &mem_end);
382
bootx_dt_add_string("linux,bootx-height", &mem_end);
383
bootx_dt_add_string("linux,bootx-linebytes", &mem_end);
384
bootx_dt_add_string("linux,bootx-addr", &mem_end);
385
/* Wrap up strings */
386
hdr->off_dt_strings = bootx_dt_strbase - mem_start;
387
hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase;
388
389
/* Build structure */
390
mem_end = ALIGN(mem_end, 16);
391
DBG("Building device tree structure at: %x\n", mem_end);
392
hdr->off_dt_struct = mem_end - mem_start;
393
bootx_scan_dt_build_struct(base, 4, &mem_end);
394
dt_push_token(OF_DT_END, &mem_end);
395
396
/* Finish header */
397
hdr->boot_cpuid_phys = 0;
398
hdr->magic = OF_DT_HEADER;
399
hdr->totalsize = mem_end - mem_start;
400
hdr->version = OF_DT_VERSION;
401
/* Version 16 is not backward compatible */
402
hdr->last_comp_version = 0x10;
403
404
/* Reserve the whole thing and copy the reserve map in, we
405
* also bump mem_reserve_cnt to cause further reservations to
406
* fail since it's too late.
407
*/
408
mem_end = ALIGN(mem_end, PAGE_SIZE);
409
DBG("End of boot params: %x\n", mem_end);
410
rsvmap[0] = mem_start;
411
rsvmap[1] = mem_end;
412
if (bootx_info->ramDisk) {
413
rsvmap[2] = ((unsigned long)bootx_info) + bootx_info->ramDisk;
414
rsvmap[3] = rsvmap[2] + bootx_info->ramDiskSize;
415
rsvmap[4] = 0;
416
rsvmap[5] = 0;
417
} else {
418
rsvmap[2] = 0;
419
rsvmap[3] = 0;
420
}
421
422
return (unsigned long)hdr;
423
}
424
425
426
#ifdef CONFIG_BOOTX_TEXT
427
static void __init btext_welcome(boot_infos_t *bi)
428
{
429
unsigned long flags;
430
unsigned long pvr;
431
432
bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n");
433
bootx_printf("\nlinked at : 0x%x", KERNELBASE);
434
bootx_printf("\nframe buffer at : 0x%x", bi->dispDeviceBase);
435
bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase);
436
bootx_printf(" (log)");
437
bootx_printf("\nklimit : 0x%x",(unsigned long)_end);
438
bootx_printf("\nboot_info at : 0x%x", bi);
439
__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
440
bootx_printf("\nMSR : 0x%x", flags);
441
__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
442
bootx_printf("\nPVR : 0x%x", pvr);
443
pvr >>= 16;
444
if (pvr > 1) {
445
__asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
446
bootx_printf("\nHID0 : 0x%x", flags);
447
}
448
if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
449
__asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
450
bootx_printf("\nICTC : 0x%x", flags);
451
}
452
#ifdef DEBUG
453
bootx_printf("\n\n");
454
bootx_printf("bi->deviceTreeOffset : 0x%x\n",
455
bi->deviceTreeOffset);
456
bootx_printf("bi->deviceTreeSize : 0x%x\n",
457
bi->deviceTreeSize);
458
#endif
459
bootx_printf("\n\n");
460
}
461
#endif /* CONFIG_BOOTX_TEXT */
462
463
void __init bootx_init(unsigned long r3, unsigned long r4)
464
{
465
boot_infos_t *bi = (boot_infos_t *) r4;
466
unsigned long hdr;
467
unsigned long space;
468
unsigned long ptr;
469
char *model;
470
unsigned long offset = reloc_offset();
471
472
reloc_got2(offset);
473
474
bootx_info = bi;
475
476
/* We haven't cleared any bss at this point, make sure
477
* what we need is initialized
478
*/
479
bootx_dt_strbase = bootx_dt_strend = 0;
480
bootx_node_chosen = 0;
481
bootx_disp_path[0] = 0;
482
483
if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
484
bi->logicalDisplayBase = bi->dispDeviceBase;
485
486
/* Fixup depth 16 -> 15 as that's what MacOS calls 16bpp */
487
if (bi->dispDeviceDepth == 16)
488
bi->dispDeviceDepth = 15;
489
490
491
#ifdef CONFIG_BOOTX_TEXT
492
ptr = (unsigned long)bi->logicalDisplayBase;
493
ptr += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes;
494
ptr += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8);
495
btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0],
496
bi->dispDeviceRect[3] - bi->dispDeviceRect[1],
497
bi->dispDeviceDepth, bi->dispDeviceRowBytes,
498
(unsigned long)bi->logicalDisplayBase);
499
btext_clearscreen();
500
btext_flushscreen();
501
#endif /* CONFIG_BOOTX_TEXT */
502
503
/*
504
* Test if boot-info is compatible. Done only in config
505
* CONFIG_BOOTX_TEXT since there is nothing much we can do
506
* with an incompatible version, except display a message
507
* and eventually hang the processor...
508
*
509
* I'll try to keep enough of boot-info compatible in the
510
* future to always allow display of this message;
511
*/
512
if (!BOOT_INFO_IS_COMPATIBLE(bi)) {
513
bootx_printf(" !!! WARNING - Incompatible version"
514
" of BootX !!!\n\n\n");
515
for (;;)
516
;
517
}
518
if (bi->architecture != BOOT_ARCH_PCI) {
519
bootx_printf(" !!! WARNING - Unsupported machine"
520
" architecture !\n");
521
for (;;)
522
;
523
}
524
525
#ifdef CONFIG_BOOTX_TEXT
526
btext_welcome(bi);
527
#endif
528
529
/* New BootX enters kernel with MMU off, i/os are not allowed
530
* here. This hack will have been done by the boostrap anyway.
531
*/
532
if (bi->version < 4) {
533
/*
534
* XXX If this is an iMac, turn off the USB controller.
535
*/
536
model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset,
537
4, "model");
538
if (model
539
&& (strcmp(model, "iMac,1") == 0
540
|| strcmp(model, "PowerMac1,1") == 0)) {
541
bootx_printf("iMac,1 detected, shutting down USB\n");
542
out_le32((unsigned __iomem *)0x80880008, 1); /* XXX */
543
}
544
}
545
546
/* Get a pointer that points above the device tree, args, ramdisk,
547
* etc... to use for generating the flattened tree
548
*/
549
if (bi->version < 5) {
550
space = bi->deviceTreeOffset + bi->deviceTreeSize;
551
if (bi->ramDisk >= space)
552
space = bi->ramDisk + bi->ramDiskSize;
553
} else
554
space = bi->totalParamsSize;
555
556
bootx_printf("Total space used by parameters & ramdisk: 0x%x\n", space);
557
558
/* New BootX will have flushed all TLBs and enters kernel with
559
* MMU switched OFF, so this should not be useful anymore.
560
*/
561
if (bi->version < 4) {
562
unsigned long x __maybe_unused;
563
564
bootx_printf("Touching pages...\n");
565
566
/*
567
* Touch each page to make sure the PTEs for them
568
* are in the hash table - the aim is to try to avoid
569
* getting DSI exceptions while copying the kernel image.
570
*/
571
for (ptr = ((unsigned long) &_stext) & PAGE_MASK;
572
ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
573
x = *(volatile unsigned long *)ptr;
574
}
575
576
/* Ok, now we need to generate a flattened device-tree to pass
577
* to the kernel
578
*/
579
bootx_printf("Preparing boot params...\n");
580
581
hdr = bootx_flatten_dt(space);
582
583
#ifdef CONFIG_BOOTX_TEXT
584
#ifdef SET_BOOT_BAT
585
bootx_printf("Preparing BAT...\n");
586
btext_prepare_BAT();
587
#else
588
btext_unmap();
589
#endif
590
#endif
591
592
reloc_got2(-offset);
593
594
__start(hdr, KERNELBASE + offset, 0);
595
}
596
597