Skip to content

Commit

Permalink
s390: unify identity mapping limits handling
Browse files Browse the repository at this point in the history
Currently we have to consider too many different values which
in the end only affect identity mapping size. These are:
1. max_physmem_end - end of physical memory online or standby.
   Always <= end of the last online memory block (get_mem_detect_end()).
2. CONFIG_MAX_PHYSMEM_BITS - the maximum size of physical memory the
   kernel is able to support.
3. "mem=" kernel command line option which limits physical memory usage.
4. OLDMEM_BASE which is a kdump memory limit when the kernel is executed as
   crash kernel.
5. "hsa" size which is a memory limit when the kernel is executed during
   zfcp/nvme dump.

Through out kernel startup and run we juggle all those values at once
but that does not bring any amusement, only confusion and complexity.

Unify all those values to a single one we should really care, that is
our identity mapping size.

Signed-off-by: Vasily Gorbik <[email protected]>
Reviewed-by: Alexander Gordeev <[email protected]>
Acked-by: Heiko Carstens <[email protected]>
Signed-off-by: Heiko Carstens <[email protected]>
  • Loading branch information
Vasily Gorbik authored and hcahca committed Nov 20, 2020
1 parent 1e632ea commit 73045a0
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 80 deletions.
11 changes: 7 additions & 4 deletions arch/s390/boot/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@
#ifndef BOOT_BOOT_H
#define BOOT_BOOT_H

#include <linux/types.h>

void startup_kernel(void);
void detect_memory(void);
unsigned long detect_memory(void);
bool is_ipl_block_dump(void);
void store_ipl_parmblock(void);
void setup_boot_command_line(void);
void parse_boot_command_line(void);
void setup_memory_end(void);
void verify_facilities(void);
void print_missing_facilities(void);
void print_pgm_check_info(void);
unsigned long get_random_base(unsigned long safe_addr);

extern int kaslr_enabled;
extern int vmalloc_size_set;
extern const char kernel_version[];
extern unsigned long memory_limit;
extern int vmalloc_size_set;
extern int kaslr_enabled;

unsigned long read_ipl_report(unsigned long safe_offset);

Expand Down
44 changes: 14 additions & 30 deletions arch/s390/boot/ipl_parm.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ int __bootdata_preserved(ipl_block_valid);
unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;

unsigned long __bootdata(vmalloc_size) = VMALLOC_DEFAULT_SIZE;
unsigned long __bootdata(memory_end);
int __bootdata(memory_end_set);
int __bootdata(noexec_disabled);

unsigned long memory_limit;
int vmalloc_size_set;
int kaslr_enabled;

Expand Down Expand Up @@ -58,6 +57,17 @@ void store_ipl_parmblock(void)
ipl_block_valid = 1;
}

bool is_ipl_block_dump(void)
{
if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
return true;
if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
return true;
return false;
}

static size_t scpdata_length(const u8 *buf, size_t count)
{
while (count) {
Expand Down Expand Up @@ -238,10 +248,8 @@ void parse_boot_command_line(void)
while (*args) {
args = next_arg(args, &param, &val);

if (!strcmp(param, "mem") && val) {
memory_end = round_down(memparse(val, NULL), PAGE_SIZE);
memory_end_set = 1;
}
if (!strcmp(param, "mem") && val)
memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);

if (!strcmp(param, "vmalloc") && val) {
vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
Expand Down Expand Up @@ -282,27 +290,3 @@ void parse_boot_command_line(void)
#endif
}
}

static inline bool is_ipl_block_dump(void)
{
if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
return true;
if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
return true;
return false;
}

void setup_memory_end(void)
{
#ifdef CONFIG_CRASH_DUMP
if (OLDMEM_BASE) {
kaslr_enabled = 0;
} else if (ipl_block_valid && is_ipl_block_dump()) {
kaslr_enabled = 0;
if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
memory_end_set = 1;
}
#endif
}
3 changes: 1 addition & 2 deletions arch/s390/boot/kaslr.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ unsigned long get_random_base(unsigned long safe_addr)
unsigned long kasan_needs;
int i;

if (memory_end_set)
memory_limit = min(memory_limit, memory_end);
memory_limit = min(memory_limit, ident_map_size);

/*
* Avoid putting kernel in the end of physical memory
Expand Down
13 changes: 7 additions & 6 deletions arch/s390/boot/mem_detect.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include "compressed/decompressor.h"
#include "boot.h"

unsigned long __bootdata(max_physmem_end);
struct mem_detect_info __bootdata(mem_detect);

/* up to 256 storage elements, 1020 subincrements each */
Expand Down Expand Up @@ -149,27 +148,29 @@ static void search_mem_end(void)
add_mem_detect_block(0, (offset + 1) << 20);
}

void detect_memory(void)
unsigned long detect_memory(void)
{
unsigned long max_physmem_end;

sclp_early_get_memsize(&max_physmem_end);

if (!sclp_early_read_storage_info()) {
mem_detect.info_source = MEM_DETECT_SCLP_STOR_INFO;
return;
return max_physmem_end;
}

if (!diag260()) {
mem_detect.info_source = MEM_DETECT_DIAG260;
return;
return max_physmem_end;
}

if (max_physmem_end) {
add_mem_detect_block(0, max_physmem_end);
mem_detect.info_source = MEM_DETECT_SCLP_READ_INFO;
return;
return max_physmem_end;
}

search_mem_end();
mem_detect.info_source = MEM_DETECT_BIN_SEARCH;
max_physmem_end = get_mem_detect_end();
return get_mem_detect_end();
}
48 changes: 44 additions & 4 deletions arch/s390/boot/startup.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/string.h>
#include <linux/elf.h>
#include <asm/boot_data.h>
#include <asm/sections.h>
#include <asm/cpu_mf.h>
#include <asm/setup.h>
Expand All @@ -14,6 +15,7 @@
extern char __boot_data_start[], __boot_data_end[];
extern char __boot_data_preserved_start[], __boot_data_preserved_end[];
unsigned long __bootdata_preserved(__kaslr_offset);
unsigned long __bootdata(ident_map_size);

/*
* Some code and data needs to stay below 2 GB, even when the kernel would be
Expand Down Expand Up @@ -127,6 +129,46 @@ static void handle_relocs(unsigned long offset)
}
}

/*
* Merge information from several sources into a single ident_map_size value.
* "ident_map_size" represents the upper limit of physical memory we may ever
* reach. It might not be all online memory, but also include standby (offline)
* memory. "ident_map_size" could be lower then actual standby or even online
* memory present, due to limiting factors. We should never go above this limit.
* It is the size of our identity mapping.
*
* Consider the following factors:
* 1. max_physmem_end - end of physical memory online or standby.
* Always <= end of the last online memory block (get_mem_detect_end()).
* 2. CONFIG_MAX_PHYSMEM_BITS - the maximum size of physical memory the
* kernel is able to support.
* 3. "mem=" kernel command line option which limits physical memory usage.
* 4. OLDMEM_BASE which is a kdump memory limit when the kernel is executed as
* crash kernel.
* 5. "hsa" size which is a memory limit when the kernel is executed during
* zfcp/nvme dump.
*/
static void setup_ident_map_size(unsigned long max_physmem_end)
{
unsigned long hsa_size;

ident_map_size = max_physmem_end;
if (memory_limit)
ident_map_size = min(ident_map_size, memory_limit);
ident_map_size = min(ident_map_size, 1UL << MAX_PHYSMEM_BITS);

#ifdef CONFIG_CRASH_DUMP
if (OLDMEM_BASE) {
kaslr_enabled = 0;
ident_map_size = min(ident_map_size, OLDMEM_SIZE);
} else if (ipl_block_valid && is_ipl_block_dump()) {
kaslr_enabled = 0;
if (!sclp_early_get_hsa_size(&hsa_size) && hsa_size)
ident_map_size = min(ident_map_size, hsa_size);
}
#endif
}

/*
* This function clears the BSS section of the decompressed Linux kernel and NOT the decompressor's.
*/
Expand All @@ -145,8 +187,7 @@ static void setup_vmalloc_size(void)

if (vmalloc_size_set)
return;
size = (memory_end ?: max_physmem_end) >> 3;
size = round_up(size, _SEGMENT_SIZE);
size = round_up(ident_map_size / 8, _SEGMENT_SIZE);
vmalloc_size = max(size, vmalloc_size);
}

Expand All @@ -165,8 +206,7 @@ void startup_kernel(void)
sclp_early_read_info();
setup_boot_command_line();
parse_boot_command_line();
setup_memory_end();
detect_memory();
setup_ident_map_size(detect_memory());
setup_vmalloc_size();

random_lma = __kaslr_offset = 0;
Expand Down
4 changes: 1 addition & 3 deletions arch/s390/include/asm/setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ extern unsigned int zlib_dfltcc_support;
#define ZLIB_DFLTCC_FULL_DEBUG 4

extern int noexec_disabled;
extern int memory_end_set;
extern unsigned long memory_end;
extern unsigned long ident_map_size;
extern unsigned long vmalloc_size;
extern unsigned long max_physmem_end;

/* The Write Back bit position in the physaddr is given by the SLPC PCI */
extern unsigned long mio_wb_bit_mask;
Expand Down
37 changes: 17 additions & 20 deletions arch/s390/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,8 @@ char elf_platform[ELF_PLATFORM_SIZE];
unsigned long int_hwcap = 0;

int __bootdata(noexec_disabled);
int __bootdata(memory_end_set);
unsigned long __bootdata(memory_end);
unsigned long __bootdata(ident_map_size);
unsigned long __bootdata(vmalloc_size);
unsigned long __bootdata(max_physmem_end);
struct mem_detect_info __bootdata(mem_detect);

struct exception_table_entry *__bootdata_preserved(__start_dma_ex_table);
Expand Down Expand Up @@ -557,12 +555,12 @@ static void __init setup_resources(void)
#endif
}

static void __init setup_memory_end(void)
static void __init setup_ident_map_size(void)
{
unsigned long vmax, tmp;

/* Choose kernel address space layout: 3 or 4 levels. */
tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE;
tmp = ident_map_size / PAGE_SIZE;
tmp = tmp * (sizeof(struct page) + PAGE_SIZE);
if (tmp + vmalloc_size + MODULES_LEN <= _REGION2_SIZE)
vmax = _REGION2_SIZE; /* 3-level kernel page table */
Expand All @@ -589,22 +587,22 @@ static void __init setup_memory_end(void)
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, (unsigned long)vmemmap);
/* Take care that ident_map_size <= vmemmap */
ident_map_size = min(ident_map_size, (unsigned long)vmemmap);
#ifdef CONFIG_KASAN
memory_end = min(memory_end, KASAN_SHADOW_START);
ident_map_size = min(ident_map_size, KASAN_SHADOW_START);
#endif
vmemmap_size = SECTION_ALIGN_UP(memory_end / PAGE_SIZE) * sizeof(struct page);
vmemmap_size = SECTION_ALIGN_UP(ident_map_size / PAGE_SIZE) * sizeof(struct page);
#ifdef CONFIG_KASAN
/* move vmemmap above kasan shadow only if stands in a way */
if (KASAN_SHADOW_END > (unsigned long)vmemmap &&
(unsigned long)vmemmap + vmemmap_size > KASAN_SHADOW_START)
vmemmap = max(vmemmap, (struct page *)KASAN_SHADOW_END);
#endif
max_pfn = max_low_pfn = PFN_DOWN(memory_end);
memblock_remove(memory_end, ULONG_MAX);
max_pfn = max_low_pfn = PFN_DOWN(ident_map_size);
memblock_remove(ident_map_size, ULONG_MAX);

pr_notice("The maximum memory size is %luMB\n", memory_end >> 20);
pr_notice("The maximum memory size is %luMB\n", ident_map_size >> 20);
}

#ifdef CONFIG_CRASH_DUMP
Expand Down Expand Up @@ -634,12 +632,11 @@ static struct notifier_block kdump_mem_nb = {
#endif

/*
* Make sure that the area behind memory_end is protected
* Make sure that the area above identity mapping is protected
*/
static void __init reserve_memory_end(void)
static void __init reserve_above_ident_map(void)
{
if (memory_end_set)
memblock_reserve(memory_end, ULONG_MAX);
memblock_reserve(ident_map_size, ULONG_MAX);
}

/*
Expand Down Expand Up @@ -676,7 +673,7 @@ static void __init reserve_crashkernel(void)
phys_addr_t low, high;
int rc;

rc = parse_crashkernel(boot_command_line, memory_end, &crash_size,
rc = parse_crashkernel(boot_command_line, ident_map_size, &crash_size,
&crash_base);

crash_base = ALIGN(crash_base, KEXEC_CRASH_MEM_ALIGN);
Expand Down Expand Up @@ -1130,7 +1127,7 @@ void __init setup_arch(char **cmdline_p)
setup_control_program_code();

/* Do some memory reservations *before* memory is added to memblock */
reserve_memory_end();
reserve_above_ident_map();
reserve_oldmem();
reserve_kernel();
reserve_initrd();
Expand All @@ -1145,9 +1142,9 @@ void __init setup_arch(char **cmdline_p)
remove_oldmem();

setup_uv();
setup_memory_end();
setup_ident_map_size();
setup_memory();
dma_contiguous_reserve(memory_end);
dma_contiguous_reserve(ident_map_size);
vmcp_cma_reserve();

check_initrd();
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/mm/dump_pagetables.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ static int pt_dump_init(void)
*/
max_addr = (S390_lowcore.kernel_asce & _REGION_ENTRY_TYPE_MASK) >> 2;
max_addr = 1UL << (max_addr * 11 + 31);
address_markers[IDENTITY_AFTER_END_NR].start_address = memory_end;
address_markers[IDENTITY_AFTER_END_NR].start_address = ident_map_size;
address_markers[MODULES_NR].start_address = MODULES_VADDR;
address_markers[MODULES_END_NR].start_address = MODULES_END;
address_markers[VMEMMAP_NR].start_address = (unsigned long) vmemmap;
Expand Down
21 changes: 14 additions & 7 deletions arch/s390/mm/kasan_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,19 @@ void __init kasan_early_init(void)
memsize = get_mem_detect_end();
if (!memsize)
kasan_early_panic("cannot detect physical memory size\n");
/* respect mem= cmdline parameter */
if (memory_end_set && memsize > memory_end)
memsize = memory_end;
if (IS_ENABLED(CONFIG_CRASH_DUMP) && OLDMEM_BASE)
memsize = min(memsize, OLDMEM_SIZE);
memsize = min(memsize, KASAN_SHADOW_START);
/*
* Kasan currently supports standby memory but only if it follows
* online memory (default allocation), i.e. no memory holes.
* - memsize represents end of online memory
* - ident_map_size represents online + standby and memory limits
* accounted.
* Kasan maps "memsize" right away.
* [0, memsize] - as identity mapping
* [__sha(0), __sha(memsize)] - shadow memory for identity mapping
* The rest [memsize, ident_map_size] if memsize < ident_map_size
* could be mapped/unmapped dynamically later during memory hotplug.
*/
memsize = min(memsize, ident_map_size);

BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, P4D_SIZE));
BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, P4D_SIZE));
Expand Down Expand Up @@ -377,7 +384,7 @@ void __init kasan_early_init(void)
POPULATE_SHALLOW);
}
/* populate kasan shadow for untracked memory */
kasan_early_pgtable_populate(__sha(max_physmem_end), __sha(untracked_mem_end),
kasan_early_pgtable_populate(__sha(ident_map_size), __sha(untracked_mem_end),
POPULATE_ZERO_SHADOW);
kasan_early_pgtable_populate(__sha(kasan_vmax), __sha(vmax_unlimited),
POPULATE_ZERO_SHADOW);
Expand Down
Loading

0 comments on commit 73045a0

Please sign in to comment.