diff options
Diffstat (limited to 'arch/s390/kernel/setup.c')
| -rw-r--r-- | arch/s390/kernel/setup.c | 785 | 
1 files changed, 436 insertions, 349 deletions
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 6f6350826c8..1e2264b46e4 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -1,8 +1,6 @@  /* - *  arch/s390/kernel/setup.c - *   *  S390 version - *    Copyright (C) IBM Corp. 1999,2010 + *    Copyright IBM Corp. 1999, 2012   *    Author(s): Hartmut Penner (hp@de.ibm.com),   *               Martin Schwidefsky (schwidefsky@de.ibm.com)   * @@ -18,9 +16,10 @@  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt  #include <linux/errno.h> -#include <linux/module.h> +#include <linux/export.h>  #include <linux/sched.h>  #include <linux/kernel.h> +#include <linux/memblock.h>  #include <linux/mm.h>  #include <linux/stddef.h>  #include <linux/unistd.h> @@ -42,10 +41,13 @@  #include <linux/reboot.h>  #include <linux/topology.h>  #include <linux/ftrace.h> +#include <linux/kexec.h> +#include <linux/crash_dump.h> +#include <linux/memory.h> +#include <linux/compat.h>  #include <asm/ipl.h> -#include <asm/uaccess.h> -#include <asm/system.h> +#include <asm/facility.h>  #include <asm/smp.h>  #include <asm/mmu_context.h>  #include <asm/cpcmd.h> @@ -55,20 +57,11 @@  #include <asm/ptrace.h>  #include <asm/sections.h>  #include <asm/ebcdic.h> -#include <asm/compat.h>  #include <asm/kvm_virtio.h> - -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); - -/* - * User copy operations. - */ -struct uaccess_ops uaccess; -EXPORT_SYMBOL(uaccess); +#include <asm/diag.h> +#include <asm/os_info.h> +#include <asm/sclp.h> +#include "entry.h"  /*   * Machine setup.. @@ -85,10 +78,23 @@ EXPORT_SYMBOL(console_irq);  unsigned long elf_hwcap = 0;  char elf_platform[ELF_PLATFORM_SIZE]; -struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; -  int __initdata memory_end_set;  unsigned long __initdata memory_end; +unsigned long __initdata max_physmem_end; + +unsigned long VMALLOC_START; +EXPORT_SYMBOL(VMALLOC_START); + +unsigned long VMALLOC_END; +EXPORT_SYMBOL(VMALLOC_END); + +struct page *vmemmap; +EXPORT_SYMBOL(vmemmap); + +#ifdef CONFIG_64BIT +unsigned long MODULES_VADDR; +unsigned long MODULES_END; +#endif  /* An array with a pointer to the lowcore of every CPU. */  struct _lowcore *lowcore_ptr[NR_CPUS]; @@ -102,16 +108,6 @@ EXPORT_SYMBOL(lowcore_ptr);  #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, -}; -  /*   * condev= and conmode= setup parameter.   */ @@ -132,9 +128,14 @@ __setup("condev=", condev_setup);  static void __init set_preferred_console(void)  { -	if (MACHINE_IS_KVM) -		add_preferred_console("hvc", 0, NULL); -	else if (CONSOLE_IS_3215 || CONSOLE_IS_SCLP) +	if (MACHINE_IS_KVM) { +		if (sclp_has_vt220()) +			add_preferred_console("ttyS", 1, NULL); +		else if (sclp_has_linemode()) +			add_preferred_console("ttyS", 0, NULL); +		else +			add_preferred_console("hvc", 0, NULL); +	} else if (CONSOLE_IS_3215 || CONSOLE_IS_SCLP)  		add_preferred_console("ttyS", 0, NULL);  	else if (CONSOLE_IS_3270)  		add_preferred_console("tty3270", 0, NULL); @@ -210,25 +211,19 @@ static void __init conmode_default(void)  	}  } -#ifdef CONFIG_ZFCPDUMP -static void __init setup_zfcpdump(unsigned int console_devno) +#ifdef CONFIG_CRASH_DUMP +static void __init setup_zfcpdump(void)  { -	static char str[41]; -  	if (ipl_info.type != IPL_TYPE_FCP_DUMP)  		return; -	if (console_devno != -1) -		sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x", -			ipl_info.data.fcp.dev_id.devno, console_devno); -	else -		sprintf(str, " cio_ignore=all,!0.0.%04x", -			ipl_info.data.fcp.dev_id.devno); -	strcat(boot_command_line, str); +	if (OLDMEM_BASE) +		return; +	strcat(boot_command_line, " cio_ignore=all,!ipldev,!condev");  	console_loglevel = 2;  }  #else -static inline void setup_zfcpdump(unsigned int console_devno) {} -#endif /* CONFIG_ZFCPDUMP */ +static inline void setup_zfcpdump(void) {} +#endif /* CONFIG_CRASH_DUMP */   /*   * Reboot, halt and power_off stubs. They just call _machine_restart, @@ -272,107 +267,29 @@ void machine_power_off(void)   * Dummy power off function.   */  void (*pm_power_off)(void) = machine_power_off; +EXPORT_SYMBOL_GPL(pm_power_off);  static int __init early_parse_mem(char *p)  {  	memory_end = memparse(p, &p); +	memory_end &= PAGE_MASK;  	memory_end_set = 1;  	return 0;  }  early_param("mem", early_parse_mem); -unsigned int user_mode = HOME_SPACE_MODE; -EXPORT_SYMBOL_GPL(user_mode); - -static int set_amode_and_uaccess(unsigned long user_amode, -				 unsigned long user32_amode) +static int __init parse_vmalloc(char *arg)  { -	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) { -		memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess)); -		return 1; -	} else { -		memcpy(&uaccess, &uaccess_pt, sizeof(uaccess)); -		return 0; -	} -} - -/* - * Switch kernel/user addressing modes? - */ -static int __init early_parse_switch_amode(char *p) -{ -	if (user_mode != SECONDARY_SPACE_MODE) -		user_mode = PRIMARY_SPACE_MODE; +	if (!arg) +		return -EINVAL; +	VMALLOC_END = (memparse(arg, &arg) + PAGE_SIZE - 1) & PAGE_MASK;  	return 0;  } -early_param("switch_amode", early_parse_switch_amode); +early_param("vmalloc", parse_vmalloc); -static int __init early_parse_user_mode(char *p) -{ -	if (p && strcmp(p, "primary") == 0) -		user_mode = PRIMARY_SPACE_MODE; -#ifdef CONFIG_S390_EXEC_PROTECT -	else if (p && strcmp(p, "secondary") == 0) -		user_mode = SECONDARY_SPACE_MODE; -#endif -	else if (!p || strcmp(p, "home") == 0) -		user_mode = HOME_SPACE_MODE; -	else -		return 1; -	return 0; -} -early_param("user_mode", early_parse_user_mode); - -#ifdef CONFIG_S390_EXEC_PROTECT -/* - * Enable execute protection? - */ -static int __init early_parse_noexec(char *p) -{ -	if (!strncmp(p, "off", 3)) -		return 0; -	user_mode = SECONDARY_SPACE_MODE; -	return 0; -} -early_param("noexec", early_parse_noexec); -#endif /* CONFIG_S390_EXEC_PROTECT */ - -static void setup_addressing_mode(void) -{ -	if (user_mode == SECONDARY_SPACE_MODE) { -		if (set_amode_and_uaccess(PSW_ASC_SECONDARY, -					  PSW32_ASC_SECONDARY)) -			pr_info("Execute protection active, " -				"mvcos available\n"); -		else -			pr_info("Execute protection active, " -				"mvcos not available\n"); -	} else if (user_mode == PRIMARY_SPACE_MODE) { -		if (set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY)) -			pr_info("Address spaces switched, " -				"mvcos available\n"); -		else -			pr_info("Address spaces switched, " -				"mvcos not available\n"); -	} -} +void *restart_stack __attribute__((__section__(".data"))); -static void __init -setup_lowcore(void) +static void __init setup_lowcore(void)  {  	struct _lowcore *lc; @@ -381,31 +298,35 @@ setup_lowcore(void)  	 */  	BUILD_BUG_ON(sizeof(struct _lowcore) != LC_PAGES * 4096);  	lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0); -	lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; +	lc->restart_psw.mask = PSW_KERNEL_BITS;  	lc->restart_psw.addr =  		PSW_ADDR_AMODE | (unsigned long) restart_int_handler; -	if (user_mode != HOME_SPACE_MODE) -		lc->restart_psw.mask |= PSW_ASC_HOME; -	lc->external_new_psw.mask = psw_kernel_bits; +	lc->external_new_psw.mask = PSW_KERNEL_BITS | +		PSW_MASK_DAT | PSW_MASK_MCHECK;  	lc->external_new_psw.addr =  		PSW_ADDR_AMODE | (unsigned long) ext_int_handler; -	lc->svc_new_psw.mask = psw_kernel_bits | PSW_MASK_IO | PSW_MASK_EXT; +	lc->svc_new_psw.mask = PSW_KERNEL_BITS | +		PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;  	lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call; -	lc->program_new_psw.mask = psw_kernel_bits; +	lc->program_new_psw.mask = PSW_KERNEL_BITS | +		PSW_MASK_DAT | PSW_MASK_MCHECK;  	lc->program_new_psw.addr = -		PSW_ADDR_AMODE | (unsigned long)pgm_check_handler; -	lc->mcck_new_psw.mask = -		psw_kernel_bits & ~PSW_MASK_MCHECK & ~PSW_MASK_DAT; +		PSW_ADDR_AMODE | (unsigned long) pgm_check_handler; +	lc->mcck_new_psw.mask = PSW_KERNEL_BITS;  	lc->mcck_new_psw.addr =  		PSW_ADDR_AMODE | (unsigned long) mcck_int_handler; -	lc->io_new_psw.mask = psw_kernel_bits; +	lc->io_new_psw.mask = PSW_KERNEL_BITS | +		PSW_MASK_DAT | PSW_MASK_MCHECK;  	lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler;  	lc->clock_comparator = -1ULL; -	lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE; +	lc->kernel_stack = ((unsigned long) &init_thread_union) +		+ THREAD_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs);  	lc->async_stack = (unsigned long) -		__alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE; +		__alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) +		+ ASYNC_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs);  	lc->panic_stack = (unsigned long) -		__alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) + PAGE_SIZE; +		__alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) +		+ PAGE_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs);  	lc->current_task = (unsigned long) init_thread_union.thread_info.task;  	lc->thread_info = (unsigned long) &init_thread_union;  	lc->machine_flags = S390_lowcore.machine_flags; @@ -420,7 +341,6 @@ setup_lowcore(void)  		__ctl_set_bit(14, 29);  	}  #else -	lc->cmf_hpp = -1ULL;  	lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0];  #endif  	lc->sync_enter_timer = S390_lowcore.sync_enter_timer; @@ -432,244 +352,345 @@ setup_lowcore(void)  	lc->last_update_timer = S390_lowcore.last_update_timer;  	lc->last_update_clock = S390_lowcore.last_update_clock;  	lc->ftrace_func = S390_lowcore.ftrace_func; + +	restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0); +	restart_stack += ASYNC_SIZE; + +	/* +	 * Set up PSW restart to call ipl.c:do_restart(). Copy the relevant +	 * restart data to the absolute zero lowcore. This is necessary if +	 * PSW restart is done on an offline CPU that has lowcore zero. +	 */ +	lc->restart_stack = (unsigned long) restart_stack; +	lc->restart_fn = (unsigned long) do_restart; +	lc->restart_data = 0; +	lc->restart_source = -1UL; + +	/* Setup absolute zero lowcore */ +	mem_assign_absolute(S390_lowcore.restart_stack, lc->restart_stack); +	mem_assign_absolute(S390_lowcore.restart_fn, lc->restart_fn); +	mem_assign_absolute(S390_lowcore.restart_data, lc->restart_data); +	mem_assign_absolute(S390_lowcore.restart_source, lc->restart_source); +	mem_assign_absolute(S390_lowcore.restart_psw, lc->restart_psw); + +#ifdef CONFIG_SMP +	lc->spinlock_lockval = arch_spin_lockval(0); +#endif +  	set_prefix((u32)(unsigned long) lc);  	lowcore_ptr[0] = lc;  } -static void __init -setup_resources(void) +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, +}; + +static struct resource bss_resource = { +	.name = "Kernel bss", +	.flags = IORESOURCE_BUSY | IORESOURCE_MEM, +}; + +static struct resource __initdata *standard_resources[] = { +	&code_resource, +	&data_resource, +	&bss_resource, +}; + +static void __init setup_resources(void)  { -	struct resource *res, *sub_res; -	int i; +	struct resource *res, *std_res, *sub_res; +	struct memblock_region *reg; +	int j;  	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; +	bss_resource.start = (unsigned long) &__bss_start; +	bss_resource.end = (unsigned long) &__bss_stop - 1; -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		if (!memory_chunk[i].size) -			continue; -		res = alloc_bootmem_low(sizeof(struct resource)); +	for_each_memblock(memory, reg) { +		res = alloc_bootmem_low(sizeof(*res));  		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); -		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); -		} +		res->name = "System RAM"; +		res->start = reg->base; +		res->end = reg->base + reg->size - 1; +		request_resource(&iomem_resource, 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); +		for (j = 0; j < ARRAY_SIZE(standard_resources); j++) { +			std_res = standard_resources[j]; +			if (std_res->start < res->start || +			    std_res->start > res->end) +				continue; +			if (std_res->end > res->end) { +				sub_res = alloc_bootmem_low(sizeof(*sub_res)); +				*sub_res = *std_res; +				sub_res->end = res->end; +				std_res->start = res->end + 1; +				request_resource(res, sub_res); +			} else { +				request_resource(res, std_res); +			}  		} - -		if (data_resource.start >= res->start && -			data_resource.start <= res->end && -			data_resource.end <= res->end) -			request_resource(res, &data_resource);  	}  } -unsigned long real_memory_size; -EXPORT_SYMBOL_GPL(real_memory_size); -  static void __init setup_memory_end(void)  { -	unsigned long memory_size; -	unsigned long max_mem; -	int i; - -#ifdef CONFIG_ZFCPDUMP -	if (ipl_info.type == IPL_TYPE_FCP_DUMP) { -		memory_end = ZFCPDUMP_HSA_SIZE; -		memory_end_set = 1; -	} +	unsigned long vmax, vmalloc_size, tmp; + +	/* Choose kernel address space layout: 2, 3, or 4 levels. */ +#ifdef CONFIG_64BIT +	vmalloc_size = VMALLOC_END ?: (128UL << 30) - MODULES_LEN; +	tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE; +	tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size; +	if (tmp <= (1UL << 42)) +		vmax = 1UL << 42;	/* 3-level kernel page table */ +	else +		vmax = 1UL << 53;	/* 4-level kernel page table */ +	/* module area is at the end of the kernel address space. */ +	MODULES_END = vmax; +	MODULES_VADDR = MODULES_END - MODULES_LEN; +	VMALLOC_END = MODULES_VADDR; +#else +	vmalloc_size = VMALLOC_END ?: 96UL << 20; +	vmax = 1UL << 31;		/* 2-level kernel page table */ +	/* vmalloc area is at the end of the kernel address space. */ +	VMALLOC_END = vmax;  #endif -	memory_size = 0; -	memory_end &= PAGE_MASK; +	VMALLOC_START = vmax - vmalloc_size; + +	/* Split remaining virtual space between 1:1 mapping & vmemmap array */ +	tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page)); +	/* vmemmap contains a multiple of PAGES_PER_SECTION struct pages */ +	tmp = SECTION_ALIGN_UP(tmp); +	tmp = VMALLOC_START - tmp * sizeof(struct page); +	tmp &= ~((vmax >> 11) - 1);	/* align to page table level */ +	tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS); +	vmemmap = (struct page *) tmp; + +	/* Take care that memory_end is set and <= vmemmap */ +	memory_end = min(memory_end ?: max_physmem_end, tmp); +	max_pfn = max_low_pfn = PFN_DOWN(memory_end); +	memblock_remove(memory_end, ULONG_MAX); + +	pr_notice("Max memory size: %luMB\n", memory_end >> 20); +} -	max_mem = memory_end ? min(VMEM_MAX_PHYS, memory_end) : VMEM_MAX_PHYS; -	memory_end = min(max_mem, memory_end); +static void __init setup_vmcoreinfo(void) +{ +	mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note()); +} -	/* -	 * 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; -		} -	} +#ifdef CONFIG_CRASH_DUMP -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		struct mem_chunk *chunk = &memory_chunk[i]; +/* + * When kdump is enabled, we have to ensure that no memory from + * the area [0 - crashkernel memory size] and + * [crashk_res.start - crashk_res.end] is set offline. + */ +static int kdump_mem_notifier(struct notifier_block *nb, +			      unsigned long action, void *data) +{ +	struct memory_notify *arg = data; + +	if (arg->start_pfn < PFN_DOWN(resource_size(&crashk_res))) +		return NOTIFY_BAD; +	if (arg->start_pfn > PFN_DOWN(crashk_res.end)) +		return NOTIFY_OK; +	if (arg->start_pfn + arg->nr_pages - 1 < PFN_DOWN(crashk_res.start)) +		return NOTIFY_OK; +	return NOTIFY_BAD; +} -		real_memory_size = max(real_memory_size, -				       chunk->addr + chunk->size); -		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); +static struct notifier_block kdump_mem_nb = { +	.notifier_call = kdump_mem_notifier, +}; + +#endif + +/* + * Make sure that the area behind memory_end is protected + */ +static void reserve_memory_end(void) +{ +#ifdef CONFIG_CRASH_DUMP +	if (ipl_info.type == IPL_TYPE_FCP_DUMP && +	    !OLDMEM_BASE && sclp_get_hsa_size()) { +		memory_end = sclp_get_hsa_size(); +		memory_end &= PAGE_MASK; +		memory_end_set = 1;  	} -	if (!memory_end) -		memory_end = memory_size; +#endif +	if (!memory_end_set) +		return; +	memblock_reserve(memory_end, ULONG_MAX);  } -static void __init -setup_memory(void) +/* + * Make sure that oldmem, where the dump is stored, is protected + */ +static void reserve_oldmem(void)  { -        unsigned long bootmap_size; -	unsigned long start_pfn, end_pfn; -	int i; +#ifdef CONFIG_CRASH_DUMP +	if (OLDMEM_BASE) +		/* Forget all memory above the running kdump system */ +		memblock_reserve(OLDMEM_SIZE, (phys_addr_t)ULONG_MAX); +#endif +} -	/* -	 * partially used pages are not usable - thus -	 * we are rounding upwards: -	 */ -	start_pfn = PFN_UP(__pa(&_end)); -	end_pfn = max_pfn = PFN_DOWN(memory_end); +/* + * Make sure that oldmem, where the dump is stored, is protected + */ +static void remove_oldmem(void) +{ +#ifdef CONFIG_CRASH_DUMP +	if (OLDMEM_BASE) +		/* Forget all memory above the running kdump system */ +		memblock_remove(OLDMEM_SIZE, (phys_addr_t)ULONG_MAX); +#endif +} -#ifdef CONFIG_BLK_DEV_INITRD -	/* -	 * Move the initrd in case the bitmap of the bootmem allocater -	 * would overwrite it. -	 */ +/* + * Reserve memory for kdump kernel to be loaded with kexec + */ +static void __init reserve_crashkernel(void) +{ +#ifdef CONFIG_CRASH_DUMP +	unsigned long long crash_base, crash_size; +	phys_addr_t low, high; +	int rc; -	if (INITRD_START && INITRD_SIZE) { -		unsigned long bmap_size; -		unsigned long start; +	rc = parse_crashkernel(boot_command_line, memory_end, &crash_size, +			       &crash_base); -		bmap_size = bootmem_bootmap_pages(end_pfn - start_pfn + 1); -		bmap_size = PFN_PHYS(bmap_size); +	crash_base = ALIGN(crash_base, KEXEC_CRASH_MEM_ALIGN); +	crash_size = ALIGN(crash_size, KEXEC_CRASH_MEM_ALIGN); +	if (rc || crash_size == 0) +		return; -		if (PFN_PHYS(start_pfn) + bmap_size > INITRD_START) { -			start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE; +	if (memblock.memory.regions[0].size < crash_size) { +		pr_info("crashkernel reservation failed: %s\n", +			"first memory chunk must be at least crashkernel size"); +		return; +	} -			if (start + INITRD_SIZE > memory_end) { -				pr_err("initrd extends beyond end of " -				       "memory (0x%08lx > 0x%08lx) " -				       "disabling initrd\n", -				       start + INITRD_SIZE, memory_end); -				INITRD_START = INITRD_SIZE = 0; -			} else { -				pr_info("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; -			} +	low = crash_base ?: OLDMEM_BASE; +	high = low + crash_size; +	if (low >= OLDMEM_BASE && high <= OLDMEM_BASE + OLDMEM_SIZE) { +		/* The crashkernel fits into OLDMEM, reuse OLDMEM */ +		crash_base = low; +	} else { +		/* Find suitable area in free memory */ +		low = max_t(unsigned long, crash_size, sclp_get_hsa_size()); +		high = crash_base ? crash_base + crash_size : ULONG_MAX; + +		if (crash_base && crash_base < low) { +			pr_info("crashkernel reservation failed: %s\n", +				"crash_base too low"); +			return;  		} +		low = crash_base ?: low; +		crash_base = memblock_find_in_range(low, high, crash_size, +						    KEXEC_CRASH_MEM_ALIGN);  	} -#endif -	/* -	 * Initialize the boot-time allocator -	 */ -	bootmap_size = init_bootmem(start_pfn, end_pfn); +	if (!crash_base) { +		pr_info("crashkernel reservation failed: %s\n", +			"no suitable area found"); +		return; +	} -	/* -	 * Register RAM areas with the bootmem allocator. -	 */ +	if (register_memory_notifier(&kdump_mem_nb)) +		return; -	for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { -		unsigned long start_chunk, end_chunk, pfn; - -		if (memory_chunk[i].type != CHUNK_READ_WRITE) -			continue; -		start_chunk = PFN_DOWN(memory_chunk[i].addr); -		end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size); -		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); -		for (; pfn < end_chunk; pfn++) -			page_set_storage_key(PFN_PHYS(pfn), -					     PAGE_DEFAULT_KEY, 0); -	} +	if (!OLDMEM_BASE && MACHINE_IS_VM) +		diag10_range(PFN_DOWN(crash_base), PFN_DOWN(crash_size)); +	crashk_res.start = crash_base; +	crashk_res.end = crash_base + crash_size - 1; +	insert_resource(&iomem_resource, &crashk_res); +	memblock_remove(crash_base, crash_size); +	pr_info("Reserving %lluMB of memory at %lluMB " +		"for crashkernel (System RAM: %luMB)\n", +		crash_size >> 20, crash_base >> 20, +		(unsigned long)memblock.memory.total_size >> 20); +	os_info_crashkernel_add(crash_base, crash_size); +#endif +} -	psw_set_key(PAGE_DEFAULT_KEY); +/* + * Reserve the initrd from being used by memblock + */ +static void __init reserve_initrd(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD +	initrd_start = INITRD_START; +	initrd_end = initrd_start + INITRD_SIZE; +	memblock_reserve(INITRD_START, INITRD_SIZE); +#endif +} -	free_bootmem_with_active_regions(0, max_pfn); +/* + * Check for initrd being in usable memory + */ +static void __init check_initrd(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD +	if (INITRD_START && INITRD_SIZE && +	    !memblock_is_region_memory(INITRD_START, INITRD_SIZE)) { +		pr_err("initrd does not fit memory.\n"); +		memblock_free(INITRD_START, INITRD_SIZE); +		initrd_start = initrd_end = 0; +	} +#endif +} + +/* + * Reserve all kernel text + */ +static void __init reserve_kernel(void) +{ +	unsigned long start_pfn; +	start_pfn = PFN_UP(__pa(&_end));  	/*  	 * Reserve memory used for lowcore/command line/kernel image.  	 */ -	reserve_bootmem(0, (unsigned long)_ehead, BOOTMEM_DEFAULT); -	reserve_bootmem((unsigned long)_stext, -			PFN_PHYS(start_pfn) - (unsigned long)_stext, -			BOOTMEM_DEFAULT); +	memblock_reserve(0, (unsigned long)_ehead); +	memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn) +			 - (unsigned long)_stext); +} + +static void __init reserve_elfcorehdr(void) +{ +#ifdef CONFIG_CRASH_DUMP +	if (is_kdump_kernel()) +		memblock_reserve(elfcorehdr_addr - OLDMEM_BASE, +				 PAGE_ALIGN(elfcorehdr_size)); +#endif +} + +static void __init setup_memory(void) +{ +	struct memblock_region *reg; +  	/* -	 * 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. +	 * Init storage key for present memory  	 */ -	reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size, -			BOOTMEM_DEFAULT); - -#ifdef CONFIG_BLK_DEV_INITRD -	if (INITRD_START && INITRD_SIZE) { -		if (INITRD_START + INITRD_SIZE <= memory_end) { -			reserve_bootmem(INITRD_START, INITRD_SIZE, -					BOOTMEM_DEFAULT); -			initrd_start = INITRD_START; -			initrd_end = initrd_start + INITRD_SIZE; -		} else { -			pr_err("initrd extends beyond end of " -			       "memory (0x%08lx > 0x%08lx) " -			       "disabling initrd\n", -			       initrd_start + INITRD_SIZE, memory_end); -			initrd_start = initrd_end = 0; -		} +	for_each_memblock(memory, reg) { +		storage_key_init_range(reg->base, reg->base + reg->size);  	} -#endif +	psw_set_key(PAGE_DEFAULT_KEY); + +	/* Only cosmetics */ +	memblock_enforce_memory_limit(memblock_end_of_DRAM());  }  /* @@ -712,7 +733,7 @@ static void __init setup_hwcaps(void)  	 * 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 +	 * doublewords passed to the instruction. The additional facilities  	 * are:  	 *   Bit 42: decimal floating point facility is installed  	 *   Bit 44: perform floating point operation facility is installed @@ -728,12 +749,20 @@ static void __init setup_hwcaps(void)  	if (MACHINE_HAS_HPAGE)  		elf_hwcap |= HWCAP_S390_HPAGE; +#if defined(CONFIG_64BIT)  	/*  	 * 64-bit register support for 31-bit processes  	 * HWCAP_S390_HIGH_GPRS is bit 9.  	 */  	elf_hwcap |= HWCAP_S390_HIGH_GPRS; +	/* +	 * Transactional execution support HWCAP_S390_TE is bit 10. +	 */ +	if (test_facility(50) && test_facility(73)) +		elf_hwcap |= HWCAP_S390_TE; +#endif +  	get_cpu_id(&cpu_id);  	switch (cpu_id.machine) {  	case 0x9672: @@ -762,8 +791,13 @@ static void __init setup_hwcaps(void)  		strcpy(elf_platform, "z10");  		break;  	case 0x2817: +	case 0x2818:  		strcpy(elf_platform, "z196");  		break; +	case 0x2827: +	case 0x2828: +		strcpy(elf_platform, "zEC12"); +		break;  	}  } @@ -772,8 +806,7 @@ static void __init setup_hwcaps(void)   * was printed.   */ -void __init -setup_arch(char **cmdline_p) +void __init setup_arch(char **cmdline_p)  {          /*           * print what head.S has found out about the machine @@ -806,25 +839,47 @@ setup_arch(char **cmdline_p)          ROOT_DEV = Root_RAM0; +	/* Is init_mm really needed? */  	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; -	if (MACHINE_HAS_MVCOS) -		memcpy(&uaccess, &uaccess_mvcos, sizeof(uaccess)); -	else -		memcpy(&uaccess, &uaccess_std, sizeof(uaccess)); -  	parse_early_param(); - +	os_info_init();  	setup_ipl(); + +	/* Do some memory reservations *before* memory is added to memblock */ +	reserve_memory_end(); +	reserve_oldmem(); +	reserve_kernel(); +	reserve_initrd(); +	reserve_elfcorehdr(); +	memblock_allow_resize(); + +	/* Get information about *all* installed memory */ +	detect_memory_memblock(); + +	remove_oldmem(); + +	/* +	 * Make sure all chunks are MAX_ORDER aligned so we don't need the +	 * extra checks that HOLES_IN_ZONE would require. +	 * +	 * Is this still required? +	 */ +	memblock_trim_memory(1UL << (MAX_ORDER - 1 + PAGE_SHIFT)); +  	setup_memory_end(); -	setup_addressing_mode();  	setup_memory(); + +	check_initrd(); +	reserve_crashkernel(); +  	setup_resources(); +	setup_vmcoreinfo();  	setup_lowcore(); - +	smp_fill_possible_mask();          cpu_init();  	s390_init_cpu_topology(); @@ -843,5 +898,37 @@ setup_arch(char **cmdline_p)  	set_preferred_console();  	/* Setup zfcpdump support */ -	setup_zfcpdump(console_devno); +	setup_zfcpdump(); +} + +#ifdef CONFIG_32BIT +static int no_removal_warning __initdata; + +static int __init parse_no_removal_warning(char *str) +{ +	no_removal_warning = 1; +	return 0;  } +__setup("no_removal_warning", parse_no_removal_warning); + +static int __init removal_warning(void) +{ +	if (no_removal_warning) +		return 0; +	printk(KERN_ALERT "\n\n"); +	printk(KERN_CONT "Warning - you are using a 31 bit kernel!\n\n"); +	printk(KERN_CONT "We plan to remove 31 bit kernel support from the kernel sources in March 2015.\n"); +	printk(KERN_CONT "Currently we assume that nobody is using the 31 bit kernel on old 31 bit\n"); +	printk(KERN_CONT "hardware anymore. If you think that the code should not be removed and also\n"); +	printk(KERN_CONT "future versions of the Linux kernel should be able to run in 31 bit mode\n"); +	printk(KERN_CONT "please let us know. Please write to:\n"); +	printk(KERN_CONT "linux390@de.ibm.com (mail address) and/or\n"); +	printk(KERN_CONT "linux-s390@vger.kernel.org (mailing list).\n\n"); +	printk(KERN_CONT "Thank you!\n\n"); +	printk(KERN_CONT "If this kernel runs on a 64 bit machine you may consider using a 64 bit kernel.\n"); +	printk(KERN_CONT "This message can be disabled with the \"no_removal_warning\" kernel parameter.\n"); +	schedule_timeout_uninterruptible(300 * HZ); +	return 0; +} +early_initcall(removal_warning); +#endif  | 
