2009-08-30 17:15:11 -07:00
/*
* Carsten Langgaard , carstenl @ mips . com
* Copyright ( C ) 2000 MIPS Technologies , Inc . All rights reserved .
* Portions copyright ( C ) 2009 Cisco Systems , Inc .
*
* This program is free software ; you can distribute it and / or modify it
* under the terms of the GNU General Public License ( Version 2 ) as
* published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston MA 02111 - 1307 , USA .
*/
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/ioport.h>
# include <linux/pci.h>
# include <linux/screen_info.h>
# include <linux/notifier.h>
# include <linux/etherdevice.h>
# include <linux/if_ether.h>
# include <linux/ctype.h>
# include <linux/cpu.h>
2010-02-22 16:31:13 -08:00
# include <linux/time.h>
2009-08-30 17:15:11 -07:00
# include <asm/bootinfo.h>
# include <asm/irq.h>
# include <asm/mips-boards/generic.h>
# include <asm/mips-boards/prom.h>
# include <asm/dma.h>
2010-02-22 16:31:13 -08:00
# include <asm/asm.h>
2009-08-30 17:15:11 -07:00
# include <asm/traps.h>
# include <asm/asm-offsets.h>
# include "reset.h"
# define VAL(n) STR(n)
/*
* Macros for loading addresses and storing registers :
2010-02-22 16:31:13 -08:00
* LONG_L_ Stringified version of LONG_L for use in asm ( ) statement
* LONG_S_ Stringified version of LONG_S for use in asm ( ) statement
* PTR_LA_ Stringified version of PTR_LA for use in asm ( ) statement
2009-08-30 17:15:11 -07:00
* REG_SIZE Number of 8 - bit bytes in a full width register
*/
2010-02-22 16:31:13 -08:00
# define LONG_L_ VAL(LONG_L) " "
# define LONG_S_ VAL(LONG_S) " "
# define PTR_LA_ VAL(PTR_LA) " "
2009-08-30 17:15:11 -07:00
# ifdef CONFIG_64BIT
# warning TODO: 64-bit code needs to be verified
# define REG_SIZE "8" /* In bytes */
# endif
# ifdef CONFIG_32BIT
# define REG_SIZE "4" /* In bytes */
# endif
static void register_panic_notifier ( void ) ;
static int panic_handler ( struct notifier_block * notifier_block ,
unsigned long event , void * cause_string ) ;
const char * get_system_type ( void )
{
return " PowerTV " ;
}
void __init plat_mem_setup ( void )
{
panic_on_oops = 1 ;
register_panic_notifier ( ) ;
#if 0
mips_pcibios_init ( ) ;
# endif
mips_reboot_setup ( ) ;
}
/*
* Install a panic notifier for platform - specific diagnostics
*/
static void register_panic_notifier ( )
{
static struct notifier_block panic_notifier = {
. notifier_call = panic_handler ,
. next = NULL ,
. priority = INT_MAX
} ;
atomic_notifier_chain_register ( & panic_notifier_list , & panic_notifier ) ;
}
static int panic_handler ( struct notifier_block * notifier_block ,
unsigned long event , void * cause_string )
{
struct pt_regs my_regs ;
/* Save all of the registers */
{
unsigned long at , v0 , v1 ; /* Must be on the stack */
/* Start by saving $at and v0 on the stack. We use $at
* ourselves , but it looks like the compiler may use v0 or v1
* to load the address of the pt_regs structure . We ' ll come
* back later to store the registers in the pt_regs
* structure . */
__asm__ __volatile__ (
" .set noat \n "
2010-02-22 16:31:13 -08:00
LONG_S_ " $at, %[at] \n "
LONG_S_ " $2, %[v0] \n "
LONG_S_ " $3, %[v1] \n "
2009-08-30 17:15:11 -07:00
:
[ at ] " =m " ( at ) ,
[ v0 ] " =m " ( v0 ) ,
[ v1 ] " =m " ( v1 )
:
: " at "
) ;
__asm__ __volatile__ (
" .set noat \n "
" move $at, %[pt_regs] \n "
/* Argument registers */
2010-02-22 16:31:13 -08:00
LONG_S_ " $4, " VAL ( PT_R4 ) " ($at) \n "
LONG_S_ " $5, " VAL ( PT_R5 ) " ($at) \n "
LONG_S_ " $6, " VAL ( PT_R6 ) " ($at) \n "
LONG_S_ " $7, " VAL ( PT_R7 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* Temporary regs */
2010-02-22 16:31:13 -08:00
LONG_S_ " $8, " VAL ( PT_R8 ) " ($at) \n "
LONG_S_ " $9, " VAL ( PT_R9 ) " ($at) \n "
LONG_S_ " $10, " VAL ( PT_R10 ) " ($at) \n "
LONG_S_ " $11, " VAL ( PT_R11 ) " ($at) \n "
LONG_S_ " $12, " VAL ( PT_R12 ) " ($at) \n "
LONG_S_ " $13, " VAL ( PT_R13 ) " ($at) \n "
LONG_S_ " $14, " VAL ( PT_R14 ) " ($at) \n "
LONG_S_ " $15, " VAL ( PT_R15 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* "Saved" registers */
2010-02-22 16:31:13 -08:00
LONG_S_ " $16, " VAL ( PT_R16 ) " ($at) \n "
LONG_S_ " $17, " VAL ( PT_R17 ) " ($at) \n "
LONG_S_ " $18, " VAL ( PT_R18 ) " ($at) \n "
LONG_S_ " $19, " VAL ( PT_R19 ) " ($at) \n "
LONG_S_ " $20, " VAL ( PT_R20 ) " ($at) \n "
LONG_S_ " $21, " VAL ( PT_R21 ) " ($at) \n "
LONG_S_ " $22, " VAL ( PT_R22 ) " ($at) \n "
LONG_S_ " $23, " VAL ( PT_R23 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* Add'l temp regs */
2010-02-22 16:31:13 -08:00
LONG_S_ " $24, " VAL ( PT_R24 ) " ($at) \n "
LONG_S_ " $25, " VAL ( PT_R25 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* Kernel temp regs */
2010-02-22 16:31:13 -08:00
LONG_S_ " $26, " VAL ( PT_R26 ) " ($at) \n "
LONG_S_ " $27, " VAL ( PT_R27 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* Global pointer, stack pointer, frame pointer and
* return address */
2010-02-22 16:31:13 -08:00
LONG_S_ " $gp, " VAL ( PT_R28 ) " ($at) \n "
LONG_S_ " $sp, " VAL ( PT_R29 ) " ($at) \n "
LONG_S_ " $fp, " VAL ( PT_R30 ) " ($at) \n "
LONG_S_ " $ra, " VAL ( PT_R31 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
/* Now we can get the $at and v0 registers back and
* store them */
2010-02-22 16:31:13 -08:00
LONG_L_ " $8, %[at] \n "
LONG_S_ " $8, " VAL ( PT_R1 ) " ($at) \n "
LONG_L_ " $8, %[v0] \n "
LONG_S_ " $8, " VAL ( PT_R2 ) " ($at) \n "
LONG_L_ " $8, %[v1] \n "
LONG_S_ " $8, " VAL ( PT_R3 ) " ($at) \n "
2009-08-30 17:15:11 -07:00
:
:
[ at ] " m " ( at ) ,
[ v0 ] " m " ( v0 ) ,
[ v1 ] " m " ( v1 ) ,
[ pt_regs ] " r " ( & my_regs )
: " at " , " t0 "
) ;
/* Set the current EPC value to be the current location in this
* function */
__asm__ __volatile__ (
" .set noat \n "
" 1: \n "
2010-02-22 16:31:13 -08:00
PTR_LA_ " $at, 1b \n "
LONG_S_ " $at, %[cp0_epc] \n "
2009-08-30 17:15:11 -07:00
:
[ cp0_epc ] " =m " ( my_regs . cp0_epc )
:
: " at "
) ;
my_regs . cp0_cause = read_c0_cause ( ) ;
my_regs . cp0_status = read_c0_status ( ) ;
}
pr_crit ( " I'm feeling a bit sleepy. hmmmmm... perhaps a nap would... "
" zzzz... \n " ) ;
return NOTIFY_DONE ;
}
/* Information about the RF MAC address, if one was supplied on the
* command line . */
static bool have_rfmac ;
static u8 rfmac [ ETH_ALEN ] ;
static int rfmac_param ( char * p )
{
u8 * q ;
bool is_high_nibble ;
int c ;
/* Skip a leading "0x", if present */
if ( * p = = ' 0 ' & & * ( p + 1 ) = = ' x ' )
p + = 2 ;
q = rfmac ;
is_high_nibble = true ;
for ( c = ( unsigned char ) * p + + ;
isxdigit ( c ) & & q - rfmac < ETH_ALEN ;
c = ( unsigned char ) * p + + ) {
int nibble ;
nibble = ( isdigit ( c ) ? ( c - ' 0 ' ) :
( isupper ( c ) ? c - ' A ' + 10 : c - ' a ' + 10 ) ) ;
if ( is_high_nibble )
* q = nibble < < 4 ;
else
* q + + | = nibble ;
is_high_nibble = ! is_high_nibble ;
}
/* If we parsed all the way to the end of the parameter value and
* parsed all ETH_ALEN bytes , we have a usable RF MAC address */
have_rfmac = ( c = = ' \0 ' & & q - rfmac = = ETH_ALEN ) ;
return 0 ;
}
early_param ( " rfmac " , rfmac_param ) ;
/*
* Generate an Ethernet MAC address that has a good chance of being unique .
* @ addr : Pointer to six - byte array containing the Ethernet address
* Generates an Ethernet MAC address that is highly likely to be unique for
* this particular system on a network with other systems of the same type .
*
* The problem we are solving is that , when random_ether_addr ( ) is used to
* generate MAC addresses at startup , there isn ' t much entropy for the random
* number generator to use and the addresses it produces are fairly likely to
* be the same as those of other identical systems on the same local network .
* This is true even for relatively small numbers of systems ( for the reason
* why , see the Wikipedia entry for " Birthday problem " at :
* http : //en.wikipedia.org/wiki/Birthday_problem
*
* The good news is that we already have a MAC address known to be unique , the
* RF MAC address . The bad news is that this address is already in use on the
* RF interface . Worse , the obvious trick , taking the RF MAC address and
* turning on the locally managed bit , has already been used for other devices .
* Still , this does give us something to work with .
*
* The approach we take is :
* 1. If we can ' t get the RF MAC Address , just call random_ether_addr .
* 2. Use the 24 - bit NIC - specific bits of the RF MAC address as the last 24
* bits of the new address . This is very likely to be unique , except for
* the current box .
* 3. To avoid using addresses already on the current box , we set the top
* six bits of the address with a value different from any currently
* registered Scientific Atlanta organizationally unique identifyer
* ( OUI ) . This avoids duplication with any addresses on the system that
* were generated from valid Scientific Atlanta - registered address by
* simply flipping the locally managed bit .
* 4. We aren ' t generating a multicast address , so we leave the multicast
* bit off . Since we aren ' t using a registered address , we have to set
* the locally managed bit .
* 5. We then randomly generate the remaining 16 - bits . This does two
* things :
* a . It allows us to call this function for more than one device
* in this system
* b . It ensures that things will probably still work even if
* some device on the device network has a locally managed
* address that matches the top six bits from step 2.
*/
void platform_random_ether_addr ( u8 addr [ ETH_ALEN ] )
{
const int num_random_bytes = 2 ;
const unsigned char non_sciatl_oui_bits = 0xc0u ;
const unsigned char mac_addr_locally_managed = ( 1 < < 1 ) ;
if ( ! have_rfmac ) {
pr_warning ( " rfmac not available on command line; "
" generating random MAC address \n " ) ;
random_ether_addr ( addr ) ;
}
else {
int i ;
/* Set the first byte to something that won't match a Scientific
* Atlanta OUI , is locally managed , and isn ' t a multicast
* address */
addr [ 0 ] = non_sciatl_oui_bits | mac_addr_locally_managed ;
/* Get some bytes of random address information */
get_random_bytes ( & addr [ 1 ] , num_random_bytes ) ;
/* Copy over the NIC-specific bits of the RF MAC address */
for ( i = 1 + num_random_bytes ; i < ETH_ALEN ; i + + )
addr [ i ] = rfmac [ i ] ;
}
}