// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Procedures for interfacing to Open Firmware .
*
* Paul Mackerras August 1996 .
* Copyright ( C ) 1996 - 2005 Paul Mackerras .
*
* Adapted for 64 bit PowerPC by Dave Engebretsen and Peter Bergner .
* { engebret | bergner } @ us . ibm . com
*/
#undef DEBUG_PROM
/* we cannot use FORTIFY as it brings in new symbols */
#define __NO_FORTIFY
#include <linux/stdarg.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/initrd.h>
#include <linux/bitops.h>
#include <linux/pgtable.h>
#include <linux/printk.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <asm /prom.h>
#include <asm /rtas.h>
#include <asm /page.h>
#include <asm /processor.h>
#include <asm /interrupt.h>
#include <asm /irq.h>
#include <asm /io.h>
#include <asm /smp.h>
#include <asm /mmu.h>
#include <asm /iommu.h>
#include <asm /btext.h>
#include <asm /sections.h>
#include <asm /setup.h>
#include <asm /asm -prototypes.h>
#include <asm /ultravisor-api.h>
#include <linux/linux_logo.h>
/* All of prom_init bss lives here */
#define __prombss __section(".bss.prominit" )
/*
* Eventually bump that one up
*/
#define DEVTREE_CHUNK_SIZE 0 x100000
/*
* This is the size of the local memory reserve map that gets copied
* into the boot params passed to the kernel . That size is totally
* flexible as the kernel just reads the list until it encounters an
* entry with size 0 , so it can be changed without breaking binary
* compatibility
*/
#define MEM_RESERVE_MAP_SIZE 8
/*
* prom_init ( ) is called very early on , before the kernel text
* and data have been mapped to KERNELBASE . At this point the code
* is running at whatever address it has been loaded at .
* On ppc32 we compile with - mrelocatable , which means that references
* to extern and static variables get relocated automatically .
* ppc64 objects are always relocatable , we just need to relocate the
* TOC .
*
* Because OF may have mapped I / O devices into the area starting at
* KERNELBASE , particularly on CHRP machines , we can ' t safely call
* OF once the kernel has been mapped to KERNELBASE . Therefore all
* OF calls must be done within prom_init ( ) .
*
* ADDR is used in calls to call_prom . The 4 th and following
* arguments to call_prom should be 32 - bit values .
* On ppc64 , 64 bit values are truncated to 32 bits ( and
* fortunately don ' t get interpreted as two arguments ) .
*/
#define ADDR(x) (u32)(unsigned long )(x)
#ifdef CONFIG_PPC64
#define OF_WORKAROUNDS 0
#else
#define OF_WORKAROUNDS of_workarounds
static int of_workarounds __prombss;
#endif
#define OF_WA_CLAIM 1 /* do phys/virt claim separately, then map */
#define OF_WA_LONGTRAIL 2 /* work around longtrail bugs */
#ifdef DEBUG_PROM
#define prom_debug(x...) prom_printf(x)
#else
#define prom_debug(x...) do { } while (0 )
#endif
typedef u32 prom_arg_t;
struct prom_args {
__be32 service;
__be32 nargs;
__be32 nret;
__be32 args[10 ];
};
struct prom_t {
ihandle root;
phandle chosen;
int cpu;
ihandle stdout;
ihandle mmumap;
ihandle memory;
};
struct mem_map_entry {
__be64 base;
__be64 size;
};
typedef __be32 cell_t;
extern void __start(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7, unsigned long r8,
unsigned long r9);
#ifdef CONFIG_PPC64
extern int enter_prom(struct prom_args *args, unsigned long entry);
#else
static inline int enter_prom(struct prom_args *args, unsigned long entry)
{
return ((int (*)(struct prom_args *))entry)(args);
}
#endif
extern void copy_and_flush(unsigned long dest, unsigned long src,
unsigned long size, unsigned long offset);
/* prom structure */
static struct prom_t __prombss prom;
static unsigned long __prombss prom_entry;
static char __prombss of_stdout_device[256 ];
static char __prombss prom_scratch[256 ];
static unsigned long __prombss dt_header_start;
static unsigned long __prombss dt_struct_start, dt_struct_end;
static unsigned long __prombss dt_string_start, dt_string_end;
static unsigned long __prombss prom_initrd_start, prom_initrd_end;
#ifdef CONFIG_PPC64
static int __prombss prom_iommu_force_on;
static int __prombss prom_iommu_off;
static unsigned long __prombss prom_tce_alloc_start;
static unsigned long __prombss prom_tce_alloc_end;
#endif
#ifdef CONFIG_PPC_PSERIES
static bool __prombss prom_radix_disable;
static bool __prombss prom_radix_gtse_disable;
static bool __prombss prom_xive_disable;
#endif
#ifdef CONFIG_PPC_SVM
static bool __prombss prom_svm_enable;
#endif
struct platform_support {
bool hash_mmu;
bool radix_mmu;
bool radix_gtse;
bool xive;
};
/* Platforms codes are now obsolete in the kernel. Now only used within this
* file and ultimately gone too . Feel free to change them if you need , they
* are not shared with anything outside of this file anymore
*/
#define PLATFORM_PSERIES 0 x0100
#define PLATFORM_PSERIES_LPAR 0 x0101
#define PLATFORM_LPAR 0 x0001
#define PLATFORM_POWERMAC 0 x0400
#define PLATFORM_GENERIC 0 x0500
static int __prombss of_platform;
static char __prombss prom_cmd_line[COMMAND_LINE_SIZE];
static unsigned long __prombss prom_memory_limit;
static unsigned long __prombss alloc_top;
static unsigned long __prombss alloc_top_high;
static unsigned long __prombss alloc_bottom;
static unsigned long __prombss rmo_top;
static unsigned long __prombss ram_top;
static struct mem_map_entry __prombss mem_reserve_map[MEM_RESERVE_MAP_SIZE];
static int __prombss mem_reserve_cnt;
static cell_t __prombss regbuf[1024 ];
static bool __prombss rtas_has_query_cpu_stopped;
/*
* Error results . . . some OF calls will return " - 1 " on error , some
* will return 0 , some will return either . To simplify , here are
* macros to use with any ihandle or phandle return value to check if
* it is valid
*/
#define PROM_ERROR (-1 u)
#define PHANDLE_VALID(p) ((p) != 0 && (p) != PROM_ERROR)
#define IHANDLE_VALID(i) ((i) != 0 && (i) != PROM_ERROR)
/* Copied from lib/string.c and lib/kstrtox.c */
static int __init prom_strcmp(const char *cs, const char *ct)
{
unsigned char c1, c2;
while (1 ) {
c1 = *cs++;
c2 = *ct++;
if (c1 != c2)
return c1 < c2 ? -1 : 1 ;
if (!c1)
break ;
}
return 0 ;
}
static ssize_t __init prom_strscpy_pad(char *dest, const char *src, size_t n)
{
ssize_t rc;
size_t i;
if (n == 0 || n > INT_MAX)
return -E2BIG;
// Copy up to n bytes
for (i = 0 ; i < n && src[i] != '\0' ; i++)
dest[i] = src[i];
rc = i;
// If we copied all n then we have run out of space for the nul
if (rc == n) {
// Rewind by one character to ensure nul termination
i--;
rc = -E2BIG;
}
for (; i < n; i++)
dest[i] = '\0' ;
return rc;
}
static int __init prom_strncmp(const char *cs, const char *ct, size_t count)
{
unsigned char c1, c2;
while (count) {
c1 = *cs++;
c2 = *ct++;
if (c1 != c2)
return c1 < c2 ? -1 : 1 ;
if (!c1)
break ;
count--;
}
return 0 ;
}
static size_t __init prom_strlen(const char *s)
{
const char *sc;
for (sc = s; *sc != '\0' ; ++sc)
/* nothing */;
return sc - s;
}
static int __init prom_memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0 ;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0 )
break ;
return res;
}
static char __init *prom_strstr(const char *s1, const char *s2)
{
size_t l1, l2;
l2 = prom_strlen(s2);
if (!l2)
return (char *)s1;
l1 = prom_strlen(s1);
while (l1 >= l2) {
l1--;
if (!prom_memcmp(s1, s2, l2))
return (char *)s1;
s1++;
}
return NULL;
}
static size_t __init prom_strlcat(char *dest, const char *src, size_t count)
{
size_t dsize = prom_strlen(dest);
size_t len = prom_strlen(src);
size_t res = dsize + len;
/* This would be a bug */
if (dsize >= count)
return count;
dest += dsize;
count -= dsize;
if (len >= count)
len = count-1 ;
memcpy(dest, src, len);
dest[len] = 0 ;
return res;
}
#ifdef CONFIG_PPC_PSERIES
static int __init prom_strtobool(const char *s, bool *res)
{
if (!s)
return -EINVAL;
switch (s[0 ]) {
case 'y' :
case 'Y' :
case '1' :
*res = true ;
return 0 ;
case 'n' :
case 'N' :
case '0' :
*res = false ;
return 0 ;
case 'o' :
case 'O' :
switch (s[1 ]) {
case 'n' :
case 'N' :
*res = true ;
return 0 ;
case 'f' :
case 'F' :
*res = false ;
return 0 ;
default :
break ;
}
break ;
default :
break ;
}
return -EINVAL;
}
#endif
/* This is the one and *ONLY* place where we actually call open
* firmware .
*/
static int __init call_prom(const char *service, int nargs, int nret, ...)
{
int i;
struct prom_args args;
va_list list;
args.service = cpu_to_be32(ADDR(service));
args.nargs = cpu_to_be32(nargs);
args.nret = cpu_to_be32(nret);
va_start(list, nret);
for (i = 0 ; i < nargs; i++)
args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t));
va_end(list);
for (i = 0 ; i < nret; i++)
args.args[nargs+i] = 0 ;
if (enter_prom(&args, prom_entry) < 0 )
return PROM_ERROR;
return (nret > 0 ) ? be32_to_cpu(args.args[nargs]) : 0 ;
}
static int __init call_prom_ret(const char *service, int nargs, int nret,
prom_arg_t *rets, ...)
{
int i;
struct prom_args args;
va_list list;
args.service = cpu_to_be32(ADDR(service));
args.nargs = cpu_to_be32(nargs);
args.nret = cpu_to_be32(nret);
va_start(list, rets);
for (i = 0 ; i < nargs; i++)
args.args[i] = cpu_to_be32(va_arg(list, prom_arg_t));
va_end(list);
for (i = 0 ; i < nret; i++)
args.args[nargs+i] = 0 ;
if (enter_prom(&args, prom_entry) < 0 )
return PROM_ERROR;
if (rets != NULL)
for (i = 1 ; i < nret; ++i)
rets[i-1 ] = be32_to_cpu(args.args[nargs+i]);
return (nret > 0 ) ? be32_to_cpu(args.args[nargs]) : 0 ;
}
static void __init prom_print(const char *msg)
{
const char *p, *q;
if (prom.stdout == 0 )
return ;
for (p = msg; *p != 0 ; p = q) {
for (q = p; *q != 0 && *q != '\n' ; ++q)
;
if (q > p)
call_prom("write" , 3 , 1 , prom.stdout, p, q - p);
if (*q == 0 )
break ;
++q;
call_prom("write" , 3 , 1 , prom.stdout, ADDR("\r\n" ), 2 );
}
}
/*
* Both prom_print_hex & prom_print_dec takes an unsigned long as input so that
* we do not need _ _ udivdi3 or _ _ umoddi3 on 32 bits .
*/
static void __init prom_print_hex(unsigned long val)
{
int i, nibbles = sizeof (val)*2 ;
char buf[sizeof (val)*2 +1 ];
for (i = nibbles-1 ; i >= 0 ; i--) {
buf[i] = (val & 0 xf) + '0' ;
if (buf[i] > '9' )
buf[i] += ('a' -'0' -10 );
val >>= 4 ;
}
buf[nibbles] = '\0' ;
call_prom("write" , 3 , 1 , prom.stdout, buf, nibbles);
}
/* max number of decimal digits in an unsigned long */
#define UL_DIGITS 21
static void __init prom_print_dec(unsigned long val)
{
int i, size;
char buf[UL_DIGITS+1 ];
for (i = UL_DIGITS-1 ; i >= 0 ; i--) {
buf[i] = (val % 10 ) + '0' ;
val = val/10 ;
if (val == 0 )
break ;
}
/* shift stuff down */
size = UL_DIGITS - i;
call_prom("write" , 3 , 1 , prom.stdout, buf+i, size);
}
__printf(1 , 2 )
static void __init prom_printf(const char *format, ...)
{
const char *p, *q, *s;
va_list args;
unsigned long v;
long vs;
int n = 0 ;
va_start(args, format);
for (p = format; *p != 0 ; p = q) {
for (q = p; *q != 0 && *q != '\n' && *q != '%' ; ++q)
;
if (q > p)
call_prom("write" , 3 , 1 , prom.stdout, p, q - p);
if (*q == 0 )
break ;
if (*q == '\n' ) {
++q;
call_prom("write" , 3 , 1 , prom.stdout,
ADDR("\r\n" ), 2 );
continue ;
}
++q;
if (*q == 0 )
break ;
while (*q == 'l' ) {
++q;
++n;
}
switch (*q) {
case 's' :
++q;
s = va_arg(args, const char *);
prom_print(s);
break ;
case 'x' :
++q;
switch (n) {
case 0 :
v = va_arg(args, unsigned int );
break ;
case 1 :
v = va_arg(args, unsigned long );
break ;
case 2 :
default :
v = va_arg(args, unsigned long long );
break ;
}
prom_print_hex(v);
break ;
case 'u' :
++q;
switch (n) {
case 0 :
v = va_arg(args, unsigned int );
break ;
case 1 :
v = va_arg(args, unsigned long );
break ;
case 2 :
default :
v = va_arg(args, unsigned long long );
break ;
}
prom_print_dec(v);
break ;
case 'd' :
++q;
switch (n) {
case 0 :
vs = va_arg(args, int );
break ;
case 1 :
vs = va_arg(args, long );
break ;
case 2 :
default :
vs = va_arg(args, long long );
break ;
}
if (vs < 0 ) {
prom_print("-" );
vs = -vs;
}
prom_print_dec(vs);
break ;
}
}
va_end(args);
}
static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
unsigned long align)
{
if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) {
/*
* Old OF requires we claim physical and virtual separately
* and then map explicitly ( assuming virtual mode )
*/
int ret;
prom_arg_t result;
ret = call_prom_ret("call-method" , 5 , 2 , &result,
ADDR("claim" ), prom.memory,
align, size, virt);
if (ret != 0 || result == -1 )
return -1 ;
ret = call_prom_ret("call-method" , 5 , 2 , &result,
ADDR("claim" ), prom.mmumap,
align, size, virt);
if (ret != 0 ) {
call_prom("call-method" , 4 , 1 , ADDR("release" ),
prom.memory, size, virt);
return -1 ;
}
/* the 0x12 is M (coherence) + PP == read/write */
call_prom("call-method" , 6 , 1 ,
ADDR("map" ), prom.mmumap, 0 x12, size, virt, virt);
return virt;
}
return call_prom("claim" , 3 , 1 , (prom_arg_t)virt, (prom_arg_t)size,
(prom_arg_t)align);
}
static void __init __attribute__((noreturn)) prom_panic(const char *reason)
{
prom_print(reason);
/* Do not call exit because it clears the screen on pmac
* it also causes some sort of double-fault on early pmacs */
if (of_platform == PLATFORM_POWERMAC)
asm ("trap\n" );
/* ToDo: should put up an SRC here on pSeries */
call_prom("exit" , 0 , 0 );
for (;;) /* should never get here */
;
}
static int __init prom_next_node(phandle *nodep)
{
phandle node;
if ((node = *nodep) != 0
&& (*nodep = call_prom("child" , 1 , 1 , node)) != 0 )
return 1 ;
if ((*nodep = call_prom("peer" , 1 , 1 , node)) != 0 )
return 1 ;
for (;;) {
if ((node = call_prom("parent" , 1 , 1 , node)) == 0 )
return 0 ;
if ((*nodep = call_prom("peer" , 1 , 1 , node)) != 0 )
return 1 ;
}
}
static inline int __init prom_getprop(phandle node, const char *pname,
void *value, size_t valuelen)
{
return call_prom("getprop" , 4 , 1 , node, ADDR(pname),
(u32)(unsigned long ) value, (u32) valuelen);
}
static inline int __init prom_getproplen(phandle node, const char *pname)
{
return call_prom("getproplen" , 2 , 1 , node, ADDR(pname));
}
static void __init add_string(char **str, const char *q)
{
char *p = *str;
while (*q)
*p++ = *q++;
*p++ = ' ' ;
*str = p;
}
static char *__init tohex(unsigned int x)
{
static const char digits[] __initconst = "0123456789abcdef" ;
static char result[9 ] __prombss;
int i;
result[8 ] = 0 ;
i = 8 ;
do {
--i;
result[i] = digits[x & 0 xf];
x >>= 4 ;
} while (x != 0 && i > 0 );
return &result[i];
}
static int __init prom_setprop(phandle node, const char *nodename,
const char *pname, void *value, size_t valuelen)
{
char cmd[256 ], *p;
if (!(OF_WORKAROUNDS & OF_WA_LONGTRAIL))
return call_prom("setprop" , 4 , 1 , node, ADDR(pname),
(u32)(unsigned long ) value, (u32) valuelen);
/* gah... setprop doesn't work on longtrail, have to use interpret */
p = cmd;
add_string(&p, "dev" );
add_string(&p, nodename);
add_string(&p, tohex((u32)(unsigned long ) value));
add_string(&p, tohex(valuelen));
add_string(&p, tohex(ADDR(pname)));
add_string(&p, tohex(prom_strlen(pname)));
add_string(&p, "property" );
*p = 0 ;
return call_prom("interpret" , 1 , 1 , (u32)(unsigned long ) cmd);
}
/* We can't use the standard versions because of relocation headaches. */
#define prom_isxdigit(c) \
(('0' <= (c) && (c) <= '9' ) || ('a' <= (c) && (c) <= 'f' ) || ('A' <= (c) && (c) <= 'F' ))
#define prom_isdigit(c) ('0' <= (c) && (c) <= '9' )
#define prom_islower(c) ('a' <= (c) && (c) <= 'z' )
#define prom_toupper(c) (prom_islower(c) ? ((c) - 'a' + 'A' ) : (c))
static unsigned long __init prom_strtoul(const char *cp, const char **endp)
{
unsigned long result = 0 , base = 10 , value;
if (*cp == '0' ) {
base = 8 ;
cp++;
if (prom_toupper(*cp) == 'X' ) {
cp++;
base = 16 ;
}
}
while (prom_isxdigit(*cp) &&
(value = prom_isdigit(*cp) ? *cp - '0' : prom_toupper(*cp) - 'A' + 10 ) < base) {
result = result * base + value;
cp++;
}
if (endp)
*endp = cp;
return result;
}
static unsigned long __init prom_memparse(const char *ptr, const char **retptr)
{
unsigned long ret = prom_strtoul(ptr, retptr);
int shift = 0 ;
/*
* We can ' t use a switch here because GCC * may * generate a
* jump table which won ' t work , because we ' re not running at
* the address we ' re linked at .
*/
if ('G' == **retptr || 'g' == **retptr)
shift = 30 ;
if ('M' == **retptr || 'm' == **retptr)
shift = 20 ;
if ('K' == **retptr || 'k' == **retptr)
shift = 10 ;
if (shift) {
ret <<= shift;
(*retptr)++;
}
return ret;
}
/*
* Early parsing of the command line passed to the kernel , used for
* " mem = x " and the options that affect the iommu
*/
static void __init early_cmdline_parse(void )
{
const char *opt;
char *p;
int l = 0 ;
prom_cmd_line[0 ] = 0 ;
p = prom_cmd_line;
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && (long )prom.chosen > 0 )
l = prom_getprop(prom.chosen, "bootargs" , p, COMMAND_LINE_SIZE-1 );
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || l <= 0 || p[0 ] == '\0' )
prom_strlcat(prom_cmd_line, " " CONFIG_CMDLINE,
sizeof (prom_cmd_line));
prom_printf("command line: %s\n" , prom_cmd_line);
#ifdef CONFIG_PPC64
opt = prom_strstr(prom_cmd_line, "iommu=" );
if (opt) {
prom_printf("iommu opt is: %s\n" , opt);
opt += 6 ;
while (*opt && *opt == ' ' )
opt++;
if (!prom_strncmp(opt, "off" , 3 ))
prom_iommu_off = 1 ;
else if (!prom_strncmp(opt, "force" , 5 ))
prom_iommu_force_on = 1 ;
}
#endif
opt = prom_strstr(prom_cmd_line, "mem=" );
if (opt) {
opt += 4 ;
prom_memory_limit = prom_memparse(opt, (const char **)&opt);
#ifdef CONFIG_PPC64
/* Align down to 16 MB which is large page size with hash page translation */
prom_memory_limit = ALIGN_DOWN(prom_memory_limit, SZ_16M);
#endif
}
#ifdef CONFIG_PPC_PSERIES
prom_radix_disable = !IS_ENABLED(CONFIG_PPC_RADIX_MMU_DEFAULT);
opt = prom_strstr(prom_cmd_line, "disable_radix" );
if (opt) {
opt += 13 ;
if (*opt && *opt == '=' ) {
bool val;
if (prom_strtobool(++opt, &val))
prom_radix_disable = false ;
else
prom_radix_disable = val;
} else
prom_radix_disable = true ;
}
if (prom_radix_disable)
prom_debug("Radix disabled from cmdline\n" );
opt = prom_strstr(prom_cmd_line, "radix_hcall_invalidate=on" );
if (opt) {
prom_radix_gtse_disable = true ;
prom_debug("Radix GTSE disabled from cmdline\n" );
}
opt = prom_strstr(prom_cmd_line, "xive=off" );
if (opt) {
prom_xive_disable = true ;
prom_debug("XIVE disabled from cmdline\n" );
}
#endif /* CONFIG_PPC_PSERIES */
#ifdef CONFIG_PPC_SVM
opt = prom_strstr(prom_cmd_line, "svm=" );
if (opt) {
bool val;
opt += sizeof ("svm=" ) - 1 ;
if (!prom_strtobool(opt, &val))
prom_svm_enable = val;
}
#endif /* CONFIG_PPC_SVM */
}
#ifdef CONFIG_PPC_PSERIES
/*
* The architecture vector has an array of PVR mask / value pairs ,
* followed by # option vectors - 1 , followed by the option vectors .
*
* See prom . h for the definition of the bits specified in the
* architecture vector .
*/
/* Firmware expects the value to be n - 1, where n is the # of vectors */
#define NUM_VECTORS(n) ((n) - 1 )
/*
* Firmware expects 1 + n - 2 , where n is the length of the option vector in
* bytes . The 1 accounts for the length byte itself , the - 2 . . ?
*/
#define VECTOR_LENGTH(n) (1 + (n) - 2 )
struct option_vector1 {
u8 byte1;
u8 arch_versions;
u8 arch_versions3;
} __packed;
struct option_vector2 {
u8 byte1;
__be16 reserved;
__be32 real_base;
__be32 real_size;
__be32 virt_base;
__be32 virt_size;
__be32 load_base;
__be32 min_rma;
__be32 min_load;
u8 min_rma_percent;
u8 max_pft_size;
} __packed;
struct option_vector3 {
u8 byte1;
u8 byte2;
} __packed;
struct option_vector4 {
u8 byte1;
u8 min_vp_cap;
} __packed;
struct option_vector5 {
u8 byte1;
u8 byte2;
u8 byte3;
u8 cmo;
u8 associativity;
u8 bin_opts;
u8 micro_checkpoint;
u8 reserved0;
__be32 max_cpus;
__be16 papr_level;
__be16 reserved1;
u8 platform_facilities;
u8 reserved2;
__be16 reserved3;
u8 subprocessors;
u8 byte22;
u8 intarch;
u8 mmu;
u8 hash_ext;
u8 radix_ext;
} __packed;
struct option_vector6 {
u8 reserved;
u8 secondary_pteg;
u8 os_name;
} __packed;
struct option_vector7 {
u8 os_id[256 ];
} __packed;
struct ibm_arch_vec {
struct { __be32 mask, val; } pvrs[16 ];
u8 num_vectors;
u8 vec1_len;
struct option_vector1 vec1;
u8 vec2_len;
struct option_vector2 vec2;
u8 vec3_len;
struct option_vector3 vec3;
u8 vec4_len;
struct option_vector4 vec4;
u8 vec5_len;
struct option_vector5 vec5;
u8 vec6_len;
struct option_vector6 vec6;
u8 vec7_len;
struct option_vector7 vec7;
} __packed;
static const struct ibm_arch_vec ibm_architecture_vec_template __initconst = {
.pvrs = {
{
.mask = cpu_to_be32(0 xfffe0000), /* POWER5/POWER5+ */
.val = cpu_to_be32(0 x003a0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER6 */
.val = cpu_to_be32(0 x003e0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER7 */
.val = cpu_to_be32(0 x003f0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER8E */
.val = cpu_to_be32(0 x004b0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER8NVL */
.val = cpu_to_be32(0 x004c0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER8 */
.val = cpu_to_be32(0 x004d0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER9 */
.val = cpu_to_be32(0 x004e0000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER10 */
.val = cpu_to_be32(0 x00800000),
},
{
.mask = cpu_to_be32(0 xffff0000), /* POWER11 */
.val = cpu_to_be32(0 x00820000),
},
{
.mask = cpu_to_be32(0 xffffffff), /* P11 compliant */
.val = cpu_to_be32(0 x0f000007),
},
{
.mask = cpu_to_be32(0 xffffffff), /* all 3.1-compliant */
.val = cpu_to_be32(0 x0f000006),
},
{
.mask = cpu_to_be32(0 xffffffff), /* all 3.00-compliant */
.val = cpu_to_be32(0 x0f000005),
},
{
.mask = cpu_to_be32(0 xffffffff), /* all 2.07-compliant */
.val = cpu_to_be32(0 x0f000004),
},
{
.mask = cpu_to_be32(0 xffffffff), /* all 2.06-compliant */
.val = cpu_to_be32(0 x0f000003),
},
{
.mask = cpu_to_be32(0 xffffffff), /* all 2.05-compliant */
.val = cpu_to_be32(0 x0f000002),
},
{
.mask = cpu_to_be32(0 xfffffffe), /* all 2.04-compliant and earlier */
.val = cpu_to_be32(0 x0f000001),
},
},
.num_vectors = NUM_VECTORS(6 ),
.vec1_len = VECTOR_LENGTH(sizeof (struct option_vector1)),
.vec1 = {
.byte1 = 0 ,
.arch_versions = OV1_PPC_2_00 | OV1_PPC_2_01 | OV1_PPC_2_02 | OV1_PPC_2_03 |
OV1_PPC_2_04 | OV1_PPC_2_05 | OV1_PPC_2_06 | OV1_PPC_2_07,
.arch_versions3 = OV1_PPC_3_00 | OV1_PPC_3_1,
},
.vec2_len = VECTOR_LENGTH(sizeof (struct option_vector2)),
/* option vector 2: Open Firmware options supported */
.vec2 = {
.byte1 = OV2_REAL_MODE,
.reserved = 0 ,
.real_base = cpu_to_be32(0 xffffffff),
.real_size = cpu_to_be32(0 xffffffff),
.virt_base = cpu_to_be32(0 xffffffff),
.virt_size = cpu_to_be32(0 xffffffff),
.load_base = cpu_to_be32(0 xffffffff),
.min_rma = cpu_to_be32(MIN_RMA),
.min_load = cpu_to_be32(0 xffffffff), /* full client load */
.min_rma_percent = 0 , /* min RMA percentage of total RAM */
.max_pft_size = 48 , /* max log_2(hash table size) */
},
.vec3_len = VECTOR_LENGTH(sizeof (struct option_vector3)),
/* option vector 3: processor options supported */
.vec3 = {
.byte1 = 0 , /* don't ignore, don't halt */
.byte2 = OV3_FP | OV3_VMX | OV3_DFP,
},
.vec4_len = VECTOR_LENGTH(sizeof (struct option_vector4)),
/* option vector 4: IBM PAPR implementation */
.vec4 = {
.byte1 = 0 , /* don't halt */
.min_vp_cap = OV4_MIN_ENT_CAP, /* minimum VP entitled capacity */
},
.vec5_len = VECTOR_LENGTH(sizeof (struct option_vector5)),
/* option vector 5: PAPR/OF options */
.vec5 = {
.byte1 = 0 , /* don't ignore, don't halt */
.byte2 = OV5_FEAT(OV5_LPAR) | OV5_FEAT(OV5_SPLPAR) | OV5_FEAT(OV5_LARGE_PAGES) |
OV5_FEAT(OV5_DRCONF_MEMORY) | OV5_FEAT(OV5_DONATE_DEDICATE_CPU) |
#ifdef CONFIG_PCI_MSI
/* PCIe/MSI support. Without MSI full PCIe is not supported */
OV5_FEAT(OV5_MSI),
#else
0 ,
#endif
.byte3 = 0 ,
.cmo =
#ifdef CONFIG_PPC_SMLPAR
OV5_FEAT(OV5_CMO) | OV5_FEAT(OV5_XCMO),
#else
0 ,
#endif
.associativity = OV5_FEAT(OV5_FORM1_AFFINITY) | OV5_FEAT(OV5_PRRN) |
OV5_FEAT(OV5_FORM2_AFFINITY),
.bin_opts = OV5_FEAT(OV5_RESIZE_HPT) | OV5_FEAT(OV5_HP_EVT),
.micro_checkpoint = 0 ,
.reserved0 = 0 ,
.max_cpus = cpu_to_be32(NR_CPUS), /* number of cores supported */
.papr_level = 0 ,
.reserved1 = 0 ,
.platform_facilities = OV5_FEAT(OV5_PFO_HW_RNG) | OV5_FEAT(OV5_PFO_HW_ENCR) | OV5_FEAT(OV5_PFO_HW_842),
.reserved2 = 0 ,
.reserved3 = 0 ,
.subprocessors = 1 ,
.byte22 = OV5_FEAT(OV5_DRMEM_V2) | OV5_FEAT(OV5_DRC_INFO),
.intarch = 0 ,
.mmu = 0 ,
.hash_ext = 0 ,
.radix_ext = 0 ,
},
/* option vector 6: IBM PAPR hints */
.vec6_len = VECTOR_LENGTH(sizeof (struct option_vector6)),
.vec6 = {
.reserved = 0 ,
.secondary_pteg = 0 ,
.os_name = OV6_LINUX,
},
/* option vector 7: OS Identification */
.vec7_len = VECTOR_LENGTH(sizeof (struct option_vector7)),
};
static struct ibm_arch_vec __prombss ibm_architecture_vec ____cacheline_aligned;
/* Old method - ELF header with PT_NOTE sections only works on BE */
#ifdef __BIG_ENDIAN__
static const struct fake_elf {
Elf32_Ehdr elfhdr;
Elf32_Phdr phdr[2 ];
struct chrpnote {
u32 namesz;
u32 descsz;
u32 type;
char name[8 ]; /* "PowerPC" */
struct chrpdesc {
u32 real_mode;
u32 real_base;
u32 real_size;
u32 virt_base;
u32 virt_size;
u32 load_base;
} chrpdesc;
} chrpnote;
struct rpanote {
u32 namesz;
u32 descsz;
u32 type;
char name[24 ]; /* "IBM,RPA-Client-Config" */
struct rpadesc {
u32 lpar_affinity;
u32 min_rmo_size;
u32 min_rmo_percent;
u32 max_pft_size;
u32 splpar;
u32 min_load;
u32 new_mem_def;
u32 ignore_me;
} rpadesc;
} rpanote;
} fake_elf __initconst = {
.elfhdr = {
.e_ident = { 0 x7f, 'E' , 'L' , 'F' ,
ELFCLASS32, ELFDATA2MSB, EV_CURRENT },
.e_type = ET_EXEC, /* yeah right */
.e_machine = EM_PPC,
.e_version = EV_CURRENT,
.e_phoff = offsetof(struct fake_elf, phdr),
.e_phentsize = sizeof (Elf32_Phdr),
.e_phnum = 2
},
.phdr = {
[0 ] = {
.p_type = PT_NOTE,
.p_offset = offsetof(struct fake_elf, chrpnote),
.p_filesz = sizeof (struct chrpnote)
}, [1 ] = {
.p_type = PT_NOTE,
.p_offset = offsetof(struct fake_elf, rpanote),
.p_filesz = sizeof (struct rpanote)
}
},
.chrpnote = {
.namesz = sizeof ("PowerPC" ),
.descsz = sizeof (struct chrpdesc),
.type = 0 x1275,
.name = "PowerPC" ,
.chrpdesc = {
.real_mode = ~0 U, /* ~0 means "don't care" */
.real_base = ~0 U,
.real_size = ~0 U,
.virt_base = ~0 U,
.virt_size = ~0 U,
.load_base = ~0 U
},
},
.rpanote = {
.namesz = sizeof ("IBM,RPA-Client-Config" ),
.descsz = sizeof (struct rpadesc),
.type = 0 x12759999,
.name = "IBM,RPA-Client-Config" ,
.rpadesc = {
.lpar_affinity = 0 ,
.min_rmo_size = 64 , /* in megabytes */
.min_rmo_percent = 0 ,
.max_pft_size = 48 , /* 2^48 bytes max PFT size */
.splpar = 1 ,
.min_load = ~0 U,
.new_mem_def = 0
}
}
};
#endif /* __BIG_ENDIAN__ */
static int __init prom_count_smt_threads(void )
{
phandle node;
char type[64 ];
unsigned int plen;
/* Pick up th first CPU node we can find */
for (node = 0 ; prom_next_node(&node); ) {
type[0 ] = 0 ;
prom_getprop(node, "device_type" , type, sizeof (type));
if (prom_strcmp(type, "cpu" ))
continue ;
/*
* There is an entry for each smt thread , each entry being
* 4 bytes long . All cpus should have the same number of
* smt threads , so return after finding the first .
*/
plen = prom_getproplen(node, "ibm,ppc-interrupt-server#s" );
if (plen == PROM_ERROR)
break ;
plen >>= 2 ;
prom_debug("Found %lu smt threads per core\n" , (unsigned long )plen);
/* Sanity check */
if (plen < 1 || plen > 64 ) {
prom_printf("Threads per core %lu out of bounds, assuming 1\n" ,
(unsigned long )plen);
return 1 ;
}
return plen;
}
prom_debug("No threads found, assuming 1 per core\n" );
return 1 ;
}
static void __init prom_parse_mmu_model(u8 val,
struct platform_support *support)
{
switch (val) {
case OV5_FEAT(OV5_MMU_DYNAMIC):
case OV5_FEAT(OV5_MMU_EITHER): /* Either Available */
prom_debug("MMU - either supported\n" );
support->radix_mmu = !prom_radix_disable;
support->hash_mmu = true ;
break ;
case OV5_FEAT(OV5_MMU_RADIX): /* Only Radix */
prom_debug("MMU - radix only\n" );
if (prom_radix_disable) {
/*
* If we _ _ have__ to do radix , we ' re better off ignoring
* the command line rather than not booting .
*/
prom_printf("WARNING: Ignoring cmdline option disable_radix\n" );
}
support->radix_mmu = true ;
break ;
case OV5_FEAT(OV5_MMU_HASH):
prom_debug("MMU - hash only\n" );
support->hash_mmu = true ;
break ;
default :
prom_debug("Unknown mmu support option: 0x%x\n" , val);
break ;
}
}
static void __init prom_parse_xive_model(u8 val,
struct platform_support *support)
{
switch (val) {
case OV5_FEAT(OV5_XIVE_EITHER): /* Either Available */
prom_debug("XIVE - either mode supported\n" );
support->xive = !prom_xive_disable;
break ;
case OV5_FEAT(OV5_XIVE_EXPLOIT): /* Only Exploitation mode */
prom_debug("XIVE - exploitation mode supported\n" );
if (prom_xive_disable) {
/*
* If we _ _ have__ to do XIVE , we ' re better off ignoring
* the command line rather than not booting .
*/
prom_printf("WARNING: Ignoring cmdline option xive=off\n" );
}
support->xive = true ;
break ;
case OV5_FEAT(OV5_XIVE_LEGACY): /* Only Legacy mode */
prom_debug("XIVE - legacy mode supported\n" );
break ;
default :
prom_debug("Unknown xive support option: 0x%x\n" , val);
break ;
}
}
static void __init prom_parse_platform_support(u8 index, u8 val,
struct platform_support *support)
{
switch (index) {
case OV5_INDX(OV5_MMU_SUPPORT): /* MMU Model */
prom_parse_mmu_model(val & OV5_FEAT(OV5_MMU_SUPPORT), support);
break ;
case OV5_INDX(OV5_RADIX_GTSE): /* Radix Extensions */
if (val & OV5_FEAT(OV5_RADIX_GTSE))
support->radix_gtse = !prom_radix_gtse_disable;
break ;
case OV5_INDX(OV5_XIVE_SUPPORT): /* Interrupt mode */
prom_parse_xive_model(val & OV5_FEAT(OV5_XIVE_SUPPORT),
support);
break ;
}
}
static void __init prom_check_platform_support(void )
{
struct platform_support supported = {
.hash_mmu = false ,
.radix_mmu = false ,
.radix_gtse = false ,
.xive = false
};
int prop_len = prom_getproplen(prom.chosen,
"ibm,arch-vec-5-platform-support" );
/*
* First copy the architecture vec template
*
* use memcpy ( ) instead of * vec = * vec_template so that GCC replaces it
* by _ _ memcpy ( ) when KASAN is active
*/
memcpy(&ibm_architecture_vec, &ibm_architecture_vec_template,
sizeof (ibm_architecture_vec));
prom_strscpy_pad(ibm_architecture_vec.vec7.os_id, linux_banner, 256 );
if (prop_len > 1 ) {
int i;
u8 vec[8 ];
prom_debug("Found ibm,arch-vec-5-platform-support, len: %d\n" ,
prop_len);
if (prop_len > sizeof (vec))
prom_printf("WARNING: ibm,arch-vec-5-platform-support longer than expected (len: %d)\n" ,
prop_len);
prom_getprop(prom.chosen, "ibm,arch-vec-5-platform-support" , &vec, sizeof (vec));
for (i = 0 ; i < prop_len; i += 2 ) {
prom_debug("%d: index = 0x%x val = 0x%x\n" , i / 2 , vec[i], vec[i + 1 ]);
prom_parse_platform_support(vec[i], vec[i + 1 ], &supported);
}
}
if (supported.radix_mmu && IS_ENABLED(CONFIG_PPC_RADIX_MMU)) {
/* Radix preferred - Check if GTSE is also supported */
prom_debug("Asking for radix\n" );
ibm_architecture_vec.vec5.mmu = OV5_FEAT(OV5_MMU_RADIX);
if (supported.radix_gtse)
ibm_architecture_vec.vec5.radix_ext =
OV5_FEAT(OV5_RADIX_GTSE);
else
prom_debug("Radix GTSE isn't supported\n" );
} else if (supported.hash_mmu) {
/* Default to hash mmu (if we can) */
prom_debug("Asking for hash\n" );
ibm_architecture_vec.vec5.mmu = OV5_FEAT(OV5_MMU_HASH);
} else {
/* We're probably on a legacy hypervisor */
prom_debug("Assuming legacy hash support\n" );
}
if (supported.xive) {
prom_debug("Asking for XIVE\n" );
ibm_architecture_vec.vec5.intarch = OV5_FEAT(OV5_XIVE_EXPLOIT);
}
}
static void __init prom_send_capabilities(void )
{
ihandle root;
prom_arg_t ret;
u32 cores;
/* Check ibm,arch-vec-5-platform-support and fixup vec5 if required */
prom_check_platform_support();
root = call_prom("open" , 1 , 1 , ADDR("/" ));
if (root != 0 ) {
/* We need to tell the FW about the number of cores we support.
*
* To do that , we count the number of threads on the first core
* ( we assume this is the same for all cores ) and use it to
* divide NR_CPUS .
*/
cores = DIV_ROUND_UP(NR_CPUS, prom_count_smt_threads());
prom_printf("Max number of cores passed to firmware: %u (NR_CPUS = %d)\n" ,
cores, NR_CPUS);
ibm_architecture_vec.vec5.max_cpus = cpu_to_be32(cores);
/* try calling the ibm,client-architecture-support method */
prom_printf("Calling ibm,client-architecture-support..." );
if (call_prom_ret("call-method" , 3 , 2 , &ret,
ADDR("ibm,client-architecture-support" ),
root,
ADDR(&ibm_architecture_vec)) == 0 ) {
/* the call exists... */
if (ret)
prom_printf("\nWARNING: ibm,client-architecture"
"-support call FAILED!\n" );
call_prom("close" , 1 , 0 , root);
prom_printf(" done\n" );
return ;
}
call_prom("close" , 1 , 0 , root);
prom_printf(" not implemented\n" );
}
#ifdef __BIG_ENDIAN__
{
ihandle elfloader;
/* no ibm,client-architecture-support call, try the old way */
elfloader = call_prom("open" , 1 , 1 ,
ADDR("/packages/elf-loader" ));
if (elfloader == 0 ) {
prom_printf("couldn't open /packages/elf-loader\n" );
return ;
}
call_prom("call-method" , 3 , 1 , ADDR("process-elf-header" ),
elfloader, ADDR(&fake_elf));
call_prom("close" , 1 , 0 , elfloader);
}
#endif /* __BIG_ENDIAN__ */
}
#endif /* CONFIG_PPC_PSERIES */
/*
* Memory allocation strategy . . . our layout is normally :
*
* at 14 Mb or more we have vmlinux , then a gap and initrd . In some
* rare cases , initrd might end up being before the kernel though .
* We assume this won ' t override the final kernel at 0 , we have no
* provision to handle that in this version , but it should hopefully
* never happen .
*
* alloc_top is set to the top of RMO , eventually shrink down if the
* TCEs overlap
*
* alloc_bottom is set to the top of kernel / initrd
*
* from there , allocations are done this way : rtas is allocated
* topmost , and the device - tree is allocated from the bottom . We try
* to grow the device - tree allocation as we progress . If we can ' t ,
* then we fail , we don ' t currently have a facility to restart
* elsewhere , but that shouldn ' t be necessary .
*
* Note that calls to reserve_mem have to be done explicitly , memory
* allocated with either alloc_up or alloc_down isn ' t automatically
* reserved .
*/
/*
* Allocates memory in the RMO upward from the kernel / initrd
*
* When align is 0 , this is a special case , it means to allocate in place
* at the current location of alloc_bottom or fail ( that is basically
* extending the previous allocation ) . Used for the device - tree flattening
*/
static unsigned long __init alloc_up(unsigned long size, unsigned long align)
{
unsigned long base = alloc_bottom;
unsigned long addr = 0 ;
if (align)
base = ALIGN(base, align);
prom_debug("%s(%lx, %lx)\n" , __func__, size, align);
if (ram_top == 0 )
prom_panic("alloc_up() called with mem not initialized\n" );
if (align)
base = ALIGN(alloc_bottom, align);
else
base = alloc_bottom;
for (; (base + size) <= alloc_top;
base = ALIGN(base + 0 x100000, align)) {
prom_debug(" trying: 0x%lx\n\r" , base);
addr = (unsigned long )prom_claim(base, size, 0 );
if (addr != PROM_ERROR && addr != 0 )
break ;
addr = 0 ;
if (align == 0 )
break ;
}
if (addr == 0 )
return 0 ;
alloc_bottom = addr + size;
prom_debug(" -> %lx\n" , addr);
prom_debug(" alloc_bottom : %lx\n" , alloc_bottom);
prom_debug(" alloc_top : %lx\n" , alloc_top);
prom_debug(" alloc_top_hi : %lx\n" , alloc_top_high);
prom_debug(" rmo_top : %lx\n" , rmo_top);
prom_debug(" ram_top : %lx\n" , ram_top);
return addr;
}
/*
* Allocates memory downward , either from top of RMO , or if highmem
* is set , from the top of RAM . Note that this one doesn ' t handle
* failures . It does claim memory if highmem is not set .
*/
static unsigned long __init alloc_down(unsigned long size, unsigned long align,
int highmem)
{
unsigned long base, addr = 0 ;
prom_debug("%s(%lx, %lx, %s)\n" , __func__, size, align,
highmem ? "(high)" : "(low)" );
if (ram_top == 0 )
prom_panic("alloc_down() called with mem not initialized\n" );
if (highmem) {
/* Carve out storage for the TCE table. */
addr = ALIGN_DOWN(alloc_top_high - size, align);
if (addr <= alloc_bottom)
return 0 ;
/* Will we bump into the RMO ? If yes, check out that we
* didn ' t overlap existing allocations there , if we did ,
* we are dead , we must be the first in town !
*/
if (addr < rmo_top) {
/* Good, we are first */
if (alloc_top == rmo_top)
alloc_top = rmo_top = addr;
else
return 0 ;
}
alloc_top_high = addr;
goto bail;
}
base = ALIGN_DOWN(alloc_top - size, align);
for (; base > alloc_bottom;
base = ALIGN_DOWN(base - 0 x100000, align)) {
prom_debug(" trying: 0x%lx\n\r" , base);
addr = (unsigned long )prom_claim(base, size, 0 );
if (addr != PROM_ERROR && addr != 0 )
break ;
addr = 0 ;
}
if (addr == 0 )
return 0 ;
alloc_top = addr;
bail:
prom_debug(" -> %lx\n" , addr);
prom_debug(" alloc_bottom : %lx\n" , alloc_bottom);
prom_debug(" alloc_top : %lx\n" , alloc_top);
prom_debug(" alloc_top_hi : %lx\n" , alloc_top_high);
prom_debug(" rmo_top : %lx\n" , rmo_top);
prom_debug(" ram_top : %lx\n" , ram_top);
return addr;
}
/*
* Parse a " reg " cell
*/
static unsigned long __init prom_next_cell(int s, cell_t **cellp)
{
cell_t *p = *cellp;
unsigned long r = 0 ;
/* Ignore more than 2 cells */
while (s > sizeof (unsigned long ) / 4 ) {
p++;
s--;
}
r = be32_to_cpu(*p++);
#ifdef CONFIG_PPC64
if (s > 1 ) {
r <<= 32 ;
r |= be32_to_cpu(*(p++));
}
#endif
*cellp = p;
return r;
}
/*
* Very dumb function for adding to the memory reserve list , but
* we don ' t need anything smarter at this point
*
* XXX Eventually check for collisions . They should NEVER happen .
* If problems seem to show up , it would be a good start to track
* them down .
*/
static void __init reserve_mem(u64 base, u64 size)
{
u64 top = base + size;
unsigned long cnt = mem_reserve_cnt;
if (size == 0 )
return ;
/* We need to always keep one empty entry so that we
* have our terminator with " size " set to 0 since we are
* dumb and just copy this entire array to the boot params
*/
base = ALIGN_DOWN(base, PAGE_SIZE);
top = ALIGN(top, PAGE_SIZE);
size = top - base;
if (cnt >= (MEM_RESERVE_MAP_SIZE - 1 ))
prom_panic("Memory reserve map exhausted !\n" );
mem_reserve_map[cnt].base = cpu_to_be64(base);
mem_reserve_map[cnt].size = cpu_to_be64(size);
mem_reserve_cnt = cnt + 1 ;
}
/*
* Initialize memory allocation mechanism , parse " memory " nodes and
* obtain that way the top of memory and RMO to setup out local allocator
*/
static void __init prom_init_mem(void )
{
phandle node;
char type[64 ];
unsigned int plen;
cell_t *p, *endp;
__be32 val;
u32 rac, rsc;
/*
* We iterate the memory nodes to find
* 1 ) top of RMO ( first node )
* 2 ) top of memory
*/
val = cpu_to_be32(2 );
prom_getprop(prom.root, "#address-cells" , &val, sizeof (val));
rac = be32_to_cpu(val);
val = cpu_to_be32(1 );
prom_getprop(prom.root, "#size-cells" , &val, sizeof (rsc));
rsc = be32_to_cpu(val);
prom_debug("root_addr_cells: %x\n" , rac);
prom_debug("root_size_cells: %x\n" , rsc);
prom_debug("scanning memory:\n" );
for (node = 0 ; prom_next_node(&node); ) {
type[0 ] = 0 ;
prom_getprop(node, "device_type" , type, sizeof (type));
if (type[0 ] == 0 ) {
/*
* CHRP Longtrail machines have no device_type
* on the memory node , so check the name instead . . .
*/
prom_getprop(node, "name" , type, sizeof (type));
}
if (prom_strcmp(type, "memory" ))
continue ;
plen = prom_getprop(node, "reg" , regbuf, sizeof (regbuf));
if (plen > sizeof (regbuf)) {
prom_printf("memory node too large for buffer !\n" );
plen = sizeof (regbuf);
}
p = regbuf;
endp = p + (plen / sizeof (cell_t));
#ifdef DEBUG_PROM
memset(prom_scratch, 0 , sizeof (prom_scratch));
call_prom("package-to-path" , 3 , 1 , node, prom_scratch,
sizeof (prom_scratch) - 1 );
prom_debug(" node %s :\n" , prom_scratch);
#endif /* DEBUG_PROM */
while ((endp - p) >= (rac + rsc)) {
unsigned long base, size;
base = prom_next_cell(rac, &p);
size = prom_next_cell(rsc, &p);
if (size == 0 )
continue ;
prom_debug(" %lx %lx\n" , base, size);
if (base == 0 && (of_platform & PLATFORM_LPAR))
rmo_top = size;
if ((base + size) > ram_top)
ram_top = base + size;
}
}
alloc_bottom = PAGE_ALIGN((unsigned long )&_end + 0 x4000);
/*
* If prom_memory_limit is set we reduce the upper limits * except * for
* alloc_top_high . This must be the real top of RAM so we can put
* TCE ' s up there .
*/
alloc_top_high = ram_top;
if (prom_memory_limit) {
if (prom_memory_limit <= alloc_bottom) {
prom_printf("Ignoring mem=%lx <= alloc_bottom.\n" ,
prom_memory_limit);
prom_memory_limit = 0 ;
} else if (prom_memory_limit >= ram_top) {
prom_printf("Ignoring mem=%lx >= ram_top.\n" ,
prom_memory_limit);
prom_memory_limit = 0 ;
} else {
ram_top = prom_memory_limit;
rmo_top = min(rmo_top, prom_memory_limit);
}
}
/*
* Setup our top alloc point , that is top of RMO or top of
* segment 0 when running non - LPAR .
* Some RS64 machines have buggy firmware where claims up at
* 1 GB fail . Cap at 768 MB as a workaround .
* Since 768 MB is plenty of room , and we need to cap to something
* reasonable on 32 - bit , cap at 768 MB on all machines .
*/
if (!rmo_top)
rmo_top = ram_top;
rmo_top = min(0 x30000000ul, rmo_top);
alloc_top = rmo_top;
alloc_top_high = ram_top;
/*
* Check if we have an initrd after the kernel but still inside
* the RMO . If we do move our bottom point to after it .
*/
if (prom_initrd_start &&
prom_initrd_start < rmo_top &&
prom_initrd_end > alloc_bottom)
alloc_bottom = PAGE_ALIGN(prom_initrd_end);
prom_printf("memory layout at init:\n" );
prom_printf(" memory_limit : %lx (16 MB aligned)\n" ,
prom_memory_limit);
prom_printf(" alloc_bottom : %lx\n" , alloc_bottom);
prom_printf(" alloc_top : %lx\n" , alloc_top);
prom_printf(" alloc_top_hi : %lx\n" , alloc_top_high);
prom_printf(" rmo_top : %lx\n" , rmo_top);
prom_printf(" ram_top : %lx\n" , ram_top);
}
static void __init prom_close_stdin(void )
{
__be32 val;
ihandle stdin;
if (prom_getprop(prom.chosen, "stdin" , &val, sizeof (val)) > 0 ) {
stdin = be32_to_cpu(val);
call_prom("close" , 1 , 0 , stdin);
}
}
#ifdef CONFIG_PPC_SVM
static int __init prom_rtas_hcall(uint64_t args)
{
register uint64_t arg1 asm ("r3" ) = H_RTAS;
register uint64_t arg2 asm ("r4" ) = args;
asm volatile ("sc 1\n" : "=r" (arg1) :
"r" (arg1),
"r" (arg2) :);
srr_regs_clobbered();
return arg1;
}
static struct rtas_args __prombss os_term_args;
static void __init prom_rtas_os_term(char *str)
{
phandle rtas_node;
__be32 val;
u32 token;
prom_debug("%s: start...\n" , __func__);
rtas_node = call_prom("finddevice" , 1 , 1 , ADDR("/rtas" ));
prom_debug("rtas_node: %x\n" , rtas_node);
if (!PHANDLE_VALID(rtas_node))
return ;
val = 0 ;
prom_getprop(rtas_node, "ibm,os-term" , &val, sizeof (val));
token = be32_to_cpu(val);
prom_debug("ibm,os-term: %x\n" , token);
if (token == 0 )
prom_panic("Could not get token for ibm,os-term\n" );
os_term_args.token = cpu_to_be32(token);
os_term_args.nargs = cpu_to_be32(1 );
os_term_args.nret = cpu_to_be32(1 );
os_term_args.args[0 ] = cpu_to_be32(__pa(str));
prom_rtas_hcall((uint64_t)&os_term_args);
}
#endif /* CONFIG_PPC_SVM */
/*
* Allocate room for and instantiate RTAS
*/
static void __init prom_instantiate_rtas(void )
{
phandle rtas_node;
ihandle rtas_inst;
u32 base, entry = 0 ;
__be32 val;
u32 size = 0 ;
prom_debug("prom_instantiate_rtas: start...\n" );
rtas_node = call_prom("finddevice" , 1 , 1 , ADDR("/rtas" ));
prom_debug("rtas_node: %x\n" , rtas_node);
if (!PHANDLE_VALID(rtas_node))
return ;
val = 0 ;
prom_getprop(rtas_node, "rtas-size" , &val, sizeof (size));
size = be32_to_cpu(val);
if (size == 0 )
return ;
base = alloc_down(size, PAGE_SIZE, 0 );
if (base == 0 )
prom_panic("Could not allocate memory for RTAS\n" );
rtas_inst = call_prom("open" , 1 , 1 , ADDR("/rtas" ));
if (!IHANDLE_VALID(rtas_inst)) {
prom_printf("opening rtas package failed (%x)\n" , rtas_inst);
return ;
}
prom_printf("instantiating rtas at 0x%x..." , base);
if (call_prom_ret("call-method" , 3 , 2 , &entry,
ADDR("instantiate-rtas" ),
rtas_inst, base) != 0
|| entry == 0 ) {
prom_printf(" failed\n" );
return ;
}
prom_printf(" done\n" );
reserve_mem(base, size);
val = cpu_to_be32(base);
prom_setprop(rtas_node, "/rtas" , "linux,rtas-base" ,
&val, sizeof (val));
val = cpu_to_be32(entry);
prom_setprop(rtas_node, "/rtas" , "linux,rtas-entry" ,
&val, sizeof (val));
/* Check if it supports "query-cpu-stopped-state" */
if (prom_getprop(rtas_node, "query-cpu-stopped-state" ,
&val, sizeof (val)) != PROM_ERROR)
rtas_has_query_cpu_stopped = true ;
prom_debug("rtas base = 0x%x\n" , base);
prom_debug("rtas entry = 0x%x\n" , entry);
prom_debug("rtas size = 0x%x\n" , size);
prom_debug("prom_instantiate_rtas: end...\n" );
}
#ifdef CONFIG_PPC64
/*
* Allocate room for and instantiate Stored Measurement Log ( SML )
*/
static void __init prom_instantiate_sml(void )
{
phandle ibmvtpm_node;
ihandle ibmvtpm_inst;
u32 entry = 0 , size = 0 , succ = 0 ;
u64 base;
__be32 val;
prom_debug("prom_instantiate_sml: start...\n" );
ibmvtpm_node = call_prom("finddevice" , 1 , 1 , ADDR("/vdevice/vtpm" ));
prom_debug("ibmvtpm_node: %x\n" , ibmvtpm_node);
if (!PHANDLE_VALID(ibmvtpm_node))
return ;
ibmvtpm_inst = call_prom("open" , 1 , 1 , ADDR("/vdevice/vtpm" ));
if (!IHANDLE_VALID(ibmvtpm_inst)) {
prom_printf("opening vtpm package failed (%x)\n" , ibmvtpm_inst);
return ;
}
if (prom_getprop(ibmvtpm_node, "ibm,sml-efi-reformat-supported" ,
&val, sizeof (val)) != PROM_ERROR) {
if (call_prom_ret("call-method" , 2 , 2 , &succ,
ADDR("reformat-sml-to-efi-alignment" ),
ibmvtpm_inst) != 0 || succ == 0 ) {
prom_printf("Reformat SML to EFI alignment failed\n" );
return ;
}
if (call_prom_ret("call-method" , 2 , 2 , &size,
ADDR("sml-get-allocated-size" ),
ibmvtpm_inst) != 0 || size == 0 ) {
prom_printf("SML get allocated size failed\n" );
return ;
}
} else {
if (call_prom_ret("call-method" , 2 , 2 , &size,
ADDR("sml-get-handover-size" ),
ibmvtpm_inst) != 0 || size == 0 ) {
prom_printf("SML get handover size failed\n" );
return ;
}
}
base = alloc_down(size, PAGE_SIZE, 0 );
if (base == 0 )
prom_panic("Could not allocate memory for sml\n" );
prom_printf("instantiating sml at 0x%llx..." , base);
memset((void *)base, 0 , size);
if (call_prom_ret("call-method" , 4 , 2 , &entry,
ADDR("sml-handover" ),
ibmvtpm_inst, size, base) != 0 || entry == 0 ) {
prom_printf("SML handover failed\n" );
return ;
}
prom_printf(" done\n" );
reserve_mem(base, size);
prom_setprop(ibmvtpm_node, "/vdevice/vtpm" , "linux,sml-base" ,
&base, sizeof (base));
prom_setprop(ibmvtpm_node, "/vdevice/vtpm" , "linux,sml-size" ,
&size, sizeof (size));
prom_debug("sml base = 0x%llx\n" , base);
prom_debug("sml size = 0x%x\n" , size);
prom_debug("prom_instantiate_sml: end...\n" );
}
/*
* Allocate room for and initialize TCE tables
*/
#ifdef __BIG_ENDIAN__
static void __init prom_initialize_tce_table(void )
{
phandle node;
ihandle phb_node;
char compatible[64 ], type[64 ], model[64 ];
char *path = prom_scratch;
u64 base, align;
u32 minalign, minsize;
u64 tce_entry, *tce_entryp;
u64 local_alloc_top, local_alloc_bottom;
u64 i;
if (prom_iommu_off)
return ;
prom_debug("starting prom_initialize_tce_table\n" );
/* Cache current top of allocs so we reserve a single block */
local_alloc_top = alloc_top_high;
local_alloc_bottom = local_alloc_top;
/* Search all nodes looking for PHBs. */
for (node = 0 ; prom_next_node(&node); ) {
compatible[0 ] = 0 ;
type[0 ] = 0 ;
model[0 ] = 0 ;
prom_getprop(node, "compatible" ,
compatible, sizeof (compatible));
prom_getprop(node, "device_type" , type, sizeof (type));
prom_getprop(node, "model" , model, sizeof (model));
if ((type[0 ] == 0 ) || (prom_strstr(type, "pci" ) == NULL))
continue ;
/* Keep the old logic intact to avoid regression. */
if (compatible[0 ] != 0 ) {
if ((prom_strstr(compatible, "python" ) == NULL) &&
(prom_strstr(compatible, "Speedwagon" ) == NULL) &&
(prom_strstr(compatible, "Winnipeg" ) == NULL))
continue ;
} else if (model[0 ] != 0 ) {
if ((prom_strstr(model, "ython" ) == NULL) &&
(prom_strstr(model, "peedwagon" ) == NULL) &&
(prom_strstr(model, "innipeg" ) == NULL))
continue ;
}
if (prom_getprop(node, "tce-table-minalign" , &minalign,
sizeof (minalign)) == PROM_ERROR)
minalign = 0 ;
if (prom_getprop(node, "tce-table-minsize" , &minsize,
sizeof (minsize)) == PROM_ERROR)
minsize = 4 UL << 20 ;
/*
* Even though we read what OF wants , we just set the table
* size to 4 MB . This is enough to map 2 GB of PCI DMA space .
* By doing this , we avoid the pitfalls of trying to DMA to
* MMIO space and the DMA alias hole .
*/
minsize = 4 UL << 20 ;
/* Align to the greater of the align or size */
align = max(minalign, minsize);
base = alloc_down(minsize, align, 1 );
if (base == 0 )
prom_panic("ERROR, cannot find space for TCE table.\n" );
if (base < local_alloc_bottom)
local_alloc_bottom = base;
/* It seems OF doesn't null-terminate the path :-( */
memset(path, 0 , sizeof (prom_scratch));
/* Call OF to setup the TCE hardware */
if (call_prom("package-to-path" , 3 , 1 , node,
path, sizeof (prom_scratch) - 1 ) == PROM_ERROR) {
prom_printf("package-to-path failed\n" );
}
/* Save away the TCE table attributes for later use. */
prom_setprop(node, path, "linux,tce-base" , &base, sizeof (base));
prom_setprop(node, path, "linux,tce-size" , &minsize, sizeof (minsize));
prom_debug("TCE table: %s\n" , path);
prom_debug("\tnode = 0x%x\n" , node);
prom_debug("\tbase = 0x%llx\n" , base);
prom_debug("\tsize = 0x%x\n" , minsize);
/* Initialize the table to have a one-to-one mapping
* over the allocated size .
*/
tce_entryp = (u64 *)base;
for (i = 0 ; i < (minsize >> 3 ) ;tce_entryp++, i++) {
tce_entry = (i << PAGE_SHIFT);
tce_entry |= 0 x3;
*tce_entryp = tce_entry;
}
prom_printf("opening PHB %s" , path);
phb_node = call_prom("open" , 1 , 1 , path);
if (phb_node == 0 )
prom_printf("... failed\n" );
else
prom_printf("... done\n" );
call_prom("call-method" , 6 , 0 , ADDR("set-64-bit-addressing" ),
phb_node, -1 , minsize,
(u32) base, (u32) (base >> 32 ));
call_prom("close" , 1 , 0 , phb_node);
}
reserve_mem(local_alloc_bottom, local_alloc_top - local_alloc_bottom);
/* These are only really needed if there is a memory limit in
* effect, but we don't know so export them always. */
prom_tce_alloc_start = local_alloc_bottom;
prom_tce_alloc_end = local_alloc_top;
/* Flag the first invalid entry */
prom_debug("ending prom_initialize_tce_table\n" );
}
#endif /* __BIG_ENDIAN__ */
#endif /* CONFIG_PPC64 */
/*
* With CHRP SMP we need to use the OF to start the other processors .
* We can ' t wait until smp_boot_cpus ( the OF is trashed by then )
* so we have to put the processors into a holding pattern controlled
* by the kernel ( not OF ) before we destroy the OF .
*
* This uses a chunk of low memory , puts some holding pattern
* code there and sends the other processors off to there until
* smp_boot_cpus tells them to do something . The holding pattern
* checks that address until its cpu # is there , when it is that
* cpu jumps to _ _ secondary_start ( ) . smp_boot_cpus ( ) takes care
* of setting those values .
*
* We also use physical address 0 x4 here to tell when a cpu
* is in its holding pattern code .
*
* - - Cort
*/
/*
* We want to reference the copy of _ _ secondary_hold_ * in the
* 0 - 0 x100 address range
*/
#define LOW_ADDR(x) (((unsigned long ) &(x)) & 0 xff)
static void __init prom_hold_cpus(void )
{
unsigned long i;
phandle node;
char type[64 ];
unsigned long *spinloop
= (void *) LOW_ADDR(__secondary_hold_spinloop);
unsigned long *acknowledge
= (void *) LOW_ADDR(__secondary_hold_acknowledge);
unsigned long secondary_hold = LOW_ADDR(__secondary_hold);
/*
* On pseries , if RTAS supports " query - cpu - stopped - state " ,
* we skip this stage , the CPUs will be started by the
* kernel using RTAS .
*/
if ((of_platform == PLATFORM_PSERIES ||
of_platform == PLATFORM_PSERIES_LPAR) &&
rtas_has_query_cpu_stopped) {
prom_printf("prom_hold_cpus: skipped\n" );
return ;
}
prom_debug("prom_hold_cpus: start...\n" );
prom_debug(" 1) spinloop = 0x%lx\n" , (unsigned long )spinloop);
prom_debug(" 1) *spinloop = 0x%lx\n" , *spinloop);
prom_debug(" 1) acknowledge = 0x%lx\n" ,
(unsigned long )acknowledge);
prom_debug(" 1) *acknowledge = 0x%lx\n" , *acknowledge);
prom_debug(" 1) secondary_hold = 0x%lx\n" , secondary_hold);
/* Set the common spinloop variable, so all of the secondary cpus
* will block when they are awakened from their OF spinloop .
* This must occur for both SMP and non SMP kernels , since OF will
* be trashed when we move the kernel .
*/
*spinloop = 0 ;
/* look for cpus */
for (node = 0 ; prom_next_node(&node); ) {
unsigned int cpu_no;
__be32 reg;
type[0 ] = 0 ;
prom_getprop(node, "device_type" , type, sizeof (type));
if (prom_strcmp(type, "cpu" ) != 0 )
continue ;
/* Skip non-configured cpus. */
if (prom_getprop(node, "status" , type, sizeof (type)) > 0 )
if (prom_strcmp(type, "okay" ) != 0 )
continue ;
reg = cpu_to_be32(-1 ); /* make sparse happy */
prom_getprop(node, "reg" , ®, sizeof (reg));
cpu_no = be32_to_cpu(reg);
prom_debug("cpu hw idx = %u\n" , cpu_no);
/* Init the acknowledge var which will be reset by
* the secondary cpu when it awakens from its OF
* spinloop .
*/
*acknowledge = (unsigned long )-1 ;
if (cpu_no != prom.cpu) {
/* Primary Thread of non-boot cpu or any thread */
prom_printf("starting cpu hw idx %u... " , cpu_no);
call_prom("start-cpu" , 3 , 0 , node,
secondary_hold, cpu_no);
for (i = 0 ; (i < 100000000 ) &&
(*acknowledge == ((unsigned long )-1 )); i++ )
mb();
if (*acknowledge == cpu_no)
prom_printf("done\n" );
else
prom_printf("failed: %lx\n" , *acknowledge);
}
#ifdef CONFIG_SMP
else
prom_printf("boot cpu hw idx %u\n" , cpu_no);
#endif /* CONFIG_SMP */
}
prom_debug("prom_hold_cpus: end...\n" );
}
static void __init prom_init_client_services(unsigned long pp)
{
/* Get a handle to the prom entry point before anything else */
prom_entry = pp;
/* get a handle for the stdout device */
prom.chosen = call_prom("finddevice" , 1 , 1 , ADDR("/chosen" ));
if (!PHANDLE_VALID(prom.chosen))
prom_panic("cannot find chosen" ); /* msg won't be printed :( */
/* get device tree root */
prom.root = call_prom("finddevice" , 1 , 1 , ADDR("/" ));
if (!PHANDLE_VALID(prom.root))
prom_panic("cannot find device tree root" ); /* msg won't be printed :( */
prom.mmumap = 0 ;
}
#ifdef CONFIG_PPC32
/*
* For really old powermacs , we need to map things we claim .
* For that , we need the ihandle of the mmu .
* Also , on the longtrail , we need to work around other bugs .
*/
static void __init prom_find_mmu(void )
{
phandle oprom;
char version[64 ];
oprom = call_prom("finddevice" , 1 , 1 , ADDR("/openprom" ));
if (!PHANDLE_VALID(oprom))
return ;
if (prom_getprop(oprom, "model" , version, sizeof (version)) <= 0 )
return ;
version[sizeof (version) - 1 ] = 0 ;
/* XXX might need to add other versions here */
if (prom_strcmp(version, "Open Firmware, 1.0.5" ) == 0 )
of_workarounds = OF_WA_CLAIM;
else if (prom_strncmp(version, "FirmWorks,3." , 12 ) == 0 ) {
of_workarounds = OF_WA_CLAIM | OF_WA_LONGTRAIL;
call_prom("interpret" , 1 , 1 , "dev /memory 0 to allow-reclaim" );
} else
return ;
prom.memory = call_prom("open" , 1 , 1 , ADDR("/memory" ));
prom_getprop(prom.chosen, "mmu" , &prom.mmumap,
sizeof (prom.mmumap));
prom.mmumap = be32_to_cpu(prom.mmumap);
if (!IHANDLE_VALID(prom.memory) || !IHANDLE_VALID(prom.mmumap))
of_workarounds &= ~OF_WA_CLAIM; /* hmmm */
}
#else
#define prom_find_mmu()
#endif
static void __init prom_init_stdout(void )
{
char *path = of_stdout_device;
char type[16 ];
phandle stdout_node;
__be32 val;
if (prom_getprop(prom.chosen, "stdout" , &val, sizeof (val)) <= 0 )
prom_panic("cannot find stdout" );
prom.stdout = be32_to_cpu(val);
/* Get the full OF pathname of the stdout device */
memset(path, 0 , 256 );
call_prom("instance-to-path" , 3 , 1 , prom.stdout, path, 255 );
prom_printf("OF stdout device is: %s\n" , of_stdout_device);
prom_setprop(prom.chosen, "/chosen" , "linux,stdout-path" ,
path, prom_strlen(path) + 1 );
/* instance-to-package fails on PA-Semi */
stdout_node = call_prom("instance-to-package" , 1 , 1 , prom.stdout);
if (stdout_node != PROM_ERROR) {
val = cpu_to_be32(stdout_node);
/* If it's a display, note it */
memset(type, 0 , sizeof (type));
prom_getprop(stdout_node, "device_type" , type, sizeof (type));
if (prom_strcmp(type, "display" ) == 0 )
prom_setprop(stdout_node, path, "linux,boot-display" , NULL, 0 );
}
}
static int __init prom_find_machine_type(void )
{
static char compat[256 ] __prombss;
int len, i = 0 ;
#ifdef CONFIG_PPC64
phandle rtas;
int x;
#endif
/* Look for a PowerMac or a Cell */
len = prom_getprop(prom.root, "compatible" ,
compat, sizeof (compat)-1 );
if (len > 0 ) {
compat[len] = 0 ;
while (i < len) {
char *p = &compat[i];
int sl = prom_strlen(p);
if (sl == 0 )
break ;
if (prom_strstr(p, "Power Macintosh" ) ||
prom_strstr(p, "MacRISC" ))
return PLATFORM_POWERMAC;
#ifdef CONFIG_PPC64
/* We must make sure we don't detect the IBM Cell
* blades as pSeries due to some firmware issues ,
* so we do it here .
*/
if (prom_strstr(p, "IBM,CBEA" ) ||
prom_strstr(p, "IBM,CPBW-1.0" ))
return PLATFORM_GENERIC;
#endif /* CONFIG_PPC64 */
i += sl + 1 ;
}
}
#ifdef CONFIG_PPC64
/* Try to figure out if it's an IBM pSeries or any other
* PAPR compliant platform . We assume it is if :
* - / device_type is " chrp " ( please , do NOT use that for future
* non - IBM designs !
* - it has / rtas
*/
len = prom_getprop(prom.root, "device_type" ,
compat, sizeof (compat)-1 );
if (len <= 0 )
return PLATFORM_GENERIC;
if (prom_strcmp(compat, "chrp" ))
return PLATFORM_GENERIC;
/* Default to pSeries. We need to know if we are running LPAR */
rtas = call_prom("finddevice" , 1 , 1 , ADDR("/rtas" ));
if (!PHANDLE_VALID(rtas))
return PLATFORM_GENERIC;
x = prom_getproplen(rtas, "ibm,hypertas-functions" );
if (x != PROM_ERROR) {
prom_debug("Hypertas detected, assuming LPAR !\n" );
return PLATFORM_PSERIES_LPAR;
}
return PLATFORM_PSERIES;
#else
return PLATFORM_GENERIC;
#endif
}
static int __init prom_set_color(ihandle ih, int i, int r, int g, int b)
{
return call_prom("call-method" , 6 , 1 , ADDR("color!" ), ih, i, b, g, r);
}
/*
* If we have a display that we don ' t know how to drive ,
* we will want to try to execute OF ' s open method for it
* later . However , OF will probably fall over if we do that
* we ' ve taken over the MMU .
* So we check whether we will need to open the display ,
* and if so , open it now .
*/
static void __init prom_check_displays(void )
{
char type[16 ], *path;
phandle node;
ihandle ih;
int i;
static const unsigned char default_colors[] __initconst = {
0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 xaa,
0 x00, 0 xaa, 0 x00,
0 x00, 0 xaa, 0 xaa,
0 xaa, 0 x00, 0 x00,
0 xaa, 0 x00, 0 xaa,
0 xaa, 0 xaa, 0 x00,
0 xaa, 0 xaa, 0 xaa,
0 x55, 0 x55, 0 x55,
0 x55, 0 x55, 0 xff,
0 x55, 0 xff, 0 x55,
0 x55, 0 xff, 0 xff,
0 xff, 0 x55, 0 x55,
0 xff, 0 x55, 0 xff,
0 xff, 0 xff, 0 x55,
0 xff, 0 xff, 0 xff
};
const unsigned char *clut;
prom_debug("Looking for displays\n" );
for (node = 0 ; prom_next_node(&node); ) {
memset(type, 0 , sizeof (type));
prom_getprop(node, "device_type" , type, sizeof (type));
if (prom_strcmp(type, "display" ) != 0 )
continue ;
/* It seems OF doesn't null-terminate the path :-( */
path = prom_scratch;
memset(path, 0 , sizeof (prom_scratch));
/*
* leave some room at the end of the path for appending extra
* arguments
*/
if (call_prom("package-to-path" , 3 , 1 , node, path,
sizeof (prom_scratch) - 10 ) == PROM_ERROR)
continue ;
prom_printf("found display : %s, opening... " , path);
ih = call_prom("open" , 1 , 1 , path);
if (ih == 0 ) {
prom_printf("failed\n" );
continue ;
}
/* Success */
prom_printf("done\n" );
prom_setprop(node, path, "linux,opened" , NULL, 0 );
/* Setup a usable color table when the appropriate
* method is available. Should update this to set-colors */
clut = default_colors;
for (i = 0 ; i < 16 ; i++, clut += 3 )
if (prom_set_color(ih, i, clut[0 ], clut[1 ],
clut[2 ]) != 0 )
break ;
#ifdef CONFIG_LOGO_LINUX_CLUT224
clut = PTRRELOC(logo_linux_clut224.clut);
for (i = 0 ; i < logo_linux_clut224.clutsize; i++, clut += 3 )
if (prom_set_color(ih, i + 32 , clut[0 ], clut[1 ],
clut[2 ]) != 0 )
break ;
#endif /* CONFIG_LOGO_LINUX_CLUT224 */
#ifdef CONFIG_PPC_EARLY_DEBUG_BOOTX
if (prom_getprop(node, "linux,boot-display" , NULL, 0 ) !=
PROM_ERROR) {
u32 width, height, pitch, addr;
prom_printf("Setting btext !\n" );
if (prom_getprop(node, "width" , &width, 4 ) == PROM_ERROR)
return ;
if (prom_getprop(node, "height" , &height, 4 ) == PROM_ERROR)
return ;
if (prom_getprop(node, "linebytes" , &pitch, 4 ) == PROM_ERROR)
return ;
if (prom_getprop(node, "address" , &addr, 4 ) == PROM_ERROR)
return ;
prom_printf("W=%d H=%d LB=%d addr=0x%x\n" ,
width, height, pitch, addr);
btext_setup_display(width, height, 8 , pitch, addr);
btext_prepare_BAT();
}
#endif /* CONFIG_PPC_EARLY_DEBUG_BOOTX */
}
}
/* Return (relocated) pointer to this much memory: moves initrd if reqd. */
static void __init *make_room(unsigned long *mem_start, unsigned long *mem_end,
unsigned long needed, unsigned long align)
{
void *ret;
*mem_start = ALIGN(*mem_start, align);
while ((*mem_start + needed) > *mem_end) {
unsigned long room, chunk;
prom_debug("Chunk exhausted, claiming more at %lx...\n" ,
alloc_bottom);
room = alloc_top - alloc_bottom;
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
if (room < PAGE_SIZE)
prom_panic("No memory for flatten_device_tree "
"(no room)\n" );
chunk = alloc_up(room, 0 );
if (chunk == 0 )
prom_panic("No memory for flatten_device_tree "
"(claim failed)\n" );
*mem_end = chunk + room;
}
ret = (void *)*mem_start;
*mem_start += needed;
return ret;
}
#define dt_push_token(token, mem_start, mem_end) do { \
void *room = make_room(mem_start, mem_end, 4 , 4 ); \
*(__be32 *)room = cpu_to_be32(token); \
} while (0 )
static unsigned long __init dt_find_string(char *str)
{
char *s, *os;
s = os = (char *)dt_string_start;
s += 4 ;
while (s < (char *)dt_string_end) {
if (prom_strcmp(s, str) == 0 )
return s - os;
s += prom_strlen(s) + 1 ;
}
return 0 ;
}
/*
* The Open Firmware 1275 specification states properties must be 31 bytes or
* less , however not all firmwares obey this . Make it 64 bytes to be safe .
*/
#define MAX_PROPERTY_NAME 64
static void __init scan_dt_build_strings(phandle node,
unsigned long *mem_start,
unsigned long *mem_end)
{
char *prev_name, *namep, *sstart;
unsigned long soff;
phandle child;
sstart = (char *)dt_string_start;
/* get and store all property names */
prev_name = "" ;
for (;;) {
/* 64 is max len of name including nul. */
namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1 );
if (call_prom("nextprop" , 3 , 1 , node, prev_name, namep) != 1 ) {
/* No more nodes: unwind alloc */
*mem_start = (unsigned long )namep;
break ;
}
/* skip "name" */
if (prom_strcmp(namep, "name" ) == 0 ) {
*mem_start = (unsigned long )namep;
prev_name = "name" ;
continue ;
}
/* get/create string entry */
soff = dt_find_string(namep);
if (soff != 0 ) {
*mem_start = (unsigned long )namep;
namep = sstart + soff;
} else {
/* Trim off some if we can */
*mem_start = (unsigned long )namep + prom_strlen(namep) + 1 ;
dt_string_end = *mem_start;
}
prev_name = namep;
}
/* do all our children */
child = call_prom("child" , 1 , 1 , node);
while (child != 0 ) {
scan_dt_build_strings(child, mem_start, mem_end);
child = call_prom("peer" , 1 , 1 , child);
}
}
static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start,
unsigned long *mem_end)
{
phandle child;
char *namep, *prev_name, *sstart, *p, *ep, *lp, *path;
unsigned long soff;
unsigned char *valp;
static char pname[MAX_PROPERTY_NAME] __prombss;
int l, room, has_phandle = 0 ;
dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end);
/* get the node's full name */
namep = (char *)*mem_start;
room = *mem_end - *mem_start;
if (room > 255 )
room = 255 ;
l = call_prom("package-to-path" , 3 , 1 , node, namep, room);
if (l >= 0 ) {
/* Didn't fit? Get more room. */
if (l >= room) {
if (l >= *mem_end - *mem_start)
namep = make_room(mem_start, mem_end, l+1 , 1 );
call_prom("package-to-path" , 3 , 1 , node, namep, l);
}
namep[l] = '\0' ;
/* Fixup an Apple bug where they have bogus \0 chars in the
* middle of the path in some properties , and extract
* the unit name ( everything after the last ' / ' ) .
*/
for (lp = p = namep, ep = namep + l; p < ep; p++) {
if (*p == '/' )
lp = namep;
else if (*p != 0 )
*lp++ = *p;
}
*lp = 0 ;
*mem_start = ALIGN((unsigned long )lp + 1 , 4 );
}
/* get it again for debugging */
path = prom_scratch;
memset(path, 0 , sizeof (prom_scratch));
call_prom("package-to-path" , 3 , 1 , node, path, sizeof (prom_scratch) - 1 );
/* get and store all properties */
prev_name = "" ;
sstart = (char *)dt_string_start;
for (;;) {
if (call_prom("nextprop" , 3 , 1 , node, prev_name,
pname) != 1 )
break ;
/* skip "name" */
if (prom_strcmp(pname, "name" ) == 0 ) {
prev_name = "name" ;
continue ;
}
/* find string offset */
soff = dt_find_string(pname);
if (soff == 0 ) {
prom_printf("WARNING: Can't find string index for"
" <%s>, node %s\n" , pname, path);
break ;
}
prev_name = sstart + soff;
/* get length */
l = call_prom("getproplen" , 2 , 1 , node, pname);
/* sanity checks */
if (l == PROM_ERROR)
continue ;
/* push property head */
dt_push_token(OF_DT_PROP, mem_start, mem_end);
dt_push_token(l, mem_start, mem_end);
dt_push_token(soff, mem_start, mem_end);
/* push property content */
valp = make_room(mem_start, mem_end, l, 4 );
call_prom("getprop" , 4 , 1 , node, pname, valp, l);
*mem_start = ALIGN(*mem_start, 4 );
if (!prom_strcmp(pname, "phandle" ))
has_phandle = 1 ;
}
/* Add a "phandle" property if none already exist */
if (!has_phandle) {
soff = dt_find_string("phandle" );
if (soff == 0 )
prom_printf("WARNING: Can't find string index for <phandle> node %s\n" , path);
else {
dt_push_token(OF_DT_PROP, mem_start, mem_end);
dt_push_token(4 , mem_start, mem_end);
dt_push_token(soff, mem_start, mem_end);
valp = make_room(mem_start, mem_end, 4 , 4 );
*(__be32 *)valp = cpu_to_be32(node);
}
}
/* do all our children */
child = call_prom("child" , 1 , 1 , node);
while (child != 0 ) {
scan_dt_build_struct(child, mem_start, mem_end);
child = call_prom("peer" , 1 , 1 , child);
}
dt_push_token(OF_DT_END_NODE, mem_start, mem_end);
}
static void __init flatten_device_tree(void )
{
phandle root;
unsigned long mem_start, mem_end, room;
struct boot_param_header *hdr;
char *namep;
u64 *rsvmap;
/*
* Check how much room we have between alloc top & bottom ( + / - a
* few pages ) , crop to 1 MB , as this is our " chunk " size
*/
room = alloc_top - alloc_bottom - 0 x4000;
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
prom_debug("starting device tree allocs at %lx\n" , alloc_bottom);
/* Now try to claim that */
mem_start = (unsigned long )alloc_up(room, PAGE_SIZE);
if (mem_start == 0 )
prom_panic("Can't allocate initial device-tree chunk\n" );
mem_end = mem_start + room;
/* Get root of tree */
root = call_prom("peer" , 1 , 1 , (phandle)0 );
if (root == (phandle)0 )
prom_panic ("couldn't get device tree root\n" );
/* Build header and make room for mem rsv map */
mem_start = ALIGN(mem_start, 4 );
hdr = make_room(&mem_start, &mem_end,
sizeof (struct boot_param_header), 4 );
dt_header_start = (unsigned long )hdr;
rsvmap = make_room(&mem_start, &mem_end, sizeof (mem_reserve_map), 8 );
/* Start of strings */
mem_start = PAGE_ALIGN(mem_start);
dt_string_start = mem_start;
mem_start += 4 ; /* hole */
/* Add "phandle" in there, we'll need it */
namep = make_room(&mem_start, &mem_end, 16 , 1 );
prom_strscpy_pad(namep, "phandle" , sizeof ("phandle" ));
mem_start = (unsigned long )namep + prom_strlen(namep) + 1 ;
/* Build string array */
prom_printf("Building dt strings...\n" );
scan_dt_build_strings(root, &mem_start, &mem_end);
dt_string_end = mem_start;
/* Build structure */
mem_start = PAGE_ALIGN(mem_start);
dt_struct_start = mem_start;
prom_printf("Building dt structure...\n" );
scan_dt_build_struct(root, &mem_start, &mem_end);
dt_push_token(OF_DT_END, &mem_start, &mem_end);
dt_struct_end = PAGE_ALIGN(mem_start);
/* Finish header */
hdr->boot_cpuid_phys = cpu_to_be32(prom.cpu);
hdr->magic = cpu_to_be32(OF_DT_HEADER);
hdr->totalsize = cpu_to_be32(dt_struct_end - dt_header_start);
hdr->off_dt_struct = cpu_to_be32(dt_struct_start - dt_header_start);
hdr->off_dt_strings = cpu_to_be32(dt_string_start - dt_header_start);
hdr->dt_strings_size = cpu_to_be32(dt_string_end - dt_string_start);
hdr->off_mem_rsvmap = cpu_to_be32(((unsigned long )rsvmap) - dt_header_start);
hdr->version = cpu_to_be32(OF_DT_VERSION);
/* Version 16 is not backward compatible */
hdr->last_comp_version = cpu_to_be32(0 x10);
/* Copy the reserve map in */
memcpy(rsvmap, mem_reserve_map, sizeof (mem_reserve_map));
#ifdef DEBUG_PROM
{
int i;
prom_printf("reserved memory map:\n" );
for (i = 0 ; i < mem_reserve_cnt; i++)
prom_printf(" %llx - %llx\n" ,
be64_to_cpu(mem_reserve_map[i].base),
be64_to_cpu(mem_reserve_map[i].size));
}
#endif
/* Bump mem_reserve_cnt to cause further reservations to fail
* since it ' s too late .
*/
mem_reserve_cnt = MEM_RESERVE_MAP_SIZE;
prom_printf("Device tree strings 0x%lx -> 0x%lx\n" ,
dt_string_start, dt_string_end);
prom_printf("Device tree struct 0x%lx -> 0x%lx\n" ,
dt_struct_start, dt_struct_end);
}
/*
* Pegasos and BriQ lacks the " ranges " property in the isa node
* Pegasos needs decimal IRQ 14 / 15 , not hexadecimal
* Pegasos has the IDE configured in legacy mode , but advertised as native
*/
static void __init fixup_device_tree_chrp(void )
{
phandle ph;
u32 prop[6 ];
u32 rloc = 0 x01006000; /* IO space; PCI device = 12 */
char *name;
int rc;
name = "/pci@80000000/isa@c" ;
ph = call_prom("finddevice" , 1 , 1 , ADDR(name));
if (!PHANDLE_VALID(ph)) {
name = "/pci@ff500000/isa@6" ;
ph = call_prom("finddevice" , 1 , 1 , ADDR(name));
rloc = 0 x01003000; /* IO space; PCI device = 6 */
}
if (PHANDLE_VALID(ph)) {
rc = prom_getproplen(ph, "ranges" );
if (rc == 0 || rc == PROM_ERROR) {
prom_printf("Fixing up missing ISA range on Pegasos...\n" );
prop[0 ] = 0 x1;
prop[1 ] = 0 x0;
prop[2 ] = rloc;
prop[3 ] = 0 x0;
prop[4 ] = 0 x0;
prop[5 ] = 0 x00010000;
prom_setprop(ph, name, "ranges" , prop, sizeof (prop));
}
}
name = "/pci@80000000/ide@C,1" ;
ph = call_prom("finddevice" , 1 , 1 , ADDR(name));
if (PHANDLE_VALID(ph)) {
prom_printf("Fixing up IDE interrupt on Pegasos...\n" );
prop[0 ] = 14 ;
prop[1 ] = 0 x0;
prom_setprop(ph, name, "interrupts" , prop, 2 *sizeof (u32));
prom_printf("Fixing up IDE class-code on Pegasos...\n" );
rc = prom_getprop(ph, "class-code" , prop, sizeof (u32));
if (rc == sizeof (u32)) {
prop[0 ] &= ~0 x5;
prom_setprop(ph, name, "class-code" , prop, sizeof (u32));
}
}
}
static void __init fixup_device_tree_pmac64(void )
{
phandle u3, i2c, mpic;
u32 u3_rev;
u32 interrupts[2 ];
u32 parent;
/* Some G5s have a missing interrupt definition, fix it up here */
u3 = call_prom("finddevice" , 1 , 1 , ADDR("/u3@0,f8000000" ));
if (!PHANDLE_VALID(u3))
return ;
i2c = call_prom("finddevice" , 1 , 1 , ADDR("/u3@0,f8000000/i2c@f8001000" ));
if (!PHANDLE_VALID(i2c))
return ;
mpic = call_prom("finddevice" , 1 , 1 , ADDR("/u3@0,f8000000/mpic@f8040000" ));
if (!PHANDLE_VALID(mpic))
return ;
/* check if proper rev of u3 */
if (prom_getprop(u3, "device-rev" , &u3_rev, sizeof (u3_rev))
== PROM_ERROR)
return ;
if (u3_rev < 0 x35 || u3_rev > 0 x39)
return ;
/* does it need fixup ? */
if (prom_getproplen(i2c, "interrupts" ) > 0 )
return ;
prom_printf("fixing up bogus interrupts for u3 i2c...\n" );
/* interrupt on this revision of u3 is number 0 and level */
interrupts[0 ] = 0 ;
interrupts[1 ] = 1 ;
prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000" , "interrupts" ,
&interrupts, sizeof (interrupts));
parent = (u32)mpic;
prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000" , "interrupt-parent" ,
&parent, sizeof (parent));
}
static void __init fixup_device_tree_pmac(void )
{
__be32 val = 1 ;
char type[8 ];
phandle node;
// Some pmacs are missing #size-cells on escc or i2s nodes
for (node = 0 ; prom_next_node(&node); ) {
type[0 ] = '\0' ;
prom_getprop(node, "device_type" , type, sizeof (type));
if (prom_strcmp(type, "escc" ) && prom_strcmp(type, "i2s" ))
continue ;
if (prom_getproplen(node, "#size-cells" ) != PROM_ERROR)
continue ;
prom_setprop(node, NULL, "#size-cells" , &val, sizeof (val));
}
}
/*
* The MPC5200 FEC driver requires an phy - handle property to tell it how
* to talk to the phy . If the phy - handle property is missing , then this
* function is called to add the appropriate nodes and link it to the
* ethernet node .
*/
static void __init fixup_device_tree_efika_add_phy(void )
{
u32 node;
char prop[64 ];
int rv;
/* Check if /builtin/ethernet exists - bail if it doesn't */
node = call_prom("finddevice" , 1 , 1 , ADDR("/builtin/ethernet" ));
if (!PHANDLE_VALID(node))
return ;
/* Check if the phy-handle property exists - bail if it does */
rv = prom_getprop(node, "phy-handle" , prop, sizeof (prop));
if (rv <= 0 )
return ;
/*
* At this point the ethernet device doesn ' t have a phy described .
* Now we need to add the missing phy node and linkage
*/
/* Check for an MDIO bus node - if missing then create one */
node = call_prom("finddevice" , 1 , 1 , ADDR("/builtin/mdio" ));
if (!PHANDLE_VALID(node)) {
prom_printf("Adding Ethernet MDIO node\n" );
call_prom("interpret" , 1 , 1 ,
" s\" /builtin\" find-device"
" new-device"
" 1 encode-int s\" #address -cells\" property"
" 0 encode-int s\" #size -cells\" property"
" s\" mdio\" device-name"
" s\" fsl,mpc5200b-mdio\" encode-string"
" s\" compatible\" property"
" 0xf0003000 0x400 reg"
" 0x2 encode-int"
" 0x5 encode-int encode+"
" 0x3 encode-int encode+"
" s\" interrupts\" property"
" finish-device" );
}
/* Check for a PHY device node - if missing then create one and
* give it's phandle to the ethernet node */
node = call_prom("finddevice" , 1 , 1 ,
ADDR("/builtin/mdio/ethernet-phy" ));
if (!PHANDLE_VALID(node)) {
prom_printf("Adding Ethernet PHY node\n" );
call_prom("interpret" , 1 , 1 ,
" s\" /builtin/mdio\" find-device"
" new-device"
" s\" ethernet-phy\" device-name"
" 0x10 encode-int s\" reg\" property"
" my-self"
" ihandle>phandle"
" finish-device"
" s\" /builtin/ethernet\" find-device"
" encode-int"
" s\" phy-handle\" property"
" device-end" );
}
}
static void __init fixup_device_tree_efika(void )
{
int sound_irq[3 ] = { 2 , 2 , 0 };
int bcomm_irq[3 *16 ] = { 3 ,0 ,0 , 3 ,1 ,0 , 3 ,2 ,0 , 3 ,3 ,0 ,
3 ,4 ,0 , 3 ,5 ,0 , 3 ,6 ,0 , 3 ,7 ,0 ,
3 ,8 ,0 , 3 ,9 ,0 , 3 ,10 ,0 , 3 ,11 ,0 ,
3 ,12 ,0 , 3 ,13 ,0 , 3 ,14 ,0 , 3 ,15 ,0 };
u32 node;
char prop[64 ];
int rv, len;
/* Check if we're really running on a EFIKA */
node = call_prom("finddevice" , 1 , 1 , ADDR("/" ));
if (!PHANDLE_VALID(node))
return ;
rv = prom_getprop(node, "model" , prop, sizeof (prop));
if (rv == PROM_ERROR)
return ;
if (prom_strcmp(prop, "EFIKA5K2" ))
return ;
prom_printf("Applying EFIKA device tree fixups\n" );
/* Claiming to be 'chrp' is death */
node = call_prom("finddevice" , 1 , 1 , ADDR("/" ));
rv = prom_getprop(node, "device_type" , prop, sizeof (prop));
if (rv != PROM_ERROR && (prom_strcmp(prop, "chrp" ) == 0 ))
prom_setprop(node, "/" , "device_type" , "efika" , sizeof ("efika" ));
/* CODEGEN,description is exposed in /proc/cpuinfo so
fix that too */
rv = prom_getprop(node, "CODEGEN,description" , prop, sizeof (prop));
if (rv != PROM_ERROR && (prom_strstr(prop, "CHRP" )))
prom_setprop(node, "/" , "CODEGEN,description" ,
"Efika 5200B PowerPC System" ,
sizeof ("Efika 5200B PowerPC System" ));
/* Fixup bestcomm interrupts property */
node = call_prom("finddevice" , 1 , 1 , ADDR("/builtin/bestcomm" ));
if (PHANDLE_VALID(node)) {
len = prom_getproplen(node, "interrupts" );
if (len == 12 ) {
prom_printf("Fixing bestcomm interrupts property\n" );
prom_setprop(node, "/builtin/bestcom" , "interrupts" ,
bcomm_irq, sizeof (bcomm_irq));
}
}
/* Fixup sound interrupts property */
node = call_prom("finddevice" , 1 , 1 , ADDR("/builtin/sound" ));
if (PHANDLE_VALID(node)) {
rv = prom_getprop(node, "interrupts" , prop, sizeof (prop));
if (rv == PROM_ERROR) {
prom_printf("Adding sound interrupts property\n" );
prom_setprop(node, "/builtin/sound" , "interrupts" ,
sound_irq, sizeof (sound_irq));
}
}
/* Make sure ethernet phy-handle property exists */
fixup_device_tree_efika_add_phy();
}
/*
* CFE supplied on Nemo is broken in several ways , biggest
* problem is that it reassigns ISA interrupts to unused mpic ints .
* Add an interrupt - controller property for the io - bridge to use
* and correct the ints so we can attach them to an irq_domain
*/
static void __init fixup_device_tree_pasemi(void )
{
u32 interrupts[2 ], parent, rval, val = 0 ;
char *name, *pci_name;
phandle iob, node;
/* Find the root pci node */
name = "/pxp@0,e0000000" ;
iob = call_prom("finddevice" , 1 , 1 , ADDR(name));
if (!PHANDLE_VALID(iob))
return ;
/* check if interrupt-controller node set yet */
if (prom_getproplen(iob, "interrupt-controller" ) !=PROM_ERROR)
return ;
prom_printf("adding interrupt-controller property for SB600...\n" );
prom_setprop(iob, name, "interrupt-controller" , &val, 0 );
pci_name = "/pxp@0,e0000000/pci@11" ;
node = call_prom("finddevice" , 1 , 1 , ADDR(pci_name));
parent = ADDR(iob);
for ( ; prom_next_node(&node); ) {
/* scan each node for one with an interrupt */
if (!PHANDLE_VALID(node))
continue ;
rval = prom_getproplen(node, "interrupts" );
if (rval == 0 || rval == PROM_ERROR)
continue ;
prom_getprop(node, "interrupts" , &interrupts, sizeof (interrupts));
if ((interrupts[0 ] < 212 ) || (interrupts[0 ] > 222 ))
continue ;
/* found a node, update both interrupts and interrupt-parent */
if ((interrupts[0 ] >= 212 ) && (interrupts[0 ] <= 215 ))
interrupts[0 ] -= 203 ;
if ((interrupts[0 ] >= 216 ) && (interrupts[0 ] <= 220 ))
interrupts[0 ] -= 213 ;
if (interrupts[0 ] == 221 )
interrupts[0 ] = 14 ;
if (interrupts[0 ] == 222 )
interrupts[0 ] = 8 ;
prom_setprop(node, pci_name, "interrupts" , interrupts,
sizeof (interrupts));
prom_setprop(node, pci_name, "interrupt-parent" , &parent,
sizeof (parent));
}
/*
* The io - bridge has device_type set to ' io - bridge ' change it to ' isa '
* so that generic isa - bridge code can add the SB600 and its on - board
* peripherals .
*/
name = "/pxp@0,e0000000/io-bridge@0" ;
iob = call_prom("finddevice" , 1 , 1 , ADDR(name));
if (!PHANDLE_VALID(iob))
return ;
/* device_type is already set, just change it. */
prom_printf("Changing device_type of SB600 node...\n" );
prom_setprop(iob, name, "device_type" , "isa" , sizeof ("isa" ));
}
static void __init fixup_device_tree(void )
{
if (IS_ENABLED(CONFIG_PPC_CHRP))
fixup_device_tree_chrp();
if (IS_ENABLED(CONFIG_PPC_PMAC))
fixup_device_tree_pmac();
if (IS_ENABLED(CONFIG_PPC_PMAC) && IS_ENABLED(CONFIG_PPC64))
fixup_device_tree_pmac64();
if (IS_ENABLED(CONFIG_PPC_EFIKA))
fixup_device_tree_efika();
if (IS_ENABLED(CONFIG_PPC_PASEMI_NEMO))
fixup_device_tree_pasemi();
}
static void __init prom_find_boot_cpu(void )
{
__be32 rval;
ihandle prom_cpu;
phandle cpu_pkg;
rval = 0 ;
if (prom_getprop(prom.chosen, "cpu" , &rval, sizeof (rval)) <= 0 )
return ;
prom_cpu = be32_to_cpu(rval);
cpu_pkg = call_prom("instance-to-package" , 1 , 1 , prom_cpu);
if (!PHANDLE_VALID(cpu_pkg))
return ;
prom_getprop(cpu_pkg, "reg" , &rval, sizeof (rval));
prom.cpu = be32_to_cpu(rval);
prom_debug("Booting CPU hw index = %d\n" , prom.cpu);
}
static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
{
#ifdef CONFIG_BLK_DEV_INITRD
if (r3 && r4 && r4 != 0 xdeadbeef) {
__be64 val;
prom_initrd_start = is_kernel_addr(r3) ? __pa(r3) : r3;
prom_initrd_end = prom_initrd_start + r4;
val = cpu_to_be64(prom_initrd_start);
prom_setprop(prom.chosen, "/chosen" , "linux,initrd-start" ,
&val, sizeof (val));
val = cpu_to_be64(prom_initrd_end);
prom_setprop(prom.chosen, "/chosen" , "linux,initrd-end" ,
&val, sizeof (val));
reserve_mem(prom_initrd_start,
prom_initrd_end - prom_initrd_start);
prom_debug("initrd_start=0x%lx\n" , prom_initrd_start);
prom_debug("initrd_end=0x%lx\n" , prom_initrd_end);
}
#endif /* CONFIG_BLK_DEV_INITRD */
}
#ifdef CONFIG_PPC_SVM
/*
* Perform the Enter Secure Mode ultracall .
*/
static int __init enter_secure_mode(unsigned long kbase, unsigned long fdt)
{
register unsigned long r3 asm ("r3" ) = UV_ESM;
register unsigned long r4 asm ("r4" ) = kbase;
register unsigned long r5 asm ("r5" ) = fdt;
asm volatile ("sc 2" : "+r" (r3) : "r" (r4), "r" (r5));
return r3;
}
/*
* Call the Ultravisor to transfer us to secure memory if we have an ESM blob .
*/
static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt)
{
int ret;
if (!prom_svm_enable)
return ;
/* Switch to secure mode. */
prom_printf("Switching to secure mode.\n" );
/*
* The ultravisor will do an integrity check of the kernel image but we
* relocated it so the check will fail . Restore the original image by
* relocating it back to the kernel virtual base address .
*/
relocate(KERNELBASE);
ret = enter_secure_mode(kbase, fdt);
/* Relocate the kernel again. */
relocate(kbase);
if (ret != U_SUCCESS) {
prom_printf("Returned %d from switching to secure mode.\n" , ret);
prom_rtas_os_term("Switch to secure mode failed.\n" );
}
}
#else
static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt)
{
}
#endif /* CONFIG_PPC_SVM */
/*
* We enter here early on , when the Open Firmware prom is still
* handling exceptions and the MMU hash table for us .
*/
unsigned long __init prom_init(unsigned long r3, unsigned long r4,
unsigned long pp,
unsigned long r6, unsigned long r7,
unsigned long kbase)
{
unsigned long hdr;
#ifdef CONFIG_PPC32
unsigned long offset = reloc_offset();
reloc_got2(offset);
#endif
/*
* First zero the BSS
*/
memset(&__bss_start, 0 , __bss_stop - __bss_start);
/*
* Init interface to Open Firmware , get some node references ,
* like / chosen
*/
prom_init_client_services(pp);
/*
* See if this OF is old enough that we need to do explicit maps
* and other workarounds
*/
prom_find_mmu();
/*
* Init prom stdout device
*/
prom_init_stdout();
prom_printf("Preparing to boot %s" , linux_banner);
/*
* Get default machine type . At this point , we do not differentiate
* between pSeries SMP and pSeries LPAR
*/
of_platform = prom_find_machine_type();
prom_printf("Detected machine type: %x\n" , of_platform);
#ifndef CONFIG_NONSTATIC_KERNEL
/* Bail if this is a kdump kernel. */
if (PHYSICAL_START > 0 )
prom_panic("Error: You can't boot a kdump kernel from OF!\n" );
#endif
/*
* Check for an initrd
*/
prom_check_initrd(r3, r4);
/*
* Do early parsing of command line
*/
early_cmdline_parse();
#ifdef CONFIG_PPC_PSERIES
/*
* On pSeries , inform the firmware about our capabilities
*/
if (of_platform == PLATFORM_PSERIES ||
of_platform == PLATFORM_PSERIES_LPAR)
prom_send_capabilities();
#endif
/*
* Copy the CPU hold code
*/
if (of_platform != PLATFORM_POWERMAC)
copy_and_flush(0 , kbase, 0 x100, 0 );
/*
* Initialize memory management within prom_init
*/
prom_init_mem();
/*
* Determine which cpu is actually running right _ now_
*/
prom_find_boot_cpu();
/*
* Initialize display devices
*/
prom_check_displays();
#if defined (CONFIG_PPC64) && defined (__BIG_ENDIAN__)
/*
* Initialize IOMMU ( TCE tables ) on pSeries . Do that before anything else
* that uses the allocator , we need to make sure we get the top of memory
* available for us here . . .
*/
if (of_platform == PLATFORM_PSERIES)
prom_initialize_tce_table();
#endif
/*
* On non - powermacs , try to instantiate RTAS . PowerMacs don ' t
* have a usable RTAS implementation .
*/
if (of_platform != PLATFORM_POWERMAC)
prom_instantiate_rtas();
#ifdef CONFIG_PPC64
/* instantiate sml */
prom_instantiate_sml();
#endif
/*
* On non - powermacs , put all CPUs in spin - loops .
*
* PowerMacs use a different mechanism to spin CPUs
*
* ( This must be done after instantiating RTAS )
*/
if (of_platform != PLATFORM_POWERMAC)
prom_hold_cpus();
/*
* Fill in some infos for use by the kernel later on
*/
if (prom_memory_limit) {
__be64 val = cpu_to_be64(prom_memory_limit);
prom_setprop(prom.chosen, "/chosen" , "linux,memory-limit" ,
&val, sizeof (val));
}
#ifdef CONFIG_PPC64
if (prom_iommu_off)
prom_setprop(prom.chosen, "/chosen" , "linux,iommu-off" ,
NULL, 0 );
if (prom_iommu_force_on)
prom_setprop(prom.chosen, "/chosen" , "linux,iommu-force-on" ,
NULL, 0 );
if (prom_tce_alloc_start) {
prom_setprop(prom.chosen, "/chosen" , "linux,tce-alloc-start" ,
&prom_tce_alloc_start,
sizeof (prom_tce_alloc_start));
prom_setprop(prom.chosen, "/chosen" , "linux,tce-alloc-end" ,
&prom_tce_alloc_end,
sizeof (prom_tce_alloc_end));
}
#endif
/*
* Fixup any known bugs in the device - tree
*/
fixup_device_tree();
/*
* Now finally create the flattened device - tree
*/
prom_printf("copying OF device tree...\n" );
flatten_device_tree();
/*
* in case stdin is USB and still active on IBM machines . . .
* Unfortunately quiesce crashes on some powermacs if we have
* closed stdin already ( in particular the powerbook 101 ) .
*/
if (of_platform != PLATFORM_POWERMAC)
prom_close_stdin();
/*
* Call OF " quiesce " method to shut down pending DMA ' s from
* devices etc . . .
*/
prom_printf("Quiescing Open Firmware ...\n" );
call_prom("quiesce" , 0 , 0 );
/*
* And finally , call the kernel passing it the flattened device
* tree and NULL as r5 , thus triggering the new entry point which
* is common to us and kexec
*/
hdr = dt_header_start;
prom_printf("Booting Linux via __start() @ 0x%lx ...\n" , kbase);
prom_debug("->dt_header_start=0x%lx\n" , hdr);
#ifdef CONFIG_PPC32
reloc_got2(-offset);
#endif
/* Move to secure memory if we're supposed to be secure guests. */
setup_secure_guest(kbase, hdr);
__start(hdr, kbase, 0 , 0 , 0 , 0 , 0 );
return 0 ;
}
Messung V0.5 in Prozent C=93 H=92 G=92
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.71Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤
*Eine klare Vorstellung vom Zielzustand