2005-04-16 15:20:36 -07:00
/*
* arch / s390 / kernel / setup . c
*
* S390 version
* Copyright ( C ) 1999 , 2000 IBM Deutschland Entwicklung GmbH , IBM Corporation
* Author ( s ) : Hartmut Penner ( hp @ de . ibm . com ) ,
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
*
* Derived from " arch/i386/kernel/setup.c "
* Copyright ( C ) 1995 , Linus Torvalds
*/
/*
* This file handles the architecture - dependent parts of initialization
*/
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/user.h>
# include <linux/tty.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/initrd.h>
# include <linux/bootmem.h>
# include <linux/root_dev.h>
# include <linux/console.h>
# include <linux/seq_file.h>
# include <linux/kernel_stat.h>
2005-10-30 15:00:11 -08:00
# include <linux/device.h>
2006-06-29 15:08:25 +02:00
# include <linux/notifier.h>
2006-09-20 15:58:41 +02:00
# include <linux/pfn.h>
2007-02-05 21:18:24 +01:00
# include <linux/ctype.h>
2007-02-05 21:16:47 +01:00
# include <linux/reboot.h>
2008-04-17 07:46:12 +02:00
# include <linux/topology.h>
2005-04-16 15:20:36 -07:00
2007-02-21 10:55:21 +01:00
# include <asm/ipl.h>
2005-04-16 15:20:36 -07:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/smp.h>
# include <asm/mmu_context.h>
# include <asm/cpcmd.h>
# include <asm/lowcore.h>
# include <asm/irq.h>
2005-05-01 08:58:58 -07:00
# include <asm/page.h>
# include <asm/ptrace.h>
2006-06-25 05:49:30 -07:00
# include <asm/sections.h>
2007-02-05 21:18:24 +01:00
# include <asm/ebcdic.h>
2007-02-05 21:18:17 +01:00
# include <asm/compat.h>
2008-06-20 15:24:18 +02:00
# include <asm/kvm_virtio.h>
2007-02-05 21:18:17 +01:00
long psw_kernel_bits = ( PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY |
PSW_MASK_MCHECK | PSW_DEFAULT_KEY ) ;
long psw_user_bits = ( PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
PSW_MASK_PSTATE | PSW_DEFAULT_KEY ) ;
2005-04-16 15:20:36 -07:00
2006-09-20 15:59:42 +02:00
/*
* User copy operations .
*/
struct uaccess_ops uaccess ;
2007-05-04 18:47:48 +02:00
EXPORT_SYMBOL ( uaccess ) ;
2006-09-20 15:59:42 +02:00
2005-04-16 15:20:36 -07:00
/*
* Machine setup . .
*/
unsigned int console_mode = 0 ;
unsigned int console_devno = - 1 ;
unsigned int console_irq = - 1 ;
2008-04-30 13:38:45 +02:00
unsigned long machine_flags ;
2007-05-04 18:48:28 +02:00
unsigned long elf_hwcap = 0 ;
char elf_platform [ ELF_PLATFORM_SIZE ] ;
2006-12-04 15:40:38 +01:00
2008-07-14 09:59:21 +02:00
struct mem_chunk __initdata memory_chunk [ MEMORY_CHUNKS ] ;
2005-04-16 15:20:36 -07:00
volatile int __cpu_logical_map [ NR_CPUS ] ; /* logical cpu to cpu address */
2005-05-01 08:58:57 -07:00
static unsigned long __initdata memory_end ;
2005-04-16 15:20:36 -07:00
/*
* This is set up by the setup - routine at boot - time
* for S390 need to find out , what we have to setup
* using address 0x10400 . . .
*/
# include <asm/setup.h>
static struct resource code_resource = {
. name = " Kernel code " ,
. flags = IORESOURCE_BUSY | IORESOURCE_MEM ,
} ;
static struct resource data_resource = {
. name = " Kernel data " ,
. flags = IORESOURCE_BUSY | IORESOURCE_MEM ,
} ;
/*
* cpu_init ( ) initializes state that is per - CPU .
*/
2007-05-31 17:38:05 +02:00
void __cpuinit cpu_init ( void )
2005-04-16 15:20:36 -07:00
{
int addr = hard_smp_processor_id ( ) ;
/*
* Store processor id in lowcore ( used e . g . in timer_interrupt )
*/
2007-02-21 10:55:18 +01:00
get_cpu_id ( & S390_lowcore . cpu_data . cpu_id ) ;
2005-04-16 15:20:36 -07:00
S390_lowcore . cpu_data . cpu_addr = addr ;
/*
* Force FPU initialization :
*/
clear_thread_flag ( TIF_USEDFPU ) ;
clear_used_math ( ) ;
atomic_inc ( & init_mm . mm_count ) ;
current - > active_mm = & init_mm ;
if ( current - > mm )
BUG ( ) ;
enter_lazy_tlb ( & init_mm , current ) ;
}
/*
* condev = and conmode = setup parameter .
*/
static int __init condev_setup ( char * str )
{
int vdev ;
vdev = simple_strtoul ( str , & str , 0 ) ;
if ( vdev > = 0 & & vdev < 65536 ) {
console_devno = vdev ;
console_irq = - 1 ;
}
return 1 ;
}
__setup ( " condev= " , condev_setup ) ;
static int __init conmode_setup ( char * str )
{
2008-02-05 16:50:41 +01:00
# if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
2005-04-16 15:20:36 -07:00
if ( strncmp ( str , " hwc " , 4 ) = = 0 | | strncmp ( str , " sclp " , 5 ) = = 0 )
SET_CONSOLE_SCLP ;
# endif
# if defined(CONFIG_TN3215_CONSOLE)
if ( strncmp ( str , " 3215 " , 5 ) = = 0 )
SET_CONSOLE_3215 ;
# endif
# if defined(CONFIG_TN3270_CONSOLE)
if ( strncmp ( str , " 3270 " , 5 ) = = 0 )
SET_CONSOLE_3270 ;
# endif
return 1 ;
}
__setup ( " conmode= " , conmode_setup ) ;
static void __init conmode_default ( void )
{
char query_buffer [ 1024 ] ;
char * ptr ;
if ( MACHINE_IS_VM ) {
2006-12-04 15:40:30 +01:00
cpcmd ( " QUERY CONSOLE " , query_buffer , 1024 , NULL ) ;
2005-04-16 15:20:36 -07:00
console_devno = simple_strtoul ( query_buffer + 5 , NULL , 16 ) ;
ptr = strstr ( query_buffer , " SUBCHANNEL = " ) ;
console_irq = simple_strtoul ( ptr + 13 , NULL , 16 ) ;
2006-12-04 15:40:30 +01:00
cpcmd ( " QUERY TERM " , query_buffer , 1024 , NULL ) ;
2005-04-16 15:20:36 -07:00
ptr = strstr ( query_buffer , " CONMODE " ) ;
/*
* Set the conmode to 3215 so that the device recognition
* will set the cu_type of the console to 3215. If the
* conmode is 3270 and we don ' t set it back then both
* 3215 and the 3270 driver will try to access the console
* device ( 3215 as console and 3270 as normal tty ) .
*/
2006-12-04 15:40:30 +01:00
cpcmd ( " TERM CONMODE 3215 " , NULL , 0 , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ptr = = NULL ) {
2008-02-05 16:50:41 +01:00
# if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
2005-04-16 15:20:36 -07:00
SET_CONSOLE_SCLP ;
# endif
return ;
}
if ( strncmp ( ptr + 8 , " 3270 " , 4 ) = = 0 ) {
# if defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270 ;
# elif defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215 ;
2008-02-05 16:50:41 +01:00
# elif defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
2005-04-16 15:20:36 -07:00
SET_CONSOLE_SCLP ;
# endif
} else if ( strncmp ( ptr + 8 , " 3215 " , 4 ) = = 0 ) {
# if defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215 ;
# elif defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270 ;
2008-02-05 16:50:41 +01:00
# elif defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
2005-04-16 15:20:36 -07:00
SET_CONSOLE_SCLP ;
# endif
}
} else {
2008-02-05 16:50:41 +01:00
# if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
2005-04-16 15:20:36 -07:00
SET_CONSOLE_SCLP ;
# endif
}
}
2007-04-27 16:01:49 +02:00
# if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
static void __init setup_zfcpdump ( unsigned int console_devno )
{
2008-07-14 09:59:09 +02:00
static char str [ 41 ] ;
2007-04-27 16:01:49 +02:00
if ( ipl_info . type ! = IPL_TYPE_FCP_DUMP )
return ;
if ( console_devno ! = - 1 )
2008-07-14 09:59:09 +02:00
sprintf ( str , " cio_ignore=all,!0.0.%04x,!0.0.%04x " ,
2007-04-27 16:01:49 +02:00
ipl_info . data . fcp . dev_id . devno , console_devno ) ;
else
2008-07-14 09:59:09 +02:00
sprintf ( str , " cio_ignore=all,!0.0.%04x " ,
2007-04-27 16:01:49 +02:00
ipl_info . data . fcp . dev_id . devno ) ;
2008-07-14 09:59:09 +02:00
strcat ( boot_command_line , str ) ;
2007-04-27 16:01:49 +02:00
console_loglevel = 2 ;
}
# else
static inline void setup_zfcpdump ( unsigned int console_devno ) { }
# endif /* CONFIG_ZFCPDUMP */
2005-04-16 15:20:36 -07:00
/*
* Reboot , halt and power_off stubs . They just call _machine_restart ,
* _machine_halt or _machine_power_off .
*/
void machine_restart ( char * command )
{
2007-11-20 11:13:31 +01:00
if ( ( ! in_interrupt ( ) & & ! in_atomic ( ) ) | | oops_in_progress )
2006-06-29 14:57:32 +02:00
/*
* Only unblank the console if we are called in enabled
* context or a bust_spinlocks cleared the way for us .
*/
console_unblank ( ) ;
2005-04-16 15:20:36 -07:00
_machine_restart ( command ) ;
}
void machine_halt ( void )
{
2006-06-29 14:57:32 +02:00
if ( ! in_interrupt ( ) | | oops_in_progress )
/*
* Only unblank the console if we are called in enabled
* context or a bust_spinlocks cleared the way for us .
*/
console_unblank ( ) ;
2005-04-16 15:20:36 -07:00
_machine_halt ( ) ;
}
void machine_power_off ( void )
{
2006-06-29 14:57:32 +02:00
if ( ! in_interrupt ( ) | | oops_in_progress )
/*
* Only unblank the console if we are called in enabled
* context or a bust_spinlocks cleared the way for us .
*/
console_unblank ( ) ;
2005-04-16 15:20:36 -07:00
_machine_power_off ( ) ;
}
2006-01-14 13:21:01 -08:00
/*
* Dummy power off function .
*/
void ( * pm_power_off ) ( void ) = machine_power_off ;
2006-03-24 03:15:15 -08:00
static int __init early_parse_mem ( char * p )
{
memory_end = memparse ( p , & p ) ;
return 0 ;
}
early_param ( " mem " , early_parse_mem ) ;
2007-02-05 21:18:17 +01:00
# ifdef CONFIG_S390_SWITCH_AMODE
2008-03-25 18:47:10 +01:00
# ifdef CONFIG_PGSTE
unsigned int switch_amode = 1 ;
# else
2007-02-05 21:18:17 +01:00
unsigned int switch_amode = 0 ;
2008-03-25 18:47:10 +01:00
# endif
2007-02-05 21:18:17 +01:00
EXPORT_SYMBOL_GPL ( switch_amode ) ;
2007-02-05 21:18:53 +01:00
static void set_amode_and_uaccess ( unsigned long user_amode ,
unsigned long user32_amode )
2007-02-05 21:18:17 +01:00
{
psw_user_bits = PSW_BASE_BITS | PSW_MASK_DAT | user_amode |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
PSW_MASK_PSTATE | PSW_DEFAULT_KEY ;
# ifdef CONFIG_COMPAT
psw_user32_bits = PSW_BASE32_BITS | PSW_MASK_DAT | user_amode |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
PSW_MASK_PSTATE | PSW_DEFAULT_KEY ;
psw32_user_bits = PSW32_BASE_BITS | PSW32_MASK_DAT | user32_amode |
PSW32_MASK_IO | PSW32_MASK_EXT | PSW32_MASK_MCHECK |
PSW32_MASK_PSTATE ;
# endif
psw_kernel_bits = PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME |
PSW_MASK_MCHECK | PSW_DEFAULT_KEY ;
if ( MACHINE_HAS_MVCOS ) {
printk ( " mvcos available. \n " ) ;
memcpy ( & uaccess , & uaccess_mvcos_switch , sizeof ( uaccess ) ) ;
} else {
printk ( " mvcos not available. \n " ) ;
memcpy ( & uaccess , & uaccess_pt , sizeof ( uaccess ) ) ;
}
}
/*
* Switch kernel / user addressing modes ?
*/
static int __init early_parse_switch_amode ( char * p )
{
switch_amode = 1 ;
return 0 ;
}
early_param ( " switch_amode " , early_parse_switch_amode ) ;
# else /* CONFIG_S390_SWITCH_AMODE */
static inline void set_amode_and_uaccess ( unsigned long user_amode ,
unsigned long user32_amode )
{
}
# endif /* CONFIG_S390_SWITCH_AMODE */
# ifdef CONFIG_S390_EXEC_PROTECT
unsigned int s390_noexec = 0 ;
EXPORT_SYMBOL_GPL ( s390_noexec ) ;
/*
* Enable execute protection ?
*/
static int __init early_parse_noexec ( char * p )
{
if ( ! strncmp ( p , " off " , 3 ) )
return 0 ;
switch_amode = 1 ;
s390_noexec = 1 ;
return 0 ;
}
early_param ( " noexec " , early_parse_noexec ) ;
# endif /* CONFIG_S390_EXEC_PROTECT */
static void setup_addressing_mode ( void )
{
if ( s390_noexec ) {
printk ( " S390 execute protection active, " ) ;
set_amode_and_uaccess ( PSW_ASC_SECONDARY , PSW32_ASC_SECONDARY ) ;
2007-12-04 16:09:05 +01:00
} else if ( switch_amode ) {
2007-02-05 21:18:17 +01:00
printk ( " S390 address spaces switched, " ) ;
set_amode_and_uaccess ( PSW_ASC_PRIMARY , PSW32_ASC_PRIMARY ) ;
}
2007-11-20 11:13:32 +01:00
# ifdef CONFIG_TRACE_IRQFLAGS
sysc_restore_trace_psw . mask = psw_kernel_bits & ~ PSW_MASK_MCHECK ;
io_restore_trace_psw . mask = psw_kernel_bits & ~ PSW_MASK_MCHECK ;
# endif
2007-02-05 21:18:17 +01:00
}
2005-05-01 08:58:57 -07:00
static void __init
setup_lowcore ( void )
{
struct _lowcore * lc ;
int lc_pages ;
/*
* Setup lowcore for boot cpu
*/
lc_pages = sizeof ( void * ) = = 8 ? 2 : 1 ;
lc = ( struct _lowcore * )
__alloc_bootmem ( lc_pages * PAGE_SIZE , lc_pages * PAGE_SIZE , 0 ) ;
memset ( lc , 0 , lc_pages * PAGE_SIZE ) ;
2005-05-01 08:58:58 -07:00
lc - > restart_psw . mask = PSW_BASE_BITS | PSW_DEFAULT_KEY ;
2005-05-01 08:58:57 -07:00
lc - > restart_psw . addr =
PSW_ADDR_AMODE | ( unsigned long ) restart_int_handler ;
2007-02-05 21:18:17 +01:00
if ( switch_amode )
lc - > restart_psw . mask | = PSW_ASC_HOME ;
lc - > external_new_psw . mask = psw_kernel_bits ;
2005-05-01 08:58:57 -07:00
lc - > external_new_psw . addr =
PSW_ADDR_AMODE | ( unsigned long ) ext_int_handler ;
2007-02-05 21:18:17 +01:00
lc - > svc_new_psw . mask = psw_kernel_bits | PSW_MASK_IO | PSW_MASK_EXT ;
2005-05-01 08:58:57 -07:00
lc - > svc_new_psw . addr = PSW_ADDR_AMODE | ( unsigned long ) system_call ;
2007-02-05 21:18:17 +01:00
lc - > program_new_psw . mask = psw_kernel_bits ;
2005-05-01 08:58:57 -07:00
lc - > program_new_psw . addr =
PSW_ADDR_AMODE | ( unsigned long ) pgm_check_handler ;
2005-06-25 14:55:30 -07:00
lc - > mcck_new_psw . mask =
2007-02-05 21:18:17 +01:00
psw_kernel_bits & ~ PSW_MASK_MCHECK & ~ PSW_MASK_DAT ;
2005-05-01 08:58:57 -07:00
lc - > mcck_new_psw . addr =
PSW_ADDR_AMODE | ( unsigned long ) mcck_int_handler ;
2007-02-05 21:18:17 +01:00
lc - > io_new_psw . mask = psw_kernel_bits ;
2005-05-01 08:58:57 -07:00
lc - > io_new_psw . addr = PSW_ADDR_AMODE | ( unsigned long ) io_int_handler ;
lc - > ipl_device = S390_lowcore . ipl_device ;
2008-04-17 07:46:25 +02:00
lc - > clock_comparator = - 1ULL ;
2005-05-01 08:58:57 -07:00
lc - > kernel_stack = ( ( unsigned long ) & init_thread_union ) + THREAD_SIZE ;
lc - > async_stack = ( unsigned long )
__alloc_bootmem ( ASYNC_SIZE , ASYNC_SIZE , 0 ) + ASYNC_SIZE ;
lc - > panic_stack = ( unsigned long )
__alloc_bootmem ( PAGE_SIZE , PAGE_SIZE , 0 ) + PAGE_SIZE ;
lc - > current_task = ( unsigned long ) init_thread_union . thread_info . task ;
lc - > thread_info = ( unsigned long ) & init_thread_union ;
2006-01-06 00:19:28 -08:00
# ifndef CONFIG_64BIT
2005-06-25 14:55:30 -07:00
if ( MACHINE_HAS_IEEE ) {
lc - > extended_save_area_addr = ( __u32 )
__alloc_bootmem ( PAGE_SIZE , PAGE_SIZE , 0 ) ;
/* enable extended save area */
2006-11-06 10:49:02 +01:00
__ctl_set_bit ( 14 , 29 ) ;
2005-06-25 14:55:30 -07:00
}
# endif
2005-05-01 08:58:57 -07:00
set_prefix ( ( u32 ) ( unsigned long ) lc ) ;
}
static void __init
setup_resources ( void )
{
2007-02-05 21:18:24 +01:00
struct resource * res , * sub_res ;
2005-05-01 08:58:57 -07:00
int i ;
2006-06-25 05:49:30 -07:00
code_resource . start = ( unsigned long ) & _text ;
code_resource . end = ( unsigned long ) & _etext - 1 ;
data_resource . start = ( unsigned long ) & _etext ;
data_resource . end = ( unsigned long ) & _edata - 1 ;
2008-01-26 14:11:02 +01:00
for ( i = 0 ; i < MEMORY_CHUNKS ; i + + ) {
if ( ! memory_chunk [ i ] . size )
continue ;
2005-05-01 08:58:57 -07:00
res = alloc_bootmem_low ( sizeof ( struct resource ) ) ;
res - > flags = IORESOURCE_BUSY | IORESOURCE_MEM ;
switch ( memory_chunk [ i ] . type ) {
case CHUNK_READ_WRITE :
res - > name = " System RAM " ;
break ;
case CHUNK_READ_ONLY :
res - > name = " System ROM " ;
res - > flags | = IORESOURCE_READONLY ;
break ;
default :
res - > name = " reserved " ;
}
res - > start = memory_chunk [ i ] . addr ;
res - > end = memory_chunk [ i ] . addr + memory_chunk [ i ] . size - 1 ;
request_resource ( & iomem_resource , res ) ;
2007-02-05 21:18:24 +01:00
if ( code_resource . start > = res - > start & &
code_resource . start < = res - > end & &
code_resource . end > res - > end ) {
sub_res = alloc_bootmem_low ( sizeof ( struct resource ) ) ;
memcpy ( sub_res , & code_resource ,
sizeof ( struct resource ) ) ;
sub_res - > end = res - > end ;
code_resource . start = res - > end + 1 ;
request_resource ( res , sub_res ) ;
}
if ( code_resource . start > = res - > start & &
code_resource . start < = res - > end & &
code_resource . end < = res - > end )
request_resource ( res , & code_resource ) ;
if ( data_resource . start > = res - > start & &
data_resource . start < = res - > end & &
data_resource . end > res - > end ) {
sub_res = alloc_bootmem_low ( sizeof ( struct resource ) ) ;
memcpy ( sub_res , & data_resource ,
sizeof ( struct resource ) ) ;
sub_res - > end = res - > end ;
data_resource . start = res - > end + 1 ;
request_resource ( res , sub_res ) ;
}
if ( data_resource . start > = res - > start & &
data_resource . start < = res - > end & &
data_resource . end < = res - > end )
request_resource ( res , & data_resource ) ;
2005-05-01 08:58:57 -07:00
}
}
2007-04-27 16:01:49 +02:00
unsigned long real_memory_size ;
EXPORT_SYMBOL_GPL ( real_memory_size ) ;
2006-12-04 15:40:56 +01:00
static void __init setup_memory_end ( void )
{
2007-04-27 16:01:49 +02:00
unsigned long memory_size ;
2008-01-26 14:11:00 +01:00
unsigned long max_mem ;
2006-12-04 15:40:56 +01:00
int i ;
2007-04-27 16:01:49 +02:00
# if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
if ( ipl_info . type = = IPL_TYPE_FCP_DUMP )
memory_end = ZFCPDUMP_HSA_SIZE ;
# endif
memory_size = 0 ;
2006-12-04 15:40:56 +01:00
memory_end & = PAGE_MASK ;
2008-02-05 16:50:49 +01:00
max_mem = memory_end ? min ( VMEM_MAX_PHYS , memory_end ) : VMEM_MAX_PHYS ;
2008-01-26 14:11:00 +01:00
memory_end = min ( max_mem , memory_end ) ;
2006-12-04 15:40:56 +01:00
2008-01-26 14:11:02 +01:00
/*
* Make sure all chunks are MAX_ORDER aligned so we don ' t need the
* extra checks that HOLES_IN_ZONE would require .
*/
for ( i = 0 ; i < MEMORY_CHUNKS ; i + + ) {
unsigned long start , end ;
struct mem_chunk * chunk ;
unsigned long align ;
chunk = & memory_chunk [ i ] ;
align = 1UL < < ( MAX_ORDER + PAGE_SHIFT - 1 ) ;
start = ( chunk - > addr + align - 1 ) & ~ ( align - 1 ) ;
end = ( chunk - > addr + chunk - > size ) & ~ ( align - 1 ) ;
if ( start > = end )
memset ( chunk , 0 , sizeof ( * chunk ) ) ;
else {
chunk - > addr = start ;
chunk - > size = end - start ;
}
}
2006-12-04 15:40:56 +01:00
for ( i = 0 ; i < MEMORY_CHUNKS ; i + + ) {
struct mem_chunk * chunk = & memory_chunk [ i ] ;
2007-04-27 16:01:49 +02:00
real_memory_size = max ( real_memory_size ,
chunk - > addr + chunk - > size ) ;
2006-12-04 15:40:56 +01:00
if ( chunk - > addr > = max_mem ) {
memset ( chunk , 0 , sizeof ( * chunk ) ) ;
continue ;
}
if ( chunk - > addr + chunk - > size > max_mem )
chunk - > size = max_mem - chunk - > addr ;
memory_size = max ( memory_size , chunk - > addr + chunk - > size ) ;
}
if ( ! memory_end )
memory_end = memory_size ;
}
2005-05-01 08:58:57 -07:00
static void __init
setup_memory ( void )
{
unsigned long bootmap_size ;
2007-02-05 21:18:24 +01:00
unsigned long start_pfn , end_pfn ;
2005-05-01 08:58:57 -07:00
int i ;
2005-04-16 15:20:36 -07:00
/*
* partially used pages are not usable - thus
* we are rounding upwards :
*/
2006-09-20 15:58:41 +02:00
start_pfn = PFN_UP ( __pa ( & _end ) ) ;
end_pfn = max_pfn = PFN_DOWN ( memory_end ) ;
2005-04-16 15:20:36 -07:00
2006-09-20 15:58:41 +02:00
# ifdef CONFIG_BLK_DEV_INITRD
/*
* Move the initrd in case the bitmap of the bootmem allocater
* would overwrite it .
*/
if ( INITRD_START & & INITRD_SIZE ) {
unsigned long bmap_size ;
unsigned long start ;
bmap_size = bootmem_bootmap_pages ( end_pfn - start_pfn + 1 ) ;
bmap_size = PFN_PHYS ( bmap_size ) ;
if ( PFN_PHYS ( start_pfn ) + bmap_size > INITRD_START ) {
start = PFN_PHYS ( start_pfn ) + bmap_size + PAGE_SIZE ;
if ( start + INITRD_SIZE > memory_end ) {
printk ( " initrd extends beyond end of memory "
" (0x%08lx > 0x%08lx) \n "
" disabling initrd \n " ,
start + INITRD_SIZE , memory_end ) ;
INITRD_START = INITRD_SIZE = 0 ;
} else {
printk ( " Moving initrd (0x%08lx -> 0x%08lx, "
" size: %ld) \n " ,
INITRD_START , start , INITRD_SIZE ) ;
memmove ( ( void * ) start , ( void * ) INITRD_START ,
INITRD_SIZE ) ;
INITRD_START = start ;
}
}
}
# endif
2005-04-16 15:20:36 -07:00
/*
2006-10-04 20:02:19 +02:00
* Initialize the boot - time allocator
2005-04-16 15:20:36 -07:00
*/
bootmap_size = init_bootmem ( start_pfn , end_pfn ) ;
/*
* Register RAM areas with the bootmem allocator .
*/
2005-05-01 08:58:57 -07:00
2005-05-01 08:58:58 -07:00
for ( i = 0 ; i < MEMORY_CHUNKS & & memory_chunk [ i ] . size > 0 ; i + + ) {
2006-12-08 15:56:10 +01:00
unsigned long start_chunk , end_chunk , pfn ;
2005-04-16 15:20:36 -07:00
if ( memory_chunk [ i ] . type ! = CHUNK_READ_WRITE )
continue ;
2006-12-08 15:56:10 +01:00
start_chunk = PFN_DOWN ( memory_chunk [ i ] . addr ) ;
2008-11-14 18:18:00 +01:00
end_chunk = start_chunk + PFN_DOWN ( memory_chunk [ i ] . size ) ;
2006-12-08 15:56:10 +01:00
end_chunk = min ( end_chunk , end_pfn ) ;
if ( start_chunk > = end_chunk )
continue ;
add_active_range ( 0 , start_chunk , end_chunk ) ;
pfn = max ( start_chunk , start_pfn ) ;
2008-11-14 18:18:00 +01:00
for ( ; pfn < end_chunk ; pfn + + )
2006-12-08 15:56:10 +01:00
page_set_storage_key ( PFN_PHYS ( pfn ) , PAGE_DEFAULT_KEY ) ;
2005-04-16 15:20:36 -07:00
}
2005-05-01 08:58:58 -07:00
psw_set_key ( PAGE_DEFAULT_KEY ) ;
2006-12-08 15:56:10 +01:00
free_bootmem_with_active_regions ( 0 , max_pfn ) ;
2005-05-01 08:58:57 -07:00
2007-02-21 10:55:37 +01:00
/*
* Reserve memory used for lowcore / command line / kernel image .
*/
2008-02-07 00:15:17 -08:00
reserve_bootmem ( 0 , ( unsigned long ) _ehead , BOOTMEM_DEFAULT ) ;
2007-02-21 10:55:37 +01:00
reserve_bootmem ( ( unsigned long ) _stext ,
2008-02-07 00:15:17 -08:00
PFN_PHYS ( start_pfn ) - ( unsigned long ) _stext ,
BOOTMEM_DEFAULT ) ;
2005-05-01 08:58:57 -07:00
/*
* Reserve the bootmem bitmap itself as well . We do this in two
* steps ( first step was init_bootmem ( ) ) because this catches
* the ( very unlikely ) case of us accidentally initializing the
* bootmem allocator with an invalid RAM area .
*/
2008-02-07 00:15:17 -08:00
reserve_bootmem ( start_pfn < < PAGE_SHIFT , bootmap_size ,
BOOTMEM_DEFAULT ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_BLK_DEV_INITRD
2006-09-20 15:58:41 +02:00
if ( INITRD_START & & INITRD_SIZE ) {
2005-04-16 15:20:36 -07:00
if ( INITRD_START + INITRD_SIZE < = memory_end ) {
2008-02-07 00:15:17 -08:00
reserve_bootmem ( INITRD_START , INITRD_SIZE ,
BOOTMEM_DEFAULT ) ;
2005-04-16 15:20:36 -07:00
initrd_start = INITRD_START ;
initrd_end = initrd_start + INITRD_SIZE ;
} else {
2005-05-01 08:58:57 -07:00
printk ( " initrd extends beyond end of memory "
" (0x%08lx > 0x%08lx) \n disabling initrd \n " ,
initrd_start + INITRD_SIZE , memory_end ) ;
initrd_start = initrd_end = 0 ;
2005-04-16 15:20:36 -07:00
}
2005-05-01 08:58:57 -07:00
}
2005-04-16 15:20:36 -07:00
# endif
2005-05-01 08:58:57 -07:00
}
2005-04-16 15:20:36 -07:00
2007-05-04 18:48:28 +02:00
/*
* Setup hardware capabilities .
*/
static void __init setup_hwcaps ( void )
{
static const int stfl_bits [ 6 ] = { 0 , 2 , 7 , 17 , 19 , 21 } ;
struct cpuinfo_S390 * cpuinfo = & S390_lowcore . cpu_data ;
unsigned long long facility_list_extended ;
unsigned int facility_list ;
int i ;
facility_list = stfl ( ) ;
/*
* The store facility list bits numbers as found in the principles
* of operation are numbered with bit 1UL < < 31 as number 0 to
* bit 1UL < < 0 as number 31.
* Bit 0 : instructions named N3 , " backported " to esa - mode
* Bit 2 : z / Architecture mode is active
* Bit 7 : the store - facility - list - extended facility is installed
* Bit 17 : the message - security assist is installed
* Bit 19 : the long - displacement facility is installed
* Bit 21 : the extended - immediate facility is installed
* These get translated to :
* HWCAP_S390_ESAN3 bit 0 , HWCAP_S390_ZARCH bit 1 ,
* HWCAP_S390_STFLE bit 2 , HWCAP_S390_MSA bit 3 ,
* HWCAP_S390_LDISP bit 4 , and HWCAP_S390_EIMM bit 5.
*/
for ( i = 0 ; i < 6 ; i + + )
if ( facility_list & ( 1UL < < ( 31 - stfl_bits [ i ] ) ) )
elf_hwcap | = 1UL < < i ;
/*
* Check for additional facilities with store - facility - list - extended .
* stfle stores doublewords ( 8 byte ) with bit 1ULL < < 63 as bit 0
* and 1ULL < < 0 as bit 63. Bits 0 - 31 contain the same information
* as stored by stfl , bits 32 - xxx contain additional facilities .
* How many facility words are stored depends on the number of
* doublewords passed to the instruction . The additional facilites
* are :
* Bit 43 : decimal floating point facility is installed
* translated to :
* HWCAP_S390_DFP bit 6.
*/
if ( ( elf_hwcap & ( 1UL < < 2 ) ) & &
2008-04-17 07:46:11 +02:00
__stfle ( & facility_list_extended , 1 ) > 0 ) {
2007-05-04 18:48:28 +02:00
if ( facility_list_extended & ( 1ULL < < ( 64 - 43 ) ) )
elf_hwcap | = 1UL < < 6 ;
}
2008-04-30 13:38:46 +02:00
if ( MACHINE_HAS_HPAGE )
elf_hwcap | = 1UL < < 7 ;
2007-05-04 18:48:28 +02:00
switch ( cpuinfo - > cpu_id . machine ) {
case 0x9672 :
# if !defined(CONFIG_64BIT)
default : /* Use "g5" as default for 31 bit kernels. */
# endif
strcpy ( elf_platform , " g5 " ) ;
break ;
case 0x2064 :
case 0x2066 :
# if defined(CONFIG_64BIT)
default : /* Use "z900" as default for 64 bit kernels. */
# endif
strcpy ( elf_platform , " z900 " ) ;
break ;
case 0x2084 :
case 0x2086 :
strcpy ( elf_platform , " z990 " ) ;
break ;
case 0x2094 :
2008-12-25 13:39:22 +01:00
case 0x2096 :
2007-05-04 18:48:28 +02:00
strcpy ( elf_platform , " z9-109 " ) ;
break ;
2008-12-25 13:39:22 +01:00
case 0x2097 :
case 0x2098 :
strcpy ( elf_platform , " z10 " ) ;
break ;
2007-05-04 18:48:28 +02:00
}
}
2005-05-01 08:58:57 -07:00
/*
* Setup function called from init / main . c just after the banner
* was printed .
*/
2005-04-16 15:20:36 -07:00
2005-05-01 08:58:57 -07:00
void __init
setup_arch ( char * * cmdline_p )
{
2008-12-25 13:39:29 +01:00
/* set up preferred console */
add_preferred_console ( " ttyS " , 0 , NULL ) ;
2005-04-16 15:20:36 -07:00
/*
2005-05-01 08:58:57 -07:00
* print what head . S has found out about the machine
2005-04-16 15:20:36 -07:00
*/
2006-01-06 00:19:28 -08:00
# ifndef CONFIG_64BIT
2005-05-01 08:58:57 -07:00
printk ( ( MACHINE_IS_VM ) ?
" We are running under VM (31 bit mode) \n " :
" We are running native (31 bit mode) \n " ) ;
printk ( ( MACHINE_HAS_IEEE ) ?
" This machine has an IEEE fpu \n " :
" This machine has no IEEE fpu \n " ) ;
2006-01-06 00:19:28 -08:00
# else /* CONFIG_64BIT */
2008-03-25 18:47:44 +01:00
if ( MACHINE_IS_VM )
printk ( " We are running under VM (64 bit mode) \n " ) ;
else if ( MACHINE_IS_KVM ) {
printk ( " We are running under KVM (64 bit mode) \n " ) ;
2008-06-20 15:24:18 +02:00
add_preferred_console ( " hvc " , 0 , NULL ) ;
s390_virtio_console_init ( ) ;
2008-03-25 18:47:44 +01:00
} else
printk ( " We are running native (64 bit mode) \n " ) ;
2006-01-06 00:19:28 -08:00
# endif /* CONFIG_64BIT */
2005-05-01 08:58:57 -07:00
2008-07-14 09:59:09 +02:00
/* Have one command line that is parsed and saved in /proc/cmdline */
/* boot_command_line has been already set up in early.c */
* cmdline_p = boot_command_line ;
2006-03-24 03:15:15 -08:00
2005-05-01 08:58:57 -07:00
ROOT_DEV = Root_RAM0 ;
2006-03-24 03:15:15 -08:00
init_mm . start_code = PAGE_OFFSET ;
init_mm . end_code = ( unsigned long ) & _etext ;
init_mm . end_data = ( unsigned long ) & _edata ;
init_mm . brk = ( unsigned long ) & _end ;
2006-09-20 15:59:44 +02:00
if ( MACHINE_HAS_MVCOS )
memcpy ( & uaccess , & uaccess_mvcos , sizeof ( uaccess ) ) ;
else
memcpy ( & uaccess , & uaccess_std , sizeof ( uaccess ) ) ;
2006-03-24 03:15:15 -08:00
parse_early_param ( ) ;
2008-01-26 14:11:11 +01:00
setup_ipl ( ) ;
2006-12-04 15:40:56 +01:00
setup_memory_end ( ) ;
2007-02-05 21:18:17 +01:00
setup_addressing_mode ( ) ;
2005-05-01 08:58:57 -07:00
setup_memory ( ) ;
setup_resources ( ) ;
setup_lowcore ( ) ;
2005-04-16 15:20:36 -07:00
cpu_init ( ) ;
__cpu_logical_map [ 0 ] = S390_lowcore . cpu_data . cpu_addr ;
2008-04-17 07:46:12 +02:00
s390_init_cpu_topology ( ) ;
2005-04-16 15:20:36 -07:00
2007-05-04 18:48:28 +02:00
/*
* Setup capabilities ( ELF_HWCAP & ELF_PLATFORM ) .
*/
setup_hwcaps ( ) ;
2005-04-16 15:20:36 -07:00
/*
* Create kernel page tables and switch to virtual addressing .
*/
paging_init ( ) ;
/* Setup default console */
conmode_default ( ) ;
2007-04-27 16:01:49 +02:00
/* Setup zfcpdump support */
setup_zfcpdump ( console_devno ) ;
2005-04-16 15:20:36 -07:00
}
2007-05-31 17:38:05 +02:00
void __cpuinit print_cpu_info ( struct cpuinfo_S390 * cpuinfo )
2005-04-16 15:20:36 -07:00
{
2008-01-26 14:10:56 +01:00
printk ( KERN_INFO " cpu %d "
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_SMP
" phys_idx=%d "
# endif
" vers=%02X ident=%06X machine=%04X unused=%04X \n " ,
cpuinfo - > cpu_nr ,
# ifdef CONFIG_SMP
cpuinfo - > cpu_addr ,
# endif
cpuinfo - > cpu_id . version ,
cpuinfo - > cpu_id . ident ,
cpuinfo - > cpu_id . machine ,
cpuinfo - > cpu_id . unused ) ;
}
/*
* show_cpuinfo - Get information on one CPU for use by procfs .
*/
static int show_cpuinfo ( struct seq_file * m , void * v )
{
2008-04-30 13:38:46 +02:00
static const char * hwcap_str [ 8 ] = {
" esan3 " , " zarch " , " stfle " , " msa " , " ldisp " , " eimm " , " dfp " ,
" edat "
2007-05-04 18:48:28 +02:00
} ;
2005-04-16 15:20:36 -07:00
struct cpuinfo_S390 * cpuinfo ;
unsigned long n = ( unsigned long ) v - 1 ;
2007-05-04 18:48:28 +02:00
int i ;
2005-04-16 15:20:36 -07:00
2007-02-05 21:18:31 +01:00
s390_adjust_jiffies ( ) ;
2005-09-16 19:27:34 -07:00
preempt_disable ( ) ;
2005-04-16 15:20:36 -07:00
if ( ! n ) {
seq_printf ( m , " vendor_id : IBM/S390 \n "
" # processors : %i \n "
" bogomips per cpu: %lu.%02lu \n " ,
num_online_cpus ( ) , loops_per_jiffy / ( 500000 / HZ ) ,
( loops_per_jiffy / ( 5000 / HZ ) ) % 100 ) ;
2007-05-04 18:48:28 +02:00
seq_puts ( m , " features \t : " ) ;
2008-04-30 13:38:46 +02:00
for ( i = 0 ; i < 8 ; i + + )
2007-05-04 18:48:28 +02:00
if ( hwcap_str [ i ] & & ( elf_hwcap & ( 1UL < < i ) ) )
seq_printf ( m , " %s " , hwcap_str [ i ] ) ;
seq_puts ( m , " \n " ) ;
2005-04-16 15:20:36 -07:00
}
2007-05-04 18:48:28 +02:00
2005-04-16 15:20:36 -07:00
if ( cpu_online ( n ) ) {
# ifdef CONFIG_SMP
if ( smp_processor_id ( ) = = n )
cpuinfo = & S390_lowcore . cpu_data ;
else
cpuinfo = & lowcore_ptr [ n ] - > cpu_data ;
# else
cpuinfo = & S390_lowcore . cpu_data ;
# endif
seq_printf ( m , " processor %li: "
" version = %02X, "
" identification = %06X, "
" machine = %04X \n " ,
n , cpuinfo - > cpu_id . version ,
cpuinfo - > cpu_id . ident ,
cpuinfo - > cpu_id . machine ) ;
}
2005-09-16 19:27:34 -07:00
preempt_enable ( ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void * c_start ( struct seq_file * m , loff_t * pos )
{
return * pos < NR_CPUS ? ( void * ) ( ( unsigned long ) * pos + 1 ) : NULL ;
}
static void * c_next ( struct seq_file * m , void * v , loff_t * pos )
{
+ + * pos ;
return c_start ( m , pos ) ;
}
static void c_stop ( struct seq_file * m , void * v )
{
}
2008-01-26 14:11:29 +01:00
const struct seq_operations cpuinfo_op = {
2005-04-16 15:20:36 -07:00
. start = c_start ,
. next = c_next ,
. stop = c_stop ,
. show = show_cpuinfo ,
} ;