Commit 14be4c61 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 's390-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Martin Schwidefsky:

 - Support for kernel address space layout randomization

 - Add support for kernel image signature verification

 - Convert s390 to the generic get_user_pages_fast code

 - Convert s390 to the stack unwind API analog to x86

 - Add support for CPU directed interrupts for PCI devices

 - Provide support for MIO instructions to the PCI base layer, this will
   allow the use of direct PCI mappings in user space code

 - Add the basic KVM guest ultravisor interface for protected VMs

 - Add AT_HWCAP bits for several new hardware capabilities

 - Update the CPU measurement facility counter definitions to SVN 6

 - Arnds cleanup patches for his quest to get LLVM compiles working

 - A vfio-ccw update with bug fixes and support for halt and clear

 - Improvements for the hardware TRNG code

 - Another round of cleanup for the QDIO layer

 - Numerous cleanups and bug fixes

* tag 's390-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (98 commits)
  s390/vdso: drop unnecessary cc-ldoption
  s390: fix clang -Wpointer-sign warnigns in boot code
  s390: drop CONFIG_VIRT_TO_BUS
  s390: boot, purgatory: pass $(CLANG_FLAGS) where needed
  s390: only build for new CPUs with clang
  s390: simplify disabled_wait
  s390/ftrace: use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
  s390/unwind: introduce stack unwind API
  s390/opcodes: add missing instructions to the disassembler
  s390/bug: add entry size to the __bug_table section
  s390: use proper expoline sections for .dma code
  s390/nospec: rename assembler generated expoline thunks
  s390: add missing ENDPROC statements to assembler functions
  locking/lockdep: check for freed initmem in static_obj()
  s390/kernel: add support for kernel address space layout randomization (KASLR)
  s390/kernel: introduce .dma sections
  s390/sclp: do not use static sccbs
  s390/kprobes: use static buffer for insn_page
  s390/kernel: convert SYSCALL and PGM_CHECK handlers to .quad
  s390/kernel: build a relocatable kernel
  ...
parents ccbc2e5e ce968f60
......@@ -3429,6 +3429,8 @@
bridges without forcing it upstream. Note:
this removes isolation between devices and
may put more devices in an IOMMU group.
force_floating [S390] Force usage of floating interrupts.
nomio [S390] Do not use MIO instructions.
pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power
Management.
......
......@@ -143,6 +143,7 @@ config S390
select HAVE_FUNCTION_TRACER
select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_GUP
select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4
......@@ -184,7 +185,6 @@ config S390
select TTY
select VIRT_CPU_ACCOUNTING
select ARCH_HAS_SCALED_CPUTIME
select VIRT_TO_BUS
select HAVE_NMI
......@@ -236,6 +236,7 @@ choice
config MARCH_Z900
bool "IBM zSeries model z800 and z900"
depends on !CC_IS_CLANG
select HAVE_MARCH_Z900_FEATURES
help
Select this to enable optimizations for model z800/z900 (2064 and
......@@ -244,6 +245,7 @@ config MARCH_Z900
config MARCH_Z990
bool "IBM zSeries model z890 and z990"
depends on !CC_IS_CLANG
select HAVE_MARCH_Z990_FEATURES
help
Select this to enable optimizations for model z890/z990 (2084 and
......@@ -252,6 +254,7 @@ config MARCH_Z990
config MARCH_Z9_109
bool "IBM System z9"
depends on !CC_IS_CLANG
select HAVE_MARCH_Z9_109_FEATURES
help
Select this to enable optimizations for IBM System z9 (2094 and
......@@ -343,12 +346,15 @@ config TUNE_DEFAULT
config TUNE_Z900
bool "IBM zSeries model z800 and z900"
depends on !CC_IS_CLANG
config TUNE_Z990
bool "IBM zSeries model z890 and z990"
depends on !CC_IS_CLANG
config TUNE_Z9_109
bool "IBM System z9"
depends on !CC_IS_CLANG
config TUNE_Z10
bool "IBM System z10"
......@@ -384,6 +390,9 @@ config COMPAT
(and some other stuff like libraries and such) is needed for
executing 31 bit applications. It is safe to say "Y".
config COMPAT_VDSO
def_bool COMPAT && !CC_IS_CLANG
config SYSVIPC_COMPAT
def_bool y if COMPAT && SYSVIPC
......@@ -545,6 +554,17 @@ config ARCH_HAS_KEXEC_PURGATORY
def_bool y
depends on KEXEC_FILE
config KEXEC_VERIFY_SIG
bool "Verify kernel signature during kexec_file_load() syscall"
depends on KEXEC_FILE && SYSTEM_DATA_VERIFICATION
help
This option makes kernel signature verification mandatory for
the kexec_file_load() syscall.
In addition to that option, you need to enable signature
verification for the corresponding kernel image type being
loaded in order for this to work.
config ARCH_RANDOM
def_bool y
prompt "s390 architectural random number generation API"
......@@ -605,6 +625,29 @@ config EXPOLINE_FULL
endchoice
config RELOCATABLE
bool "Build a relocatable kernel"
select MODULE_REL_CRCS if MODVERSIONS
default y
help
This builds a kernel image that retains relocation information
so it can be loaded at an arbitrary address.
The kernel is linked as a position-independent executable (PIE)
and contains dynamic relocations which are processed early in the
bootup process.
The relocations make the kernel image about 15% larger (compressed
10%), but are discarded at runtime.
config RANDOMIZE_BASE
bool "Randomize the address of the kernel image (KASLR)"
depends on RELOCATABLE
default y
help
In support of Kernel Address Space Layout Randomization (KASLR),
this randomizes the address at which the kernel image is loaded,
as a security feature that deters exploit attempts relying on
knowledge of the location of kernel internals.
endmenu
menu "Memory setup"
......@@ -833,6 +876,17 @@ config HAVE_PNETID
menu "Virtualization"
config PROTECTED_VIRTUALIZATION_GUEST
def_bool n
prompt "Protected virtualization guest support"
help
Select this option, if you want to be able to run this
kernel as a protected virtualization KVM guest.
Protected virtualization capable machines have a mini hypervisor
located at machine level (an ultravisor). With help of the
Ultravisor, KVM will be able to run "protected" VMs, special
VMs whose memory and management data are unavailable to KVM.
config PFAULT
def_bool y
prompt "Pseudo page fault support"
......
......@@ -16,10 +16,14 @@ KBUILD_AFLAGS_MODULE += -fPIC
KBUILD_CFLAGS_MODULE += -fPIC
KBUILD_AFLAGS += -m64
KBUILD_CFLAGS += -m64
ifeq ($(CONFIG_RELOCATABLE),y)
KBUILD_CFLAGS += -fPIE
LDFLAGS_vmlinux := -pie
endif
aflags_dwarf := -Wa,-gdwarf-2
KBUILD_AFLAGS_DECOMPRESSOR := -m64 -D__ASSEMBLY__
KBUILD_AFLAGS_DECOMPRESSOR := $(CLANG_FLAGS) -m64 -D__ASSEMBLY__
KBUILD_AFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO),$(aflags_dwarf))
KBUILD_CFLAGS_DECOMPRESSOR := -m64 -O2
KBUILD_CFLAGS_DECOMPRESSOR := $(CLANG_FLAGS) -m64 -O2
KBUILD_CFLAGS_DECOMPRESSOR += -DDISABLE_BRANCH_PROFILING -D__NO_FORTIFY
KBUILD_CFLAGS_DECOMPRESSOR += -fno-delete-null-pointer-checks -msoft-float
KBUILD_CFLAGS_DECOMPRESSOR += -fno-asynchronous-unwind-tables
......@@ -111,7 +115,7 @@ endif
cfi := $(call as-instr,.cfi_startproc\n.cfi_val_offset 15$(comma)-160\n.cfi_endproc,-DCONFIG_AS_CFI_VAL_OFFSET=1)
KBUILD_CFLAGS += -mbackchain -msoft-float $(cflags-y)
KBUILD_CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare
KBUILD_CFLAGS += -pipe -Wno-sign-compare
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables $(cfi)
KBUILD_AFLAGS += $(aflags-y) $(cfi)
export KBUILD_AFLAGS_DECOMPRESSOR
......
......@@ -12,25 +12,35 @@ KBUILD_AFLAGS := $(KBUILD_AFLAGS_DECOMPRESSOR)
KBUILD_CFLAGS := $(KBUILD_CFLAGS_DECOMPRESSOR)
#
# Use -march=z900 for als.c to be able to print an error
# Use minimum architecture for als.c to be able to print an error
# message if the kernel is started on a machine which is too old
#
ifneq ($(CC_FLAGS_MARCH),-march=z900)
ifndef CONFIG_CC_IS_CLANG
CC_FLAGS_MARCH_MINIMUM := -march=z900
else
CC_FLAGS_MARCH_MINIMUM := -march=z10
endif
ifneq ($(CC_FLAGS_MARCH),$(CC_FLAGS_MARCH_MINIMUM))
AFLAGS_REMOVE_head.o += $(CC_FLAGS_MARCH)
AFLAGS_head.o += -march=z900
AFLAGS_head.o += $(CC_FLAGS_MARCH_MINIMUM)
AFLAGS_REMOVE_mem.o += $(CC_FLAGS_MARCH)
AFLAGS_mem.o += -march=z900
AFLAGS_mem.o += $(CC_FLAGS_MARCH_MINIMUM)
CFLAGS_REMOVE_als.o += $(CC_FLAGS_MARCH)
CFLAGS_als.o += -march=z900
CFLAGS_als.o += $(CC_FLAGS_MARCH_MINIMUM)
CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_MARCH)
CFLAGS_sclp_early_core.o += -march=z900
CFLAGS_sclp_early_core.o += $(CC_FLAGS_MARCH_MINIMUM)
endif
CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o string.o ebcdic.o
obj-y += sclp_early_core.o mem.o ipl_vmparm.o cmdline.o ctype.o
targets := bzImage startup.a section_cmp.boot.data $(obj-y)
obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o
obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
obj-y += ctype.o text_dma.o
obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o
obj-$(CONFIG_RELOCATABLE) += machine_kexec_reloc.o
obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
targets := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y)
subdir- := compressed
OBJECTS := $(addprefix $(obj)/,$(obj-y))
......@@ -48,7 +58,8 @@ define cmd_section_cmp
touch $@
endef
$(obj)/bzImage: $(obj)/compressed/vmlinux $(obj)/section_cmp.boot.data FORCE
OBJCOPYFLAGS_bzImage := --pad-to $$(readelf -s $(obj)/compressed/vmlinux | awk '/\<_end\>/ {print or(strtonum("0x"$$2),4095)+1}')
$(obj)/bzImage: $(obj)/compressed/vmlinux $(obj)/section_cmp.boot.data $(obj)/section_cmp.boot.preserved.data FORCE
$(call if_changed,objcopy)
$(obj)/section_cmp%: vmlinux $(obj)/compressed/vmlinux FORCE
......
......@@ -99,7 +99,7 @@ static void facility_mismatch(void)
print_machine_type();
print_missing_facilities();
sclp_early_printk("See Principles of Operations for facility bits\n");
disabled_wait(0x8badcccc);
disabled_wait();
}
void verify_facilities(void)
......
......@@ -9,5 +9,10 @@ void setup_boot_command_line(void);
void parse_boot_command_line(void);
void setup_memory_end(void);
void print_missing_facilities(void);
unsigned long get_random_base(unsigned long safe_addr);
extern int kaslr_enabled;
unsigned long read_ipl_report(unsigned long safe_offset);
#endif /* BOOT_BOOT_H */
......@@ -17,6 +17,11 @@ struct vmlinux_info {
unsigned long bss_size; /* uncompressed image .bss size */
unsigned long bootdata_off;
unsigned long bootdata_size;
unsigned long bootdata_preserved_off;
unsigned long bootdata_preserved_size;
unsigned long dynsym_start;
unsigned long rela_dyn_start;
unsigned long rela_dyn_end;
};
extern char _vmlinux_info[];
......
......@@ -33,7 +33,29 @@ SECTIONS
*(.data.*)
_edata = . ;
}
/*
* .dma section for code, data, ex_table that need to stay below 2 GB,
* even when the kernel is relocate: above 2 GB.
*/
_sdma = .;
.dma.text : {
. = ALIGN(PAGE_SIZE);
_stext_dma = .;
*(.dma.text)
. = ALIGN(PAGE_SIZE);
_etext_dma = .;
}
. = ALIGN(16);
.dma.ex_table : {
_start_dma_ex_table = .;
KEEP(*(.dma.ex_table))
_stop_dma_ex_table = .;
}
.dma.data : { *(.dma.data) }
_edma = .;
BOOT_DATA
BOOT_DATA_PRESERVED
/*
* uncompressed image info used by the decompressor it should match
......
......@@ -305,7 +305,7 @@ ENTRY(startup_kdump)
xc 0x300(256),0x300
xc 0xe00(256),0xe00
xc 0xf00(256),0xf00
lctlg %c0,%c15,0x200(%r0) # initialize control registers
lctlg %c0,%c15,.Lctl-.LPG0(%r13) # load control registers
stcke __LC_BOOT_CLOCK
mvc __LC_LAST_UPDATE_CLOCK(8),__LC_BOOT_CLOCK+1
spt 6f-.LPG0(%r13)
......@@ -319,20 +319,54 @@ ENTRY(startup_kdump)
.align 8
6: .long 0x7fffffff,0xffffffff
.Lctl: .quad 0x04040000 # cr0: AFP registers & secondary space
.quad 0 # cr1: primary space segment table
.quad .Lduct # cr2: dispatchable unit control table
.quad 0 # cr3: instruction authorization
.quad 0xffff # cr4: instruction authorization
.quad .Lduct # cr5: primary-aste origin
.quad 0 # cr6: I/O interrupts
.quad 0 # cr7: secondary space segment table
.quad 0 # cr8: access registers translation
.quad 0 # cr9: tracing off
.quad 0 # cr10: tracing off
.quad 0 # cr11: tracing off
.quad 0 # cr12: tracing off
.quad 0 # cr13: home space segment table
.quad 0xc0000000 # cr14: machine check handling off
.quad .Llinkage_stack # cr15: linkage stack operations
.section .dma.data,"aw",@progbits
.Lduct: .long 0,.Laste,.Laste,0,.Lduald,0,0,0
.long 0,0,0,0,0,0,0,0
.Llinkage_stack:
.long 0,0,0x89000000,0,0,0,0x8a000000,0
.align 64
.Laste: .quad 0,0xffffffffffffffff,0,0,0,0,0,0
.align 128
.Lduald:.rept 8
.long 0x80000000,0,0,0 # invalid access-list entries
.endr
.previous
#include "head_kdump.S"
#
# params at 10400 (setup.h)
# Must be keept in sync with struct parmarea in setup.h
#
.org PARMAREA
.long 0,0 # IPL_DEVICE
.long 0,0 # INITRD_START
.long 0,0 # INITRD_SIZE
.long 0,0 # OLDMEM_BASE
.long 0,0 # OLDMEM_SIZE
.quad 0 # IPL_DEVICE
.quad 0 # INITRD_START
.quad 0 # INITRD_SIZE
.quad 0 # OLDMEM_BASE
.quad 0 # OLDMEM_SIZE
.org COMMAND_LINE
.byte "root=/dev/ram0 ro"
.byte 0
.org 0x11000
.org EARLY_SCCB_OFFSET
.fill 4096
.org HEAD_END
......@@ -7,16 +7,19 @@
#include <asm/sections.h>
#include <asm/boot_data.h>
#include <asm/facility.h>
#include <asm/uv.h>
#include "boot.h"
char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
struct ipl_parameter_block __bootdata(early_ipl_block);
int __bootdata(early_ipl_block_valid);
struct ipl_parameter_block __bootdata_preserved(ipl_block);
int __bootdata_preserved(ipl_block_valid);
unsigned long __bootdata(memory_end);
int __bootdata(memory_end_set);
int __bootdata(noexec_disabled);
int kaslr_enabled __section(.data);
static inline int __diag308(unsigned long subcode, void *addr)
{
register unsigned long _addr asm("0") = (unsigned long)addr;
......@@ -45,13 +48,15 @@ void store_ipl_parmblock(void)
{
int rc;
rc = __diag308(DIAG308_STORE, &early_ipl_block);
uv_set_shared(__pa(&ipl_block));
rc = __diag308(DIAG308_STORE, &ipl_block);
uv_remove_shared(__pa(&ipl_block));
if (rc == DIAG308_RC_OK &&
early_ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
early_ipl_block_valid = 1;
ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
ipl_block_valid = 1;
}
static size_t scpdata_length(const char *buf, size_t count)
static size_t scpdata_length(const u8 *buf, size_t count)
{
while (count) {
if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
......@@ -68,26 +73,26 @@ static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
size_t i;
int has_lowercase;
count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
ipb->ipl_info.fcp.scp_data_len));
count = min(size - 1, scpdata_length(ipb->fcp.scp_data,
ipb->fcp.scp_data_len));
if (!count)
goto out;
has_lowercase = 0;
for (i = 0; i < count; i++) {
if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
if (!isascii(ipb->fcp.scp_data[i])) {
count = 0;
goto out;
}
if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
if (!has_lowercase && islower(ipb->fcp.scp_data[i]))
has_lowercase = 1;
}
if (has_lowercase)
memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
memcpy(dest, ipb->fcp.scp_data, count);
else
for (i = 0; i < count; i++)
dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
dest[i] = tolower(ipb->fcp.scp_data[i]);
out:
dest[count] = '\0';
return count;
......@@ -103,14 +108,14 @@ static void append_ipl_block_parm(void)
delim = early_command_line + len; /* '\0' character position */
parm = early_command_line + len + 1; /* append right after '\0' */
switch (early_ipl_block.hdr.pbt) {
case DIAG308_IPL_TYPE_CCW:
switch (ipl_block.pb0_hdr.pbt) {
case IPL_PBT_CCW:
rc = ipl_block_get_ascii_vmparm(
parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
break;
case DIAG308_IPL_TYPE_FCP:
case IPL_PBT_FCP:
rc = ipl_block_get_ascii_scpdata(
parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
break;
}
if (rc) {
......@@ -141,7 +146,7 @@ void setup_boot_command_line(void)
strcpy(early_command_line, strim(COMMAND_LINE));
/* append IPL PARM data to the boot command line */
if (early_ipl_block_valid)
if (!is_prot_virt_guest() && ipl_block_valid)
append_ipl_block_parm();
}
......@@ -211,6 +216,7 @@ void parse_boot_command_line(void)
char *args;
int rc;
kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
args = strcpy(command_line_buf, early_command_line);
while (*args) {
args = next_arg(args, &param, &val);
......@@ -228,15 +234,21 @@ void parse_boot_command_line(void)
if (!strcmp(param, "facilities"))
modify_fac_list(val);
if (!strcmp(param, "nokaslr"))
kaslr_enabled = 0;
}
}
void setup_memory_end(void)
{
#ifdef CONFIG_CRASH_DUMP
if (!OLDMEM_BASE && early_ipl_block_valid &&
early_ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP &&
early_ipl_block.ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) {
if (OLDMEM_BASE) {
kaslr_enabled = 0;
} else if (ipl_block_valid &&
ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) {
kaslr_enabled = 0;
if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
memory_end_set = 1;
}
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/ctype.h>
#include <asm/ebcdic.h>
#include <asm/sclp.h>
#include <asm/sections.h>
#include <asm/boot_data.h>
#include <uapi/asm/ipl.h>
#include "boot.h"
int __bootdata_preserved(ipl_secure_flag);
unsigned long __bootdata_preserved(ipl_cert_list_addr);
unsigned long __bootdata_preserved(ipl_cert_list_size);
unsigned long __bootdata(early_ipl_comp_list_addr);
unsigned long __bootdata(early_ipl_comp_list_size);
#define for_each_rb_entry(entry, rb) \
for (entry = rb->entries; \
(void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
entry++)
static inline bool intersects(unsigned long addr0, unsigned long size0,
unsigned long addr1, unsigned long size1)
{
return addr0 + size0 > addr1 && addr1 + size1 > addr0;
}
static unsigned long find_bootdata_space(struct ipl_rb_components *comps,
struct ipl_rb_certificates *certs,
unsigned long safe_addr)
{
struct ipl_rb_certificate_entry *cert;
struct ipl_rb_component_entry *comp;
size_t size;
/*
* Find the length for the IPL report boot data
*/
early_ipl_comp_list_size = 0;
for_each_rb_entry(comp, comps)
early_ipl_comp_list_size += sizeof(*comp);
ipl_cert_list_size = 0;
for_each_rb_entry(cert, certs)
ipl_cert_list_size += sizeof(unsigned int) + cert->len;
size = ipl_cert_list_size + early_ipl_comp_list_size;
/*
* Start from safe_addr to find a free memory area large
* enough for the IPL report boot data. This area is used
* for ipl_cert_list_addr/ipl_cert_list_size and
* early_ipl_comp_list_addr/early_ipl_comp_list_size. It must
* not overlap with any component or any certificate.
*/
repeat:
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
intersects(INITRD_START, INITRD_SIZE, safe_addr, size))
safe_addr = INITRD_START + INITRD_SIZE;
for_each_rb_entry(comp, comps)
if (intersects(safe_addr, size, comp->addr, comp->len)) {
safe_addr = comp->addr + comp->len;
goto repeat;
}
for_each_rb_entry(cert, certs)
if (intersects(safe_addr, size, cert->addr, cert->len)) {
safe_addr = cert->addr + cert->len;
goto repeat;
}
early_ipl_comp_list_addr = safe_addr;
ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size;
return safe_addr + size;
}
static void copy_components_bootdata(struct ipl_rb_components *comps)
{
struct ipl_rb_component_entry *comp, *ptr;
ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr;
for_each_rb_entry(comp, comps)
memcpy(ptr++, comp, sizeof(*ptr));
}
static void copy_certificates_bootdata(struct ipl_rb_certificates *certs)
{
struct ipl_rb_certificate_entry *cert;
void *ptr;
ptr = (void *) ipl_cert_list_addr;
for_each_rb_entry(cert, certs) {
*(unsigned int *) ptr = cert->len;
ptr += sizeof(unsigned int);
memcpy(ptr, (void *) cert->addr, cert->len);
ptr += cert->len;
}
}
unsigned long read_ipl_report(unsigned long safe_addr)
{
struct ipl_rb_certificates *certs;
struct ipl_rb_components *comps;
struct ipl_pl_hdr *pl_hdr;
struct ipl_rl_hdr *rl_hdr;
struct ipl_rb_hdr *rb_hdr;
unsigned long tmp;
void *rl_end;
/*
* Check if there is a IPL report by looking at the copy
* of the IPL parameter information block.
*/
if (!ipl_block_valid ||
!(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR))
return safe_addr;
ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL);
/*
* There is an IPL report, to find it load the pointer to the
* IPL parameter information block from lowcore and skip past
* the IPL parameter list, then align the address to a double
* word boundary.
*/
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
pl_hdr = (struct ipl_pl_hdr *) tmp;
tmp = (tmp + pl_hdr->len + 7) & -8UL;
rl_hdr = (struct ipl_rl_hdr *) tmp;
/* Walk through the IPL report blocks in the IPL Report list */
certs = NULL;
comps = NULL;
rl_end = (void *) rl_hdr + rl_hdr->len;
rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
(void *) rb_hdr + rb_hdr->len <= rl_end) {
switch (rb_hdr->rbt) {
case IPL_RBT_CERTIFICATES:
certs = (struct ipl_rb_certificates *) rb_hdr;
break;
case IPL_RBT_COMPONENTS:
comps = (struct ipl_rb_components *) rb_hdr;
break;
default:
break;
}
rb_hdr = (void *) rb_hdr + rb_hdr->len;
}
/*
* With either the component list or the certificate list
* missing the kernel will stay ignorant of secure IPL.
*/
if (!comps || !certs)
return safe_addr;
/*
* Copy component and certificate list to a safe area
* where the decompressed kernel can find them.
*/
safe_addr = find_bootdata_space(comps, certs, safe_addr);
copy_components_bootdata(comps);
copy_certificates_bootdata(certs);
return safe_addr;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2019
*/
#include <asm/mem_detect.h>
#include <asm/cpacf.h>
#include <asm/timex.h>
#include <asm/sclp.h>
#include "compressed/decompressor.h"
#define PRNG_MODE_TDES 1
#define PRNG_MODE_SHA512 2
#define PRNG_MODE_TRNG 3
struct prno_parm {
u32 res;
u32 reseed_counter;
u64 stream_bytes;
u8 V[112];
u8 C[112];
};
struct prng_parm {
u8 parm_block[32];
u32 reseed_counter;
u64 byte_counter;
};
static int check_prng(void)
{
if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG)) {
sclp_early_printk("KASLR disabled: CPU has no PRNG\n");
return 0;
}
if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG))
return PRNG_MODE_TRNG;
if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN))
return PRNG_MODE_SHA512;
else
return PRNG_MODE_TDES;
}
static unsigned long get_random(unsigned long limit)
{
struct prng_parm prng = {
/* initial parameter block for tdes mode, copied from libica */
.parm_block = {
0x0F, 0x2B, 0x8E, 0x63, 0x8C, 0x8E, 0xD2, 0x52,
0x64, 0xB7, 0xA0, 0x7B, 0x75, 0x28, 0xB8, 0xF4,
0x75, 0x5F, 0xD2, 0xA6, 0x8D, 0x97, 0x11, 0xFF,
0x49, 0xD8, 0x23, 0xF3, 0x7E, 0x21, 0xEC, 0xA0
},
};
unsigned long seed, random;
struct prno_parm prno;
__u64 entropy[4];
int mode, i;
mode = check_prng();
seed = get_tod_clock_fast();
switch (mode) {
case PRNG_MODE_TRNG:
cpacf_trng(NULL, 0, (u8 *) &random, sizeof(random));
break;
case PRNG_MODE_SHA512:
cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, &prno, NULL, 0,
(u8 *) &seed, sizeof(seed));
cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, &prno, (u8 *) &random,
sizeof(random), NULL, 0);
break;
case PRNG_MODE_TDES:
/* add entropy */
*(unsigned long *) prng.parm_block ^= seed;
for (i = 0; i < 16; i++) {
cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block,
(char *) entropy, (char *) entropy,
sizeof(entropy));
memcpy(prng.parm_block, entropy, sizeof(entropy));
}
random = seed;
cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, (u8 *) &random,
(u8 *) &random, sizeof(random));
break;
default:
random = 0;
}
return random % limit;
}
unsigned long get_random_base(unsigned long safe_addr)
{
unsigned long base, start, end, kernel_size;
unsigned long block_sum, offset;
int i;
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE) {
if (safe_addr < INITRD_START + INITRD_SIZE)
safe_addr = INITRD_START + INITRD_SIZE;
}
safe_addr = ALIGN(safe_addr, THREAD_SIZE);
kernel_size = vmlinux.image_size + vmlinux.bss_size;
block_sum = 0;
for_each_mem_detect_block(i, &start, &end) {
if (memory_end_set) {
if (start >= memory_end)
break;
if (end > memory_end)
end = memory_end;
}
if (end - start < kernel_size)
continue;
block_sum += end - start - kernel_size;
}
if (!block_sum) {
sclp_early_printk("KASLR disabled: not enough memory\n");
return 0;
}
base = get_random(block_sum);
if (base == 0)
return 0;
if (base < safe_addr)
base = safe_addr;
block_sum = offset = 0;
for_each_mem_detect_block(i, &start, &end) {
if (memory_end_set) {
if (start >= memory_end)
break;
if (end > memory_end)
end = memory_end;
}
if (end - start < kernel_size)
continue;
block_sum += end - start - kernel_size;
if (base <= block_sum) {
base = start + base - offset;
base = ALIGN_DOWN(base, THREAD_SIZE);
break;
}
offset = block_sum;
}
return base;
}
// SPDX-License-Identifier: GPL-2.0
#include "../kernel/machine_kexec_reloc.c"
// SPDX-License-Identifier: GPL-2.0
#include <linux/string.h>
#include <linux/elf.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/kexec.h>
#include <asm/sclp.h>
#include <asm/diag.h>
#include <asm/uv.h>
#include "compressed/decompressor.h"
#include "boot.h"
extern char __boot_data_start[], __boot_data_end[];
extern char __boot_data_preserved_start[], __boot_data_preserved_end[];
unsigned long __bootdata_preserved(__kaslr_offset);
/*
* Some code and data needs to stay below 2 GB, even when the kernel would be
* relocated above 2 GB, because it has to use 31 bit addresses.
* Such code and data is part of the .dma section, and its location is passed
* over to the decompressed / relocated kernel via the .boot.preserved.data
* section.
*/
extern char _sdma[], _edma[];
extern char _stext_dma[], _etext_dma[];
extern struct exception_table_entry _start_dma_ex_table[];
extern struct exception_table_entry _stop_dma_ex_table[];
unsigned long __bootdata_preserved(__sdma) = __pa(&_sdma);
unsigned long __bootdata_preserved(__edma) = __pa(&_edma);
unsigned long __bootdata_preserved(__stext_dma) = __pa(&_stext_dma);
unsigned long __bootdata_preserved(__etext_dma) = __pa(&_etext_dma);
struct exception_table_entry *
__bootdata_preserved(__start_dma_ex_table) = _start_dma_ex_table;
struct exception_table_entry *
__bootdata_preserved(__stop_dma_ex_table) = _stop_dma_ex_table;
int _diag210_dma(struct diag210 *addr);
int _diag26c_dma(void *req, void *resp, enum diag26c_sc subcode);
int _diag14_dma(unsigned long rx, unsigned long ry1, unsigned long subcode);
void _diag0c_dma(struct hypfs_diag0c_entry *entry);
void _diag308_reset_dma(void);
struct diag_ops __bootdata_preserved(diag_dma_ops) = {
.diag210 = _diag210_dma,
.diag26c = _diag26c_dma,
.diag14 = _diag14_dma,
.diag0c = _diag0c_dma,
.diag308_reset = _diag308_reset_dma
};
static struct diag210 _diag210_tmp_dma __section(".dma.data");
struct diag210 *__bootdata_preserved(__diag210_tmp_dma) = &_diag210_tmp_dma;
void _swsusp_reset_dma(void);
unsigned long __bootdata_preserved(__swsusp_reset_dma) = __pa(_swsusp_reset_dma);
void error(char *x)
{
......@@ -13,7 +57,7 @@ void error(char *x)
sclp_early_printk(x);
sclp_early_printk("\n\n -- System halted");
disabled_wait(0xdeadbeef);
disabled_wait();
}
#ifdef CONFIG_KERNEL_UNCOMPRESSED
......@@ -23,19 +67,16 @@ unsigned long mem_safe_offset(void)
}
#endif
static void rescue_initrd(void)
static void rescue_initrd(unsigned long addr)
{
unsigned long min_initrd_addr;
if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD))
return;
if (!INITRD_START || !INITRD_SIZE)
return;
min_initrd_addr = mem_safe_offset();
if (min_initrd_addr <= INITRD_START)
if (addr <= INITRD_START)
return;
memmove((void *)min_initrd_addr, (void *)INITRD_START, INITRD_SIZE);
INITRD_START = min_initrd_addr;
memmove((void *)addr, (void *)INITRD_START, INITRD_SIZE);
INITRD_START = addr;
}
static void copy_bootdata(void)
......@@ -43,23 +84,81 @@ static void copy_bootdata(void)
if (__boot_data_end - __boot_data_start != vmlinux.bootdata_size)
error(".boot.data section size mismatch");
memcpy((void *)vmlinux.bootdata_off, __boot_data_start, vmlinux.bootdata_size);
if (__boot_data_preserved_end - __boot_data_preserved_start != vmlinux.bootdata_preserved_size)
error(".boot.preserved.data section size mismatch");
memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size);
}
static void handle_relocs(unsigned long offset)
{
Elf64_Rela *rela_start, *rela_end, *rela;
int r_type, r_sym, rc;
Elf64_Addr loc, val;
Elf64_Sym *dynsym;
rela_start = (Elf64_Rela *) vmlinux.rela_dyn_start;
rela_end = (Elf64_Rela *) vmlinux.rela_dyn_end;
dynsym = (Elf64_Sym *) vmlinux.dynsym_start;
for (rela = rela_start; rela < rela_end; rela++) {
loc = rela->r_offset + offset;
val = rela->r_addend + offset;
r_sym = ELF64_R_SYM(rela->r_info);
if (r_sym)
val += dynsym[r_sym].st_value;
r_type = ELF64_R_TYPE(rela->r_info);
rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0);
if (rc)
error("Unknown relocation type");
}
}
void startup_kernel(void)
{
unsigned long random_lma;
unsigned long safe_addr;
void *img;
rescue_initrd();
sclp_early_read_info();
store_ipl_parmblock();
safe_addr = mem_safe_offset();
safe_addr = read_ipl_report(safe_addr);
uv_query_info();
rescue_initrd(safe_addr);
sclp_early_read_info();
setup_boot_command_line();
parse_boot_command_line();
setup_memory_end();
detect_memory();
random_lma = __kaslr_offset = 0;
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) {
random_lma = get_random_base(safe_addr);
if (random_lma) {
__kaslr_offset = random_lma - vmlinux.default_lma;
img = (void *)vmlinux.default_lma;
vmlinux.default_lma += __kaslr_offset;
vmlinux.entry += __kaslr_offset;
vmlinux.bootdata_off += __kaslr_offset;
vmlinux.bootdata_preserved_off += __kaslr_offset;
vmlinux.rela_dyn_start += __kaslr_offset;
vmlinux.rela_dyn_end += __kaslr_offset;
vmlinux.dynsym_start += __kaslr_offset;
}
}
if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) {
img = decompress_kernel();
memmove((void *)vmlinux.default_lma, img, vmlinux.image_size);
}
} else if (__kaslr_offset)
memcpy((void *)vmlinux.default_lma, img, vmlinux.image_size);
copy_bootdata();
if (IS_ENABLED(CONFIG_RELOCATABLE))
handle_relocs(__kaslr_offset);
if (__kaslr_offset) {
/* Clear non-relocated kernel */
if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED))
memset(img, 0, vmlinux.image_size);
}
vmlinux.entry();
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Code that needs to run below 2 GB.
*
* Copyright IBM Corp. 2019
*/
#include <linux/linkage.h>
#include <asm/errno.h>
#include <asm/sigp.h>
#ifdef CC_USING_EXPOLINE
.pushsection .dma.text.__s390_indirect_jump_r14,"axG"
__dma__s390_indirect_jump_r14:
larl %r1,0f
ex 0,0(%r1)
j .
0: br %r14
.popsection
#endif
.section .dma.text,"ax"
/*
* Simplified version of expoline thunk. The normal thunks can not be used here,
* because they might be more than 2 GB away, and not reachable by the relative
* branch. No comdat, exrl, etc. optimizations used here, because it only
* affects a few functions that are not performance-relevant.
*/
.macro BR_EX_DMA_r14
#ifdef CC_USING_EXPOLINE
jg __dma__s390_indirect_jump_r14
#else
br %r14
#endif
.endm
/*
* int _diag14_dma(unsigned long rx, unsigned long ry1, unsigned long subcode)
*/
ENTRY(_diag14_dma)
lgr %r1,%r2
lgr %r2,%r3
lgr %r3,%r4
lhi %r5,-EIO
sam31
diag %r1,%r2,0x14
.Ldiag14_ex:
ipm %r5
srl %r5,28
.Ldiag14_fault:
sam64
lgfr %r2,%r5
BR_EX_DMA_r14
EX_TABLE_DMA(.Ldiag14_ex, .Ldiag14_fault)
ENDPROC(_diag14_dma)
/*
* int _diag210_dma(struct diag210 *addr)
*/
ENTRY(_diag210_dma)
lgr %r1,%r2
lhi %r2,-1
sam31
diag %r1,%r0,0x210
.Ldiag210_ex:
ipm %r2
srl %r2,28
.Ldiag210_fault:
sam64
lgfr %r2,%r2
BR_EX_DMA_r14
EX_TABLE_DMA(.Ldiag210_ex, .Ldiag210_fault)
ENDPROC(_diag210_dma)
/*
* int _diag26c_dma(void *req, void *resp, enum diag26c_sc subcode)
*/
ENTRY(_diag26c_dma)
lghi %r5,-EOPNOTSUPP
sam31
diag %r2,%r4,0x26c
.Ldiag26c_ex:
sam64
lgfr %r2,%r5
BR_EX_DMA_r14
EX_TABLE_DMA(.Ldiag26c_ex, .Ldiag26c_ex)
ENDPROC(_diag26c_dma)
/*
* void _diag0c_dma(struct hypfs_diag0c_entry *entry)
*/
ENTRY(_diag0c_dma)
sam31
diag %r2,%r2,0x0c
sam64
BR_EX_DMA_r14
ENDPROC(_diag0c_dma)
/*
* void _swsusp_reset_dma(void)
*/
ENTRY(_swsusp_reset_dma)
larl %r1,restart_entry
larl %r2,.Lrestart_diag308_psw
og %r1,0(%r2)
stg %r1,0(%r0)
lghi %r0,0
diag %r0,%r0,0x308
restart_entry:
lhi %r1,1
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
sam64
BR_EX_DMA_r14
ENDPROC(_swsusp_reset_dma)
/*
* void _diag308_reset_dma(void)
*
* Calls diag 308 subcode 1 and continues execution
*/
ENTRY(_diag308_reset_dma)
larl %r4,.Lctlregs # Save control registers
stctg %c0,%c15,0(%r4)
lg %r2,0(%r4) # Disable lowcore protection
nilh %r2,0xefff
larl %r4,.Lctlreg0
stg %r2,0(%r4)
lctlg %c0,%c0,0(%r4)
larl %r4,.Lfpctl # Floating point control register
stfpc 0(%r4)
larl %r4,.Lprefix # Save prefix register
stpx 0(%r4)
larl %r4,.Lprefix_zero # Set prefix register to 0
spx 0(%r4)
larl %r4,.Lcontinue_psw # Save PSW flags
epsw %r2,%r3
stm %r2,%r3,0(%r4)
larl %r4,restart_part2 # Setup restart PSW at absolute 0
larl %r3,.Lrestart_diag308_psw
og %r4,0(%r3) # Save PSW
lghi %r3,0
sturg %r4,%r3 # Use sturg, because of large pages
lghi %r1,1
lghi %r0,0
diag %r0,%r1,0x308
restart_part2:
lhi %r0,0 # Load r0 with zero
lhi %r1,2 # Use mode 2 = ESAME (dump)
sigp %r1,%r0,SIGP_SET_ARCHITECTURE # Switch to ESAME mode
sam64 # Switch to 64 bit addressing mode
larl %r4,.Lctlregs # Restore control registers
lctlg %c0,%c15,0(%r4)
larl %r4,.Lfpctl # Restore floating point ctl register
lfpc 0(%r4)
larl %r4,.Lprefix # Restore prefix register
spx 0(%r4)
larl %r4,.Lcontinue_psw # Restore PSW flags
lpswe 0(%r4)
.Lcontinue:
BR_EX_DMA_r14
ENDPROC(_diag308_reset_dma)
.section .dma.data,"aw",@progbits
.align 8
.Lrestart_diag308_psw:
.long 0x00080000,0x80000000
.align 8
.Lcontinue_psw:
.quad 0,.Lcontinue
.align 8
.Lctlreg0:
.quad 0
.Lctlregs:
.rept 16
.quad 0
.endr
.Lfpctl:
.long 0
.Lprefix:
.long 0
.Lprefix_zero:
.long 0
// SPDX-License-Identifier: GPL-2.0
#include <asm/uv.h>
#include <asm/facility.h>
#include <asm/sections.h>
int __bootdata_preserved(prot_virt_guest);
void uv_query_info(void)
{
struct uv_cb_qui uvcb = {
.header.cmd = UVC_CMD_QUI,
.header.len = sizeof(uvcb)
};
if (!test_facility(158))
return;
if (uv_call(0, (uint64_t)&uvcb))
return;
if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) &&
test_bit_inv(BIT_UVC_CMD_REMOVE_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list))
prot_virt_guest = 1;
}
......@@ -64,6 +64,7 @@ CONFIG_NUMA=y
CONFIG_PREEMPT=y
CONFIG_HZ_100=y
CONFIG_KEXEC_FILE=y
CONFIG_KEXEC_VERIFY_SIG=y
CONFIG_EXPOLINE=y
CONFIG_EXPOLINE_AUTO=y
CONFIG_MEMORY_HOTPLUG=y
......
......@@ -65,6 +65,7 @@ CONFIG_NR_CPUS=512
CONFIG_NUMA=y
CONFIG_HZ_100=y
CONFIG_KEXEC_FILE=y
CONFIG_KEXEC_VERIFY_SIG=y
CONFIG_EXPOLINE=y
CONFIG_EXPOLINE_AUTO=y
CONFIG_MEMORY_HOTPLUG=y
......
......@@ -207,5 +207,6 @@ ENTRY(crc32_be_vgfm_16)
.Ldone:
VLGVF %r2,%v2,3
BR_EX %r14
ENDPROC(crc32_be_vgfm_16)
.previous
......@@ -105,13 +105,14 @@
ENTRY(crc32_le_vgfm_16)
larl %r5,.Lconstants_CRC_32_LE
j crc32_le_vgfm_generic
ENDPROC(crc32_le_vgfm_16)
ENTRY(crc32c_le_vgfm_16)
larl %r5,.Lconstants_CRC_32C_LE
j crc32_le_vgfm_generic
ENDPROC(crc32c_le_vgfm_16)
crc32_le_vgfm_generic:
ENTRY(crc32_le_vgfm_generic)
/* Load CRC-32 constants */
VLM CONST_PERM_LE2BE,CONST_CRC_POLY,0,%r5
......@@ -267,5 +268,6 @@ crc32_le_vgfm_generic:
.Ldone:
VLGVF %r2,%v2,2
BR_EX %r14
ENDPROC(crc32_le_vgfm_generic)
.previous
......@@ -61,6 +61,7 @@ static unsigned int prng_reseed_limit;
module_param_named(reseed_limit, prng_reseed_limit, int, 0);
MODULE_PARM_DESC(prng_reseed_limit, "PRNG reseed limit");
static bool trng_available;
/*
* Any one who considers arithmetical methods of producing random digits is,
......@@ -115,46 +116,68 @@ static const u8 initial_parm_block[32] __initconst = {
/*
* generate_entropy:
* This algorithm produces 64 bytes of entropy data based on 1024
* individual stckf() invocations assuming that each stckf() value
* contributes 0.25 bits of entropy. So the caller gets 256 bit
* entropy per 64 byte or 4 bits entropy per byte.
* This function fills a given buffer with random bytes. The entropy within
* the random bytes given back is assumed to have at least 50% - meaning
* a 64 bytes buffer has at least 64 * 8 / 2 = 256 bits of entropy.
* Within the function the entropy generation is done in junks of 64 bytes.
* So the caller should also ask for buffer fill in multiples of 64 bytes.
* The generation of the entropy is based on the assumption that every stckf()
* invocation produces 0.5 bits of entropy. To accumulate 256 bits of entropy
* at least 512 stckf() values are needed. The entropy relevant part of the
* stckf value is bit 51 (counting starts at the left with bit nr 0) so
* here we use the lower 4 bytes and exor the values into 2k of bufferspace.
* To be on the save side, if there is ever a problem with stckf() the
* other half of the page buffer is filled with bytes from urandom via
* get_random_bytes(), so this function consumes 2k of urandom for each
* requested 64 bytes output data. Finally the buffer page is condensed into
* a 64 byte value by hashing with a SHA512 hash.
*/
static int generate_entropy(u8 *ebuf, size_t nbytes)
{
int n, ret = 0;
u8 *pg, *h, hash[64];
/* allocate 2 pages */
pg = (u8 *) __get_free_pages(GFP_KERNEL, 1);
u8 *pg, pblock[80] = {
/* 8 x 64 bit init values */
0x6A, 0x09, 0xE6, 0x67, 0xF3, 0xBC, 0xC9, 0x08,
0xBB, 0x67, 0xAE, 0x85, 0x84, 0xCA, 0xA7, 0x3B,
0x3C, 0x6E, 0xF3, 0x72, 0xFE, 0x94, 0xF8, 0x2B,
0xA5, 0x4F, 0xF5, 0x3A, 0x5F, 0x1D, 0x36, 0xF1,
0x51, 0x0E, 0x52, 0x7F, 0xAD, 0xE6, 0x82, 0xD1,
0x9B, 0x05, 0x68, 0x8C, 0x2B, 0x3E, 0x6C, 0x1F,
0x1F, 0x83, 0xD9, 0xAB, 0xFB, 0x41, 0xBD, 0x6B,
0x5B, 0xE0, 0xCD, 0x19, 0x13, 0x7E, 0x21, 0x79,
/* 128 bit counter total message bit length */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
/* allocate one page stckf buffer */
pg = (u8 *) __get_free_page(GFP_KERNEL);
if (!pg) {
prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
return -ENOMEM;
}
/* fill the ebuf in chunks of 64 byte each */
while (nbytes) {
/* fill pages with urandom bytes */
get_random_bytes(pg, 2*PAGE_SIZE);
/* exor pages with 1024 stckf values */
for (n = 0; n < 2 * PAGE_SIZE / sizeof(u64); n++) {
u64 *p = ((u64 *)pg) + n;
/* fill lower 2k with urandom bytes */
get_random_bytes(pg, PAGE_SIZE / 2);
/* exor upper 2k with 512 stckf values, offset 4 bytes each */
for (n = 0; n < 512; n++) {
int offset = (PAGE_SIZE / 2) + (n * 4) - 4;
u64 *p = (u64 *)(pg + offset);
*p ^= get_tod_clock_fast();
}
n = (nbytes < sizeof(hash)) ? nbytes : sizeof(hash);
if (n < sizeof(hash))
h = hash;
else
h = ebuf;
/* hash over the filled pages */
cpacf_kimd(CPACF_KIMD_SHA_512, h, pg, 2*PAGE_SIZE);
if (n < sizeof(hash))
memcpy(ebuf, hash, n);
/* hash over the filled page */
cpacf_klmd(CPACF_KLMD_SHA_512, pblock, pg, PAGE_SIZE);
n = (nbytes < 64) ? nbytes : 64;
memcpy(ebuf, pblock, n);
ret += n;
ebuf += n;
nbytes -= n;
}
free_pages((unsigned long)pg, 1);
memzero_explicit(pblock, sizeof(pblock));
memzero_explicit(pg, PAGE_SIZE);
free_page((unsigned long)pg);
return ret;
}
......@@ -344,8 +367,8 @@ static int __init prng_sha512_selftest(void)
static int __init prng_sha512_instantiate(void)
{
int ret, datalen;
u8 seed[64 + 32 + 16];
int ret, datalen, seedlen;
u8 seed[128 + 16];
pr_debug("prng runs in SHA-512 mode "
"with chunksize=%d and reseed_limit=%u\n",
......@@ -368,16 +391,36 @@ static int __init prng_sha512_instantiate(void)
if (ret)
goto outfree;
/* generate initial seed bytestring, with 256 + 128 bits entropy */
ret = generate_entropy(seed, 64 + 32);
if (ret != 64 + 32)
goto outfree;
/* followed by 16 bytes of unique nonce */
get_tod_clock_ext(seed + 64 + 32);
/* generate initial seed, we need at least 256 + 128 bits entropy. */
if (trng_available) {
/*
* Trng available, so use it. The trng works in chunks of
* 32 bytes and produces 100% entropy. So we pull 64 bytes
* which gives us 512 bits entropy.
*/
seedlen = 2 * 32;
cpacf_trng(NULL, 0, seed, seedlen);
} else {
/*
* No trng available, so use the generate_entropy() function.
* This function works in 64 byte junks and produces
* 50% entropy. So we pull 2*64 bytes which gives us 512 bits
* of entropy.
*/
seedlen = 2 * 64;
ret = generate_entropy(seed, seedlen);
if (ret != seedlen)
goto outfree;
}
/* append the seed by 16 bytes of unique nonce */
get_tod_clock_ext(seed + seedlen);
seedlen += 16;
/* initial seed of the prno drng */
/* now initial seed of the prno drng */
cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
&prng_data->prnows, NULL, 0, seed, sizeof(seed));
&prng_data->prnows, NULL, 0, seed, seedlen);
memzero_explicit(seed, sizeof(seed));
/* if fips mode is enabled, generate a first block of random
bytes for the FIPS 140-2 Conditional Self Test */
......@@ -405,17 +448,26 @@ static void prng_sha512_deinstantiate(void)
static int prng_sha512_reseed(void)
{
int ret;
int ret, seedlen;
u8 seed[64];
/* fetch 256 bits of fresh entropy */
ret = generate_entropy(seed, sizeof(seed));
if (ret != sizeof(seed))
return ret;
/* We need at least 256 bits of fresh entropy for reseeding */
if (trng_available) {
/* trng produces 256 bits entropy in 32 bytes */
seedlen = 32;
cpacf_trng(NULL, 0, seed, seedlen);
} else {
/* generate_entropy() produces 256 bits entropy in 64 bytes */
seedlen = 64;
ret = generate_entropy(seed, seedlen);
if (ret != sizeof(seed))
return ret;
}
/* do a reseed of the prno drng with this bytestring */
cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
&prng_data->prnows, NULL, 0, seed, sizeof(seed));
&prng_data->prnows, NULL, 0, seed, seedlen);
memzero_explicit(seed, sizeof(seed));
return 0;
}
......@@ -592,6 +644,7 @@ static ssize_t prng_sha512_read(struct file *file, char __user *ubuf,
ret = -EFAULT;
break;
}
memzero_explicit(p, n);
ubuf += n;
nbytes -= n;
ret += n;
......@@ -773,6 +826,10 @@ static int __init prng_init(void)
if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG))
return -EOPNOTSUPP;
/* check if TRNG subfunction is available */
if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG))
trng_available = true;
/* choose prng mode */
if (prng_mode != PRNG_MODE_TDES) {
/* check for MSA5 support for PRNO operations */
......
......@@ -39,6 +39,7 @@ CONFIG_NR_CPUS=256
CONFIG_NUMA=y
CONFIG_HZ_100=y
CONFIG_KEXEC_FILE=y
CONFIG_KEXEC_VERIFY_SIG=y
CONFIG_CRASH_DUMP=y
CONFIG_HIBERNATION=y
CONFIG_PM_DEBUG=y
......
......@@ -15,27 +15,13 @@
#define DBFS_D0C_HDR_VERSION 0
/*
* Execute diagnose 0c in 31 bit mode
*/
static void diag0c(struct hypfs_diag0c_entry *entry)
{
diag_stat_inc(DIAG_STAT_X00C);
asm volatile (
" sam31\n"
" diag %0,%0,0x0c\n"
" sam64\n"
: /* no output register */
: "a" (entry)
: "memory");
}
/*
* Get hypfs_diag0c_entry from CPU vector and store diag0c data
*/
static void diag0c_fn(void *data)
{
diag0c(((void **) data)[smp_processor_id()]);
diag_stat_inc(DIAG_STAT_X00C);
diag_dma_ops.diag0c(((void **) data)[smp_processor_id()]);
}
/*
......
......@@ -14,7 +14,7 @@
struct airq_struct {
struct hlist_node list; /* Handler queueing. */
void (*handler)(struct airq_struct *); /* Thin-interrupt handler */
void (*handler)(struct airq_struct *airq, bool floating);
u8 *lsi_ptr; /* Local-Summary-Indicator pointer */
u8 lsi_mask; /* Local-Summary-Indicator mask */
u8 isc; /* Interrupt-subclass */
......@@ -35,13 +35,15 @@ struct airq_iv {
unsigned int *data; /* 32 bit value associated with each bit */
unsigned long bits; /* Number of bits in the vector */
unsigned long end; /* Number of highest allocated bit + 1 */
unsigned long flags; /* Allocation flags */
spinlock_t lock; /* Lock to protect alloc & free */
};
#define AIRQ_IV_ALLOC 1 /* Use an allocation bit mask */
#define AIRQ_IV_BITLOCK 2 /* Allocate the lock bit mask */
#define AIRQ_IV_PTR 4 /* Allocate the ptr array */
#define AIRQ_IV_DATA 8 /* Allocate the data array */
#define AIRQ_IV_ALLOC 1 /* Use an allocation bit mask */
#define AIRQ_IV_BITLOCK 2 /* Allocate the lock bit mask */
#define AIRQ_IV_PTR 4 /* Allocate the ptr array */
#define AIRQ_IV_DATA 8 /* Allocate the data array */
#define AIRQ_IV_CACHELINE 16 /* Cacheline alignment for the vector */
struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags);
void airq_iv_release(struct airq_iv *iv);
......
......@@ -73,7 +73,7 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *ptr)
}
#endif
mask = 1UL << (nr & (BITS_PER_LONG - 1));
__atomic64_or(mask, addr);
__atomic64_or(mask, (long *)addr);
}
static inline void clear_bit(unsigned long nr, volatile unsigned long *ptr)
......@@ -94,7 +94,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *ptr)
}
#endif
mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
__atomic64_and(mask, addr);
__atomic64_and(mask, (long *)addr);
}
static inline void change_bit(unsigned long nr, volatile unsigned long *ptr)
......@@ -115,7 +115,7 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *ptr)
}
#endif
mask = 1UL << (nr & (BITS_PER_LONG - 1));
__atomic64_xor(mask, addr);
__atomic64_xor(mask, (long *)addr);
}
static inline int
......@@ -125,7 +125,7 @@ test_and_set_bit(unsigned long nr, volatile unsigned long *ptr)
unsigned long old, mask;
mask = 1UL << (nr & (BITS_PER_LONG - 1));
old = __atomic64_or_barrier(mask, addr);
old = __atomic64_or_barrier(mask, (long *)addr);
return (old & mask) != 0;
}
......@@ -136,7 +136,7 @@ test_and_clear_bit(unsigned long nr, volatile unsigned long *ptr)
unsigned long old, mask;
mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
old = __atomic64_and_barrier(mask, addr);
old = __atomic64_and_barrier(mask, (long *)addr);
return (old & ~mask) != 0;
}
......@@ -147,7 +147,7 @@ test_and_change_bit(unsigned long nr, volatile unsigned long *ptr)
unsigned long old, mask;
mask = 1UL << (nr & (BITS_PER_LONG - 1));
old = __atomic64_xor_barrier(mask, addr);
old = __atomic64_xor_barrier(mask, (long *)addr);
return (old & mask) != 0;
}
......
......@@ -5,7 +5,14 @@
#include <asm/ipl.h>
extern char early_command_line[COMMAND_LINE_SIZE];
extern struct ipl_parameter_block early_ipl_block;
extern int early_ipl_block_valid;
extern struct ipl_parameter_block ipl_block;
extern int ipl_block_valid;
extern int ipl_secure_flag;
extern unsigned long ipl_cert_list_addr;
extern unsigned long ipl_cert_list_size;
extern unsigned long early_ipl_comp_list_addr;
extern unsigned long early_ipl_comp_list_size;
#endif /* _ASM_S390_BOOT_DATA_H */
......@@ -15,7 +15,7 @@
".section .rodata.str,\"aMS\",@progbits,1\n" \
"2: .asciz \""__FILE__"\"\n" \
".previous\n" \
".section __bug_table,\"aw\"\n" \
".section __bug_table,\"awM\",@progbits,%2\n" \
"3: .long 1b-3b,2b-3b\n" \
" .short %0,%1\n" \
" .org 3b+%2\n" \
......@@ -27,17 +27,17 @@
#else /* CONFIG_DEBUG_BUGVERBOSE */
#define __EMIT_BUG(x) do { \
asm volatile( \
"0: j 0b+2\n" \
"1:\n" \
".section __bug_table,\"aw\"\n" \
"2: .long 1b-2b\n" \
" .short %0\n" \
" .org 2b+%1\n" \
".previous\n" \
: : "i" (x), \
"i" (sizeof(struct bug_entry))); \
#define __EMIT_BUG(x) do { \
asm volatile( \
"0: j 0b+2\n" \
"1:\n" \
".section __bug_table,\"awM\",@progbits,%1\n" \
"2: .long 1b-2b\n" \
" .short %0\n" \
" .org 2b+%1\n" \
".previous\n" \
: : "i" (x), \
"i" (sizeof(struct bug_entry))); \
} while (0)
#endif /* CONFIG_DEBUG_BUGVERBOSE */
......
......@@ -308,4 +308,17 @@ union diag318_info {
int diag204(unsigned long subcode, unsigned long size, void *addr);
int diag224(void *ptr);
int diag26c(void *req, void *resp, enum diag26c_sc subcode);
struct hypfs_diag0c_entry;
struct diag_ops {
int (*diag210)(struct diag210 *addr);
int (*diag26c)(void *req, void *resp, enum diag26c_sc subcode);
int (*diag14)(unsigned long rx, unsigned long ry1, unsigned long subcode);
void (*diag0c)(struct hypfs_diag0c_entry *entry);
void (*diag308_reset)(void);
};
extern struct diag_ops diag_dma_ops;
extern struct diag210 *__diag210_tmp_dma;
#endif /* _ASM_S390_DIAG_H */
......@@ -20,7 +20,7 @@ extern __u8 _ebc_tolower[256]; /* EBCDIC -> lowercase */
extern __u8 _ebc_toupper[256]; /* EBCDIC -> uppercase */
static inline void
codepage_convert(const __u8 *codepage, volatile __u8 * addr, unsigned long nr)
codepage_convert(const __u8 *codepage, volatile char *addr, unsigned long nr)
{
if (nr-- <= 0)
return;
......
......@@ -107,6 +107,10 @@
#define HWCAP_S390_VXRS_BCD 4096
#define HWCAP_S390_VXRS_EXT 8192
#define HWCAP_S390_GS 16384
#define HWCAP_S390_VXRS_EXT2 32768
#define HWCAP_S390_VXRS_PDE 65536
#define HWCAP_S390_SORT 131072
#define HWCAP_S390_DFLT 262144
/* Internal bits, not exposed via elf */
#define HWCAP_INT_SIE 1UL
......
......@@ -19,6 +19,11 @@ struct exception_table_entry
int insn, fixup;
};
extern struct exception_table_entry *__start_dma_ex_table;
extern struct exception_table_entry *__stop_dma_ex_table;
const struct exception_table_entry *s390_search_extables(unsigned long addr);
static inline unsigned long extable_fixup(const struct exception_table_entry *x)
{
return (unsigned long)&x->fixup + x->fixup;
......
......@@ -11,9 +11,16 @@
#define MCOUNT_RETURN_FIXUP 18
#endif
#define HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
#ifndef __ASSEMBLY__
#ifdef CONFIG_CC_IS_CLANG
/* https://bugs.llvm.org/show_bug.cgi?id=41424 */
#define ftrace_return_address(n) 0UL
#else
#define ftrace_return_address(n) __builtin_return_address(n)
#endif
void _mcount(void);
void ftrace_caller(void);
......
......@@ -30,14 +30,8 @@ void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr);
#define ioremap_wc ioremap_nocache
#define ioremap_wt ioremap_nocache
static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
{
return (void __iomem *) offset;
}
static inline void iounmap(volatile void __iomem *addr)
{
}
void __iomem *ioremap(unsigned long offset, unsigned long size);
void iounmap(volatile void __iomem *addr);
static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
{
......@@ -57,14 +51,17 @@ static inline void ioport_unmap(void __iomem *p)
* the corresponding device and create the mapping cookie.
*/
#define pci_iomap pci_iomap
#define pci_iomap_range pci_iomap_range
#define pci_iounmap pci_iounmap
#define pci_iomap_wc pci_iomap
#define pci_iomap_wc_range pci_iomap_range
#define pci_iomap_wc pci_iomap_wc
#define pci_iomap_wc_range pci_iomap_wc_range
#define memcpy_fromio(dst, src, count) zpci_memcpy_fromio(dst, src, count)
#define memcpy_toio(dst, src, count) zpci_memcpy_toio(dst, src, count)
#define memset_io(dst, val, count) zpci_memset_io(dst, val, count)
#define mmiowb() zpci_barrier()
#define __raw_readb zpci_read_u8
#define __raw_readw zpci_read_u16
#define __raw_readl zpci_read_u32
......
......@@ -12,74 +12,36 @@
#include <asm/types.h>
#include <asm/cio.h>
#include <asm/setup.h>
#include <uapi/asm/ipl.h>
#define NSS_NAME_SIZE 8
#define IPL_PARM_BLK_FCP_LEN (sizeof(struct ipl_list_hdr) + \
sizeof(struct ipl_block_fcp))
#define IPL_PARM_BLK0_FCP_LEN (sizeof(struct ipl_block_fcp) + 16)
struct ipl_parameter_block {
struct ipl_pl_hdr hdr;
union {
struct ipl_pb_hdr pb0_hdr;
struct ipl_pb0_common common;
struct ipl_pb0_fcp fcp;
struct ipl_pb0_ccw ccw;
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
};
} __packed __aligned(PAGE_SIZE);
#define IPL_PARM_BLK_CCW_LEN (sizeof(struct ipl_list_hdr) + \
sizeof(struct ipl_block_ccw))
#define NSS_NAME_SIZE 8
#define IPL_PARM_BLK0_CCW_LEN (sizeof(struct ipl_block_ccw) + 16)
#define IPL_BP_FCP_LEN (sizeof(struct ipl_pl_hdr) + \
sizeof(struct ipl_pb0_fcp))
#define IPL_BP0_FCP_LEN (sizeof(struct ipl_pb0_fcp))
#define IPL_BP_CCW_LEN (sizeof(struct ipl_pl_hdr) + \
sizeof(struct ipl_pb0_ccw))
#define IPL_BP0_CCW_LEN (sizeof(struct ipl_pb0_ccw))
#define IPL_MAX_SUPPORTED_VERSION (0)
struct ipl_list_hdr {
u32 len;
u8 reserved1[3];
u8 version;
u32 blk0_len;
u8 pbt;
u8 flags;
u16 reserved2;
u8 loadparm[8];
} __attribute__((packed));
struct ipl_block_fcp {
u8 reserved1[305-1];
u8 opt;
u8 reserved2[3];
u16 reserved3;
u16 devno;
u8 reserved4[4];
u64 wwpn;
u64 lun;
u32 bootprog;
u8 reserved5[12];
u64 br_lba;
u32 scp_data_len;
u8 reserved6[260];
u8 scp_data[];
} __attribute__((packed));
#define DIAG308_VMPARM_SIZE 64
#define DIAG308_SCPDATA_SIZE (PAGE_SIZE - (sizeof(struct ipl_list_hdr) + \
offsetof(struct ipl_block_fcp, scp_data)))
struct ipl_block_ccw {
u8 reserved1[84];
u16 reserved2 : 13;
u8 ssid : 3;
u16 devno;
u8 vm_flags;
u8 reserved3[3];
u32 vm_parm_len;
u8 nss_name[8];
u8 vm_parm[DIAG308_VMPARM_SIZE];
u8 reserved4[8];
} __attribute__((packed));
#define IPL_RB_CERT_UNKNOWN ((unsigned short)-1)
struct ipl_parameter_block {
struct ipl_list_hdr hdr;
union {
struct ipl_block_fcp fcp;
struct ipl_block_ccw ccw;
char raw[PAGE_SIZE - sizeof(struct ipl_list_hdr)];
} ipl_info;
} __packed __aligned(PAGE_SIZE);
#define DIAG308_VMPARM_SIZE (64)
#define DIAG308_SCPDATA_OFFSET offsetof(struct ipl_parameter_block, \
fcp.scp_data)
#define DIAG308_SCPDATA_SIZE (PAGE_SIZE - DIAG308_SCPDATA_OFFSET)
struct save_area;
struct save_area * __init save_area_alloc(bool is_boot_cpu);
......@@ -88,7 +50,6 @@ void __init save_area_add_regs(struct save_area *, void *regs);
void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs);
extern void s390_reset_system(void);
extern void ipl_store_parameters(void);
extern size_t ipl_block_get_ascii_vmparm(char *dest, size_t size,
const struct ipl_parameter_block *ipb);
......@@ -122,6 +83,33 @@ extern struct ipl_info ipl_info;
extern void setup_ipl(void);
extern void set_os_info_reipl_block(void);
struct ipl_report {
struct ipl_parameter_block *ipib;
struct list_head components;
struct list_head certificates;
size_t size;
};
struct ipl_report_component {
struct list_head list;
struct ipl_rb_component_entry entry;
};
struct ipl_report_certificate {
struct list_head list;
struct ipl_rb_certificate_entry entry;
void *key;
};
struct kexec_buf;
struct ipl_report *ipl_report_init(struct ipl_parameter_block *ipib);
void *ipl_report_finish(struct ipl_report *report);
int ipl_report_free(struct ipl_report *report);
int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf,
unsigned char flags, unsigned short cert);
int ipl_report_add_certificate(struct ipl_report *report, void *key,
unsigned long addr, unsigned long len);
/*
* DIAG 308 support
*/
......@@ -133,32 +121,12 @@ enum diag308_subcode {
DIAG308_STORE = 6,
};
enum diag308_ipl_type {
DIAG308_IPL_TYPE_FCP = 0,
DIAG308_IPL_TYPE_CCW = 2,
};
enum diag308_opt {
DIAG308_IPL_OPT_IPL = 0x10,
DIAG308_IPL_OPT_DUMP = 0x20,
};
enum diag308_flags {
DIAG308_FLAGS_LP_VALID = 0x80,
};
enum diag308_vm_flags {
DIAG308_VM_FLAGS_NSS_VALID = 0x80,
DIAG308_VM_FLAGS_VP_VALID = 0x40,
};
enum diag308_rc {
DIAG308_RC_OK = 0x0001,
DIAG308_RC_NOCONFIG = 0x0102,
};
extern int diag308(unsigned long subcode, void *addr);
extern void diag308_reset(void);
extern void store_status(void (*fn)(void *), void *data);
extern void lgr_info_log(void);
......
......@@ -47,7 +47,6 @@ enum interruption_class {
IRQEXT_CMC,
IRQEXT_FTP,
IRQIO_CIO,
IRQIO_QAI,
IRQIO_DAS,
IRQIO_C15,
IRQIO_C70,
......@@ -55,12 +54,14 @@ enum interruption_class {
IRQIO_VMR,
IRQIO_LCS,
IRQIO_CTC,
IRQIO_APB,
IRQIO_ADM,
IRQIO_CSC,
IRQIO_PCI,
IRQIO_MSI,
IRQIO_VIR,
IRQIO_QAI,
IRQIO_APB,
IRQIO_PCF,
IRQIO_PCD,
IRQIO_MSI,
IRQIO_VAI,
IRQIO_GAL,
NMI_NMI,
......
......@@ -11,6 +11,7 @@
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/setup.h>
/*
* KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
* I.e. Maximum page that is mapped directly into kernel memory,
......@@ -42,6 +43,9 @@
/* The native architecture */
#define KEXEC_ARCH KEXEC_ARCH_S390
/* Allow kexec_file to load a segment to 0 */
#define KEXEC_BUF_MEM_UNKNOWN -1
/* Provide a dummy definition to avoid build failures. */
static inline void crash_setup_regs(struct pt_regs *newregs,
struct pt_regs *oldregs) { }
......@@ -51,20 +55,24 @@ struct s390_load_data {
/* Pointer to the kernel buffer. Used to register cmdline etc.. */
void *kernel_buf;
/* Load address of the kernel_buf. */
unsigned long kernel_mem;
/* Parmarea in the kernel buffer. */
struct parmarea *parm;
/* Total size of loaded segments in memory. Used as an offset. */
size_t memsz;
/* Load address of initrd. Used to register INITRD_START in kernel. */
unsigned long initrd_load_addr;
struct ipl_report *report;
};
int kexec_file_add_purgatory(struct kimage *image,
struct s390_load_data *data);
int kexec_file_add_initrd(struct kimage *image,
struct s390_load_data *data,
char *initrd, unsigned long initrd_len);
int *kexec_file_update_kernel(struct kimage *iamge,
struct s390_load_data *data);
int s390_verify_sig(const char *kernel, unsigned long kernel_len);
void *kexec_file_add_components(struct kimage *image,
int (*add_kernel)(struct kimage *image,
struct s390_load_data *data));
int arch_kexec_do_relocs(int r_type, void *loc, unsigned long val,
unsigned long addr);
extern const struct kexec_file_ops s390_kexec_image_ops;
extern const struct kexec_file_ops s390_kexec_elf_ops;
......
......@@ -28,5 +28,12 @@
.long (_target) - . ; \
.previous
#define EX_TABLE_DMA(_fault, _target) \
.section .dma.ex_table, "a" ; \
.align 4 ; \
.long (_fault) - . ; \
.long (_target) - . ; \
.previous
#endif /* __ASSEMBLY__ */
#endif
......@@ -129,7 +129,7 @@ struct lowcore {
/* SMP info area */
__u32 cpu_nr; /* 0x03a0 */
__u32 softirq_pending; /* 0x03a4 */
__u32 preempt_count; /* 0x03a8 */
__s32 preempt_count; /* 0x03a8 */
__u32 spinlock_lockval; /* 0x03ac */
__u32 spinlock_index; /* 0x03b0 */
__u32 fpu_flags; /* 0x03b4 */
......
......@@ -32,23 +32,23 @@ _LC_BR_R1 = __LC_BR_R1
.endm
.macro __THUNK_PROLOG_BR r1,r2
__THUNK_PROLOG_NAME __s390x_indirect_jump_r\r2\()use_r\r1
__THUNK_PROLOG_NAME __s390_indirect_jump_r\r2\()use_r\r1
.endm
.macro __THUNK_PROLOG_BC d0,r1,r2
__THUNK_PROLOG_NAME __s390x_indirect_branch_\d0\()_\r2\()use_\r1
__THUNK_PROLOG_NAME __s390_indirect_branch_\d0\()_\r2\()use_\r1
.endm
.macro __THUNK_BR r1,r2
jg __s390x_indirect_jump_r\r2\()use_r\r1
jg __s390_indirect_jump_r\r2\()use_r\r1
.endm
.macro __THUNK_BC d0,r1,r2
jg __s390x_indirect_branch_\d0\()_\r2\()use_\r1
jg __s390_indirect_branch_\d0\()_\r2\()use_\r1
.endm
.macro __THUNK_BRASL r1,r2,r3
brasl \r1,__s390x_indirect_jump_r\r3\()use_r\r2
brasl \r1,__s390_indirect_jump_r\r3\()use_r\r2
.endm
.macro __DECODE_RR expand,reg,ruse
......
......@@ -26,6 +26,9 @@ int pci_proc_domain(struct pci_bus *);
#define ZPCI_BUS_NR 0 /* default bus number */
#define ZPCI_DEVFN 0 /* default device number */
#define ZPCI_NR_DMA_SPACES 1
#define ZPCI_NR_DEVICES CONFIG_PCI_NR_FUNCTIONS
/* PCI Function Controls */
#define ZPCI_FC_FN_ENABLED 0x80
#define ZPCI_FC_ERROR 0x40
......@@ -83,6 +86,8 @@ enum zpci_state {
struct zpci_bar_struct {
struct resource *res; /* bus resource */
void __iomem *mio_wb;
void __iomem *mio_wt;
u32 val; /* bar start & 3 flag bits */
u16 map_idx; /* index into bar mapping array */
u8 size; /* order 2 exponent */
......@@ -112,6 +117,8 @@ struct zpci_dev {
/* IRQ stuff */
u64 msi_addr; /* MSI address */
unsigned int max_msi; /* maximum number of MSI's */
unsigned int msi_first_bit;
unsigned int msi_nr_irqs;
struct airq_iv *aibv; /* adapter interrupt bit vector */
unsigned long aisb; /* number of the summary bit */
......@@ -130,6 +137,7 @@ struct zpci_dev {
struct iommu_device iommu_dev; /* IOMMU core handle */
char res_name[16];
bool mio_capable;
struct zpci_bar_struct bars[PCI_BAR_COUNT];
u64 start_dma; /* Start of available DMA addresses */
......@@ -158,6 +166,7 @@ static inline bool zdev_enabled(struct zpci_dev *zdev)
}
extern const struct attribute_group *zpci_attr_groups[];
extern unsigned int s390_pci_force_floating __initdata;
/* -----------------------------------------------------------------------------
Prototypes
......@@ -219,6 +228,9 @@ struct zpci_dev *get_zdev_by_fid(u32);
int zpci_dma_init(void);
void zpci_dma_exit(void);
int __init zpci_irq_init(void);
void __init zpci_irq_exit(void);
/* FMB */
int zpci_fmb_enable_device(struct zpci_dev *);
int zpci_fmb_disable_device(struct zpci_dev *);
......
......@@ -43,6 +43,8 @@ struct clp_fh_list_entry {
#define CLP_SET_ENABLE_PCI_FN 0 /* Yes, 0 enables it */
#define CLP_SET_DISABLE_PCI_FN 1 /* Yes, 1 disables it */
#define CLP_SET_ENABLE_MIO 2
#define CLP_SET_DISABLE_MIO 3
#define CLP_UTIL_STR_LEN 64
#define CLP_PFIP_NR_SEGMENTS 4
......@@ -80,7 +82,8 @@ struct clp_req_query_pci {
struct clp_rsp_query_pci {
struct clp_rsp_hdr hdr;
u16 vfn; /* virtual fn number */
u16 : 7;
u16 : 6;
u16 mio_addr_avail : 1;
u16 util_str_avail : 1; /* utility string available? */
u16 pfgid : 8; /* pci function group id */
u32 fid; /* pci function id */
......@@ -96,6 +99,15 @@ struct clp_rsp_query_pci {
u32 reserved[11];
u32 uid; /* user defined id */
u8 util_str[CLP_UTIL_STR_LEN]; /* utility string */
u32 reserved2[16];
u32 mio_valid : 6;
u32 : 26;
u32 : 32;
struct {
u64 wb;
u64 wt;
} addr[PCI_BAR_COUNT];
u32 reserved3[6];
} __packed;
/* Query PCI function group request */
......@@ -118,7 +130,11 @@ struct clp_rsp_query_pci_grp {
u8 refresh : 1; /* TLB refresh mode */
u16 reserved2;
u16 mui;
u64 reserved3;
u16 : 16;
u16 maxfaal;
u16 : 4;
u16 dnoi : 12;
u16 maxcpu;
u64 dasm; /* dma address space mask */
u64 msia; /* MSI address */
u64 reserved4;
......
......@@ -2,6 +2,8 @@
#ifndef _ASM_S390_PCI_INSN_H
#define _ASM_S390_PCI_INSN_H
#include <linux/jump_label.h>
/* Load/Store status codes */
#define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4
#define ZPCI_PCI_ST_FUNC_IN_ERR 8
......@@ -38,6 +40,8 @@
#define ZPCI_MOD_FC_RESET_ERROR 7
#define ZPCI_MOD_FC_RESET_BLOCK 9
#define ZPCI_MOD_FC_SET_MEASURE 10
#define ZPCI_MOD_FC_REG_INT_D 16
#define ZPCI_MOD_FC_DEREG_INT_D 17
/* FIB function controls */
#define ZPCI_FIB_FC_ENABLED 0x80
......@@ -51,16 +55,7 @@
#define ZPCI_FIB_FC_LS_BLOCKED 0x20
#define ZPCI_FIB_FC_DMAAS_REG 0x10
/* Function Information Block */
struct zpci_fib {
u32 fmt : 8; /* format */
u32 : 24;
u32 : 32;
u8 fc; /* function controls */
u64 : 56;
u64 pba; /* PCI base address */
u64 pal; /* PCI address limit */
u64 iota; /* I/O Translation Anchor */
struct zpci_fib_fmt0 {
u32 : 1;
u32 isc : 3; /* Interrupt subclass */
u32 noi : 12; /* Number of interrupts */
......@@ -72,16 +67,90 @@ struct zpci_fib {
u32 : 32;
u64 aibv; /* Adapter int bit vector address */
u64 aisb; /* Adapter int summary bit address */
};
struct zpci_fib_fmt1 {
u32 : 4;
u32 noi : 12;
u32 : 16;
u32 dibvo : 16;
u32 : 16;
u64 : 64;
u64 : 64;
};
/* Function Information Block */
struct zpci_fib {
u32 fmt : 8; /* format */
u32 : 24;
u32 : 32;
u8 fc; /* function controls */
u64 : 56;
u64 pba; /* PCI base address */
u64 pal; /* PCI address limit */
u64 iota; /* I/O Translation Anchor */
union {
struct zpci_fib_fmt0 fmt0;
struct zpci_fib_fmt1 fmt1;
};
u64 fmb_addr; /* Function measurement block address and key */
u32 : 32;
u32 gd;
} __packed __aligned(8);
/* directed interruption information block */
struct zpci_diib {
u32 : 1;
u32 isc : 3;
u32 : 28;
u16 : 16;
u16 nr_cpus;
u64 disb_addr;
u64 : 64;
u64 : 64;
} __packed __aligned(8);
/* cpu directed interruption information block */
struct zpci_cdiib {
u64 : 64;
u64 dibv_addr;
u64 : 64;
u64 : 64;
u64 : 64;
} __packed __aligned(8);
union zpci_sic_iib {
struct zpci_diib diib;
struct zpci_cdiib cdiib;
};
DECLARE_STATIC_KEY_FALSE(have_mio);
u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status);
int zpci_refresh_trans(u64 fn, u64 addr, u64 range);
int zpci_load(u64 *data, u64 req, u64 offset);
int zpci_store(u64 data, u64 req, u64 offset);
int zpci_store_block(const u64 *data, u64 req, u64 offset);
int zpci_set_irq_ctrl(u16 ctl, char *unused, u8 isc);
int __zpci_load(u64 *data, u64 req, u64 offset);
int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len);
int __zpci_store(u64 data, u64 req, u64 offset);
int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len);
int __zpci_store_block(const u64 *data, u64 req, u64 offset);
void zpci_barrier(void);
int __zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib);
static inline int zpci_set_irq_ctrl(u16 ctl, u8 isc)
{
union zpci_sic_iib iib = {{0}};
return __zpci_set_irq_ctrl(ctl, isc, &iib);
}
#ifdef CONFIG_PCI
static inline void enable_mio_ctl(void)
{
if (static_branch_likely(&have_mio))
__ctl_set_bit(2, 5);
}
#else /* CONFIG_PCI */
static inline void enable_mio_ctl(void) {}
#endif /* CONFIG_PCI */
#endif
......@@ -37,12 +37,10 @@ extern struct zpci_iomap_entry *zpci_iomap_start;
#define zpci_read(LENGTH, RETTYPE) \
static inline RETTYPE zpci_read_##RETTYPE(const volatile void __iomem *addr) \
{ \
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; \
u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, LENGTH); \
u64 data; \
int rc; \
\
rc = zpci_load(&data, req, ZPCI_OFFSET(addr)); \
rc = zpci_load(&data, addr, LENGTH); \
if (rc) \
data = -1ULL; \
return (RETTYPE) data; \
......@@ -52,11 +50,9 @@ static inline RETTYPE zpci_read_##RETTYPE(const volatile void __iomem *addr) \
static inline void zpci_write_##VALTYPE(VALTYPE val, \
const volatile void __iomem *addr) \
{ \
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; \
u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, LENGTH); \
u64 data = (VALTYPE) val; \
\
zpci_store(data, req, ZPCI_OFFSET(addr)); \
zpci_store(addr, data, LENGTH); \
}
zpci_read(8, u64)
......@@ -68,36 +64,38 @@ zpci_write(4, u32)
zpci_write(2, u16)
zpci_write(1, u8)
static inline int zpci_write_single(u64 req, const u64 *data, u64 offset, u8 len)
static inline int zpci_write_single(volatile void __iomem *dst, const void *src,
unsigned long len)
{
u64 val;
switch (len) {
case 1:
val = (u64) *((u8 *) data);
val = (u64) *((u8 *) src);
break;
case 2:
val = (u64) *((u16 *) data);
val = (u64) *((u16 *) src);
break;
case 4:
val = (u64) *((u32 *) data);
val = (u64) *((u32 *) src);
break;
case 8:
val = (u64) *((u64 *) data);
val = (u64) *((u64 *) src);
break;
default:
val = 0; /* let FW report error */
break;
}
return zpci_store(val, req, offset);
return zpci_store(dst, val, len);
}
static inline int zpci_read_single(u64 req, u64 *dst, u64 offset, u8 len)
static inline int zpci_read_single(void *dst, const volatile void __iomem *src,
unsigned long len)
{
u64 data;
int cc;
cc = zpci_load(&data, req, offset);
cc = zpci_load(&data, src, len);
if (cc)
goto out;
......@@ -119,10 +117,8 @@ static inline int zpci_read_single(u64 req, u64 *dst, u64 offset, u8 len)
return cc;
}
static inline int zpci_write_block(u64 req, const u64 *data, u64 offset)
{
return zpci_store_block(data, req, offset);
}
int zpci_write_block(volatile void __iomem *dst, const void *src,
unsigned long len);
static inline u8 zpci_get_max_write_size(u64 src, u64 dst, int len, int max)
{
......@@ -140,18 +136,15 @@ static inline int zpci_memcpy_fromio(void *dst,
const volatile void __iomem *src,
unsigned long n)
{
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(src)];
u64 req, offset = ZPCI_OFFSET(src);
int size, rc = 0;
while (n > 0) {
size = zpci_get_max_write_size((u64 __force) src,
(u64) dst, n, 8);
req = ZPCI_CREATE_REQ(entry->fh, entry->bar, size);
rc = zpci_read_single(req, dst, offset, size);
rc = zpci_read_single(dst, src, size);
if (rc)
break;
offset += size;
src += size;
dst += size;
n -= size;
}
......@@ -161,8 +154,6 @@ static inline int zpci_memcpy_fromio(void *dst,
static inline int zpci_memcpy_toio(volatile void __iomem *dst,
const void *src, unsigned long n)
{
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)];
u64 req, offset = ZPCI_OFFSET(dst);
int size, rc = 0;
if (!src)
......@@ -171,16 +162,14 @@ static inline int zpci_memcpy_toio(volatile void __iomem *dst,
while (n > 0) {
size = zpci_get_max_write_size((u64 __force) dst,
(u64) src, n, 128);
req = ZPCI_CREATE_REQ(entry->fh, entry->bar, size);
if (size > 8) /* main path */
rc = zpci_write_block(req, src, offset);
rc = zpci_write_block(dst, src, size);
else
rc = zpci_write_single(req, src, offset, size);
rc = zpci_write_single(dst, src, size);
if (rc)
break;
offset += size;
src += size;
dst += size;
n -= size;
}
return rc;
......
......@@ -238,7 +238,7 @@ static inline int is_module_addr(void *addr)
#define _REGION_ENTRY_NOEXEC 0x100 /* region no-execute bit */
#define _REGION_ENTRY_OFFSET 0xc0 /* region table offset */
#define _REGION_ENTRY_INVALID 0x20 /* invalid region table entry */
#define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
#define _REGION_ENTRY_TYPE_MASK 0x0c /* region table type mask */
#define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
#define _REGION_ENTRY_TYPE_R2 0x08 /* region second table type */
#define _REGION_ENTRY_TYPE_R3 0x04 /* region third table type */
......@@ -277,6 +277,7 @@ static inline int is_module_addr(void *addr)
#define _SEGMENT_ENTRY_PROTECT 0x200 /* segment protection bit */
#define _SEGMENT_ENTRY_NOEXEC 0x100 /* segment no-execute bit */
#define _SEGMENT_ENTRY_INVALID 0x20 /* invalid segment table entry */
#define _SEGMENT_ENTRY_TYPE_MASK 0x0c /* segment table type mask */
#define _SEGMENT_ENTRY (0)
#define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INVALID)
......@@ -614,15 +615,9 @@ static inline int pgd_none(pgd_t pgd)
static inline int pgd_bad(pgd_t pgd)
{
/*
* With dynamic page table levels the pgd can be a region table
* entry or a segment table entry. Check for the bit that are
* invalid for either table entry.
*/
unsigned long mask =
~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INVALID &
~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
return (pgd_val(pgd) & mask) != 0;
if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R1)
return 0;
return (pgd_val(pgd) & ~_REGION_ENTRY_BITS) != 0;
}
static inline unsigned long pgd_pfn(pgd_t pgd)
......@@ -703,6 +698,8 @@ static inline int pmd_large(pmd_t pmd)
static inline int pmd_bad(pmd_t pmd)
{
if ((pmd_val(pmd) & _SEGMENT_ENTRY_TYPE_MASK) > 0)
return 1;
if (pmd_large(pmd))
return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS_LARGE) != 0;
return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS) != 0;
......@@ -710,8 +707,12 @@ static inline int pmd_bad(pmd_t pmd)
static inline int pud_bad(pud_t pud)
{
if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
return pmd_bad(__pmd(pud_val(pud)));
unsigned long type = pud_val(pud) & _REGION_ENTRY_TYPE_MASK;
if (type > _REGION_ENTRY_TYPE_R3)
return 1;
if (type < _REGION_ENTRY_TYPE_R3)
return 0;
if (pud_large(pud))
return (pud_val(pud) & ~_REGION_ENTRY_BITS_LARGE) != 0;
return (pud_val(pud) & ~_REGION_ENTRY_BITS) != 0;
......@@ -719,8 +720,12 @@ static inline int pud_bad(pud_t pud)
static inline int p4d_bad(p4d_t p4d)
{
if ((p4d_val(p4d) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
return pud_bad(__pud(p4d_val(p4d)));
unsigned long type = p4d_val(p4d) & _REGION_ENTRY_TYPE_MASK;
if (type > _REGION_ENTRY_TYPE_R2)
return 1;
if (type < _REGION_ENTRY_TYPE_R2)
return 0;
return (p4d_val(p4d) & ~_REGION_ENTRY_BITS) != 0;
}
......@@ -1204,41 +1209,78 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
#define pmd_index(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
#define pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE-1))
#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
#define pgd_offset_raw(pgd, addr) ((pgd) + pgd_index(addr))
#define pmd_deref(pmd) (pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN)
#define pud_deref(pud) (pud_val(pud) & _REGION_ENTRY_ORIGIN)
#define p4d_deref(pud) (p4d_val(pud) & _REGION_ENTRY_ORIGIN)
#define pgd_deref(pgd) (pgd_val(pgd) & _REGION_ENTRY_ORIGIN)
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
/*
* The pgd_offset function *always* adds the index for the top-level
* region/segment table. This is done to get a sequence like the
* following to work:
* pgdp = pgd_offset(current->mm, addr);
* pgd = READ_ONCE(*pgdp);
* p4dp = p4d_offset(&pgd, addr);
* ...
* The subsequent p4d_offset, pud_offset and pmd_offset functions
* only add an index if they dereferenced the pointer.
*/
static inline pgd_t *pgd_offset_raw(pgd_t *pgd, unsigned long address)
{
p4d_t *p4d = (p4d_t *) pgd;
unsigned long rste;
unsigned int shift;
if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R1)
p4d = (p4d_t *) pgd_deref(*pgd);
return p4d + p4d_index(address);
/* Get the first entry of the top level table */
rste = pgd_val(*pgd);
/* Pick up the shift from the table type of the first entry */
shift = ((rste & _REGION_ENTRY_TYPE_MASK) >> 2) * 11 + 20;
return pgd + ((address >> shift) & (PTRS_PER_PGD - 1));
}
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
#define pgd_offset(mm, address) pgd_offset_raw(READ_ONCE((mm)->pgd), address)
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
pud_t *pud = (pud_t *) p4d;
if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) >= _REGION_ENTRY_TYPE_R1)
return (p4d_t *) pgd_deref(*pgd) + p4d_index(address);
return (p4d_t *) pgd;
}
if ((p4d_val(*p4d) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
pud = (pud_t *) p4d_deref(*p4d);
return pud + pud_index(address);
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
if ((p4d_val(*p4d) & _REGION_ENTRY_TYPE_MASK) >= _REGION_ENTRY_TYPE_R2)
return (pud_t *) p4d_deref(*p4d) + pud_index(address);
return (pud_t *) p4d;
}
static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
{
pmd_t *pmd = (pmd_t *) pud;
if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) >= _REGION_ENTRY_TYPE_R3)
return (pmd_t *) pud_deref(*pud) + pmd_index(address);
return (pmd_t *) pud;
}
if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
pmd = (pmd_t *) pud_deref(*pud);
return pmd + pmd_index(address);
static inline pte_t *pte_offset(pmd_t *pmd, unsigned long address)
{
return (pte_t *) pmd_deref(*pmd) + pte_index(address);
}
#define pte_offset_kernel(pmd, address) pte_offset(pmd, address)
#define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address)
#define pte_unmap(pte) do { } while (0)
static inline bool gup_fast_permitted(unsigned long start, int nr_pages)
{
unsigned long len, end;
len = (unsigned long) nr_pages << PAGE_SHIFT;
end = start + len;
if (end < start)
return false;
return end <= current->mm->context.asce_limit;
}
#define gup_fast_permitted gup_fast_permitted
#define pfn_pte(pfn,pgprot) mk_pte_phys(__pa((pfn) << PAGE_SHIFT),(pgprot))
#define pte_pfn(x) (pte_val(x) >> PAGE_SHIFT)
......@@ -1249,12 +1291,6 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
#define p4d_page(p4d) pfn_to_page(p4d_pfn(p4d))
#define pgd_page(pgd) pfn_to_page(pgd_pfn(pgd))
/* Find an entry in the lowest level page table.. */
#define pte_offset(pmd, addr) ((pte_t *) pmd_deref(*(pmd)) + pte_index(addr))
#define pte_offset_kernel(pmd, address) pte_offset(pmd,address)
#define pte_offset_map(pmd, address) pte_offset_kernel(pmd, address)
#define pte_unmap(pte) do { } while (0)
static inline pmd_t pmd_wrprotect(pmd_t pmd)
{
pmd_val(pmd) &= ~_SEGMENT_ENTRY_WRITE;
......
......@@ -156,25 +156,6 @@ struct thread_struct {
typedef struct thread_struct thread_struct;
/*
* Stack layout of a C stack frame.
*/
#ifndef __PACK_STACK
struct stack_frame {
unsigned long back_chain;
unsigned long empty1[5];
unsigned long gprs[10];
unsigned int empty2[8];
};
#else
struct stack_frame {
unsigned long empty1[5];
unsigned int empty2[8];
unsigned long gprs[10];
unsigned long back_chain;
};
#endif
#define ARCH_MIN_TASKALIGN 8
#define INIT_THREAD { \
......@@ -206,11 +187,7 @@ struct mm_struct;
struct seq_file;
struct pt_regs;
typedef int (*dump_trace_func_t)(void *data, unsigned long address, int reliable);
void dump_trace(dump_trace_func_t func, void *data,
struct task_struct *task, unsigned long sp);
void show_registers(struct pt_regs *regs);
void show_cacheinfo(struct seq_file *m);
/* Free all resources held by a thread. */
......@@ -244,55 +221,6 @@ static __no_kasan_or_inline unsigned short stap(void)
return cpu_address;
}
#define CALL_ARGS_0() \
register unsigned long r2 asm("2")
#define CALL_ARGS_1(arg1) \
register unsigned long r2 asm("2") = (unsigned long)(arg1)
#define CALL_ARGS_2(arg1, arg2) \
CALL_ARGS_1(arg1); \
register unsigned long r3 asm("3") = (unsigned long)(arg2)
#define CALL_ARGS_3(arg1, arg2, arg3) \
CALL_ARGS_2(arg1, arg2); \
register unsigned long r4 asm("4") = (unsigned long)(arg3)
#define CALL_ARGS_4(arg1, arg2, arg3, arg4) \
CALL_ARGS_3(arg1, arg2, arg3); \
register unsigned long r4 asm("5") = (unsigned long)(arg4)
#define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \
CALL_ARGS_4(arg1, arg2, arg3, arg4); \
register unsigned long r4 asm("6") = (unsigned long)(arg5)
#define CALL_FMT_0
#define CALL_FMT_1 CALL_FMT_0, "0" (r2)
#define CALL_FMT_2 CALL_FMT_1, "d" (r3)
#define CALL_FMT_3 CALL_FMT_2, "d" (r4)
#define CALL_FMT_4 CALL_FMT_3, "d" (r5)
#define CALL_FMT_5 CALL_FMT_4, "d" (r6)
#define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory"
#define CALL_CLOBBER_4 CALL_CLOBBER_5
#define CALL_CLOBBER_3 CALL_CLOBBER_4, "5"
#define CALL_CLOBBER_2 CALL_CLOBBER_3, "4"
#define CALL_CLOBBER_1 CALL_CLOBBER_2, "3"
#define CALL_CLOBBER_0 CALL_CLOBBER_1
#define CALL_ON_STACK(fn, stack, nr, args...) \
({ \
CALL_ARGS_##nr(args); \
unsigned long prev; \
\
asm volatile( \
" la %[_prev],0(15)\n" \
" la 15,0(%[_stack])\n" \
" stg %[_prev],%[_bc](15)\n" \
" brasl 14,%[_fn]\n" \
" la 15,0(%[_prev])\n" \
: "+&d" (r2), [_prev] "=&a" (prev) \
: [_stack] "a" (stack), \
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
[_fn] "X" (fn) CALL_FMT_##nr : CALL_CLOBBER_##nr); \
r2; \
})
/*
* Give up the time slice of the virtual PU.
*/
......@@ -339,10 +267,10 @@ static __no_kasan_or_inline void __load_psw_mask(unsigned long mask)
asm volatile(
" larl %0,1f\n"
" stg %0,%O1+8(%R1)\n"
" lpswe %1\n"
" stg %0,%1\n"
" lpswe %2\n"
"1:"
: "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
: "=&d" (addr), "=Q" (psw.addr) : "Q" (psw) : "memory", "cc");
}
/*
......@@ -387,12 +315,12 @@ void enabled_wait(void);
/*
* Function to drop a processor into disabled wait state
*/
static inline void __noreturn disabled_wait(unsigned long code)
static inline void __noreturn disabled_wait(void)
{
psw_t psw;
psw.mask = PSW_MASK_BASE | PSW_MASK_WAIT | PSW_MASK_BA | PSW_MASK_EA;
psw.addr = code;
psw.addr = _THIS_IP_;
__load_psw(psw);
while (1);
}
......
......@@ -79,6 +79,9 @@ struct sclp_info {
unsigned char has_kss : 1;
unsigned char has_gisaf : 1;
unsigned char has_diag318 : 1;
unsigned char has_sipl : 1;
unsigned char has_sipl_g2 : 1;
unsigned char has_dirq : 1;
unsigned int ibc;
unsigned int mtid;
unsigned int mtid_cp;
......
......@@ -2,8 +2,20 @@
#ifndef _S390_SECTIONS_H
#define _S390_SECTIONS_H
#define arch_is_kernel_initmem_freed arch_is_kernel_initmem_freed
#include <asm-generic/sections.h>
extern bool initmem_freed;
static inline int arch_is_kernel_initmem_freed(unsigned long addr)
{
if (!initmem_freed)
return 0;
return addr >= (unsigned long)__init_begin &&
addr < (unsigned long)__init_end;
}
/*
* .boot.data section contains variables "shared" between the decompressor and
* the decompressed kernel. The decompressor will store values in them, and
......@@ -16,4 +28,14 @@
*/
#define __bootdata(var) __section(.boot.data.var) var
/*
* .boot.preserved.data is similar to .boot.data, but it is not part of the
* .init section and thus will be preserved for later use in the decompressed
* kernel.
*/
#define __bootdata_preserved(var) __section(.boot.preserved.data.var) var
extern unsigned long __sdma, __edma;
extern unsigned long __stext_dma, __etext_dma;
#endif
......@@ -12,7 +12,10 @@
#define EP_OFFSET 0x10008
#define EP_STRING "S390EP"
#define PARMAREA 0x10400
#define PARMAREA_END 0x11000
#define EARLY_SCCB_OFFSET 0x11000
#define HEAD_END 0x12000
#define EARLY_SCCB_SIZE PAGE_SIZE
/*
* Machine features detected in early.c
......@@ -65,6 +68,16 @@
#define OLDMEM_SIZE (*(unsigned long *) (OLDMEM_SIZE_OFFSET))
#define COMMAND_LINE ((char *) (COMMAND_LINE_OFFSET))
struct parmarea {
unsigned long ipl_device; /* 0x10400 */
unsigned long initrd_start; /* 0x10408 */
unsigned long initrd_size; /* 0x10410 */
unsigned long oldmem_base; /* 0x10418 */
unsigned long oldmem_size; /* 0x10420 */
char pad1[0x10480 - 0x10428]; /* 0x10428 - 0x10480 */
char command_line[ARCH_COMMAND_LINE_SIZE]; /* 0x10480 */
};
extern int noexec_disabled;
extern int memory_end_set;
extern unsigned long memory_end;
......@@ -134,6 +147,12 @@ extern void (*_machine_restart)(char *command);
extern void (*_machine_halt)(void);
extern void (*_machine_power_off)(void);
extern unsigned long __kaslr_offset;
static inline unsigned long kaslr_offset(void)
{
return __kaslr_offset;
}
#else /* __ASSEMBLY__ */
#define IPL_DEVICE (IPL_DEVICE_OFFSET)
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_STACKTRACE_H
#define _ASM_S390_STACKTRACE_H
#include <linux/uaccess.h>
#include <linux/ptrace.h>
#include <asm/switch_to.h>
enum stack_type {
STACK_TYPE_UNKNOWN,
STACK_TYPE_TASK,
STACK_TYPE_IRQ,
STACK_TYPE_NODAT,
STACK_TYPE_RESTART,
};
struct stack_info {
enum stack_type type;
unsigned long begin, end;
};
const char *stack_type_name(enum stack_type type);
int get_stack_info(unsigned long sp, struct task_struct *task,
struct stack_info *info, unsigned long *visit_mask);
static inline bool on_stack(struct stack_info *info,
unsigned long addr, size_t len)
{
if (info->type == STACK_TYPE_UNKNOWN)
return false;
if (addr + len < addr)
return false;
return addr >= info->begin && addr + len < info->end;
}
static inline unsigned long get_stack_pointer(struct task_struct *task,
struct pt_regs *regs)
{
if (regs)
return (unsigned long) kernel_stack_pointer(regs);
if (task == current)
return current_stack_pointer();
return (unsigned long) task->thread.ksp;
}
/*
* Stack layout of a C stack frame.
*/
#ifndef __PACK_STACK
struct stack_frame {
unsigned long back_chain;
unsigned long empty1[5];
unsigned long gprs[10];
unsigned int empty2[8];
};
#else
struct stack_frame {
unsigned long empty1[5];
unsigned int empty2[8];
unsigned long gprs[10];
unsigned long back_chain;
};
#endif
#define CALL_ARGS_0() \
register unsigned long r2 asm("2")
#define CALL_ARGS_1(arg1) \
register unsigned long r2 asm("2") = (unsigned long)(arg1)
#define CALL_ARGS_2(arg1, arg2) \
CALL_ARGS_1(arg1); \
register unsigned long r3 asm("3") = (unsigned long)(arg2)
#define CALL_ARGS_3(arg1, arg2, arg3) \
CALL_ARGS_2(arg1, arg2); \
register unsigned long r4 asm("4") = (unsigned long)(arg3)
#define CALL_ARGS_4(arg1, arg2, arg3, arg4) \
CALL_ARGS_3(arg1, arg2, arg3); \
register unsigned long r4 asm("5") = (unsigned long)(arg4)
#define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \
CALL_ARGS_4(arg1, arg2, arg3, arg4); \
register unsigned long r4 asm("6") = (unsigned long)(arg5)
#define CALL_FMT_0 "=&d" (r2) :
#define CALL_FMT_1 "+&d" (r2) :
#define CALL_FMT_2 CALL_FMT_1 "d" (r3),
#define CALL_FMT_3 CALL_FMT_2 "d" (r4),
#define CALL_FMT_4 CALL_FMT_3 "d" (r5),
#define CALL_FMT_5 CALL_FMT_4 "d" (r6),
#define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory"
#define CALL_CLOBBER_4 CALL_CLOBBER_5
#define CALL_CLOBBER_3 CALL_CLOBBER_4, "5"
#define CALL_CLOBBER_2 CALL_CLOBBER_3, "4"
#define CALL_CLOBBER_1 CALL_CLOBBER_2, "3"
#define CALL_CLOBBER_0 CALL_CLOBBER_1
#define CALL_ON_STACK(fn, stack, nr, args...) \
({ \
CALL_ARGS_##nr(args); \
unsigned long prev; \
\
asm volatile( \
" la %[_prev],0(15)\n" \
" la 15,0(%[_stack])\n" \
" stg %[_prev],%[_bc](15)\n" \
" brasl 14,%[_fn]\n" \
" la 15,0(%[_prev])\n" \
: [_prev] "=&a" (prev), CALL_FMT_##nr \
[_stack] "a" (stack), \
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \
r2; \
})
#endif /* _ASM_S390_STACKTRACE_H */
......@@ -14,13 +14,8 @@
#include <linux/err.h>
#include <asm/ptrace.h>
/*
* The syscall table always contains 32 bit pointers since we know that the
* address of the function to be called is (way) below 4GB. So the "int"
* type here is what we want [need] for both 32 bit and 64 bit systems.
*/
extern const unsigned int sys_call_table[];
extern const unsigned int sys_call_table_emu[];
extern const unsigned long sys_call_table[];
extern const unsigned long sys_call_table_emu[];
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
......
......@@ -119,8 +119,8 @@
"Type aliasing is used to sanitize syscall arguments");\
asmlinkage long __s390x_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
__attribute__((alias(__stringify(__se_sys##name)))); \
ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \
static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \
long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
__S390_SYS_STUBx(x, name, __VA_ARGS__) \
asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
......
......@@ -55,8 +55,10 @@ raw_copy_from_user(void *to, const void __user *from, unsigned long n);
unsigned long __must_check
raw_copy_to_user(void __user *to, const void *from, unsigned long n);
#ifndef CONFIG_KASAN
#define INLINE_COPY_FROM_USER
#define INLINE_COPY_TO_USER
#endif
#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_UNWIND_H
#define _ASM_S390_UNWIND_H
#include <linux/sched.h>
#include <linux/ftrace.h>
#include <asm/ptrace.h>
#include <asm/stacktrace.h>
/*
* To use the stack unwinder it has to be initialized with unwind_start.
* There four combinations for task and regs:
* 1) task==NULL, regs==NULL: the unwind starts for the task that is currently
* running, sp/ip picked up from the CPU registers
* 2) task==NULL, regs!=NULL: the unwind starts from the sp/ip found in
* the struct pt_regs of an interrupt frame for the current task
* 3) task!=NULL, regs==NULL: the unwind starts for an inactive task with
* the sp picked up from task->thread.ksp and the ip picked up from the
* return address stored by __switch_to
* 4) task!=NULL, regs!=NULL: the sp/ip are picked up from the interrupt
* frame 'regs' of a inactive task
* If 'first_frame' is not zero unwind_start skips unwind frames until it
* reaches the specified stack pointer.
* The end of the unwinding is indicated with unwind_done, this can be true
* right after unwind_start, e.g. with first_frame!=0 that can not be found.
* unwind_next_frame skips to the next frame.
* Once the unwind is completed unwind_error() can be used to check if there
* has been a situation where the unwinder could not correctly understand
* the tasks call chain.
*/
struct unwind_state {
struct stack_info stack_info;
unsigned long stack_mask;
struct task_struct *task;
struct pt_regs *regs;
unsigned long sp, ip;
int graph_idx;
bool reliable;
bool error;
};
void __unwind_start(struct unwind_state *state, struct task_struct *task,
struct pt_regs *regs, unsigned long first_frame);
bool unwind_next_frame(struct unwind_state *state);
unsigned long unwind_get_return_address(struct unwind_state *state);
static inline bool unwind_done(struct unwind_state *state)
{
return state->stack_info.type == STACK_TYPE_UNKNOWN;
}
static inline bool unwind_error(struct unwind_state *state)
{
return state->error;
}
static inline void unwind_start(struct unwind_state *state,
struct task_struct *task,
struct pt_regs *regs,
unsigned long sp)
{
sp = sp ? : get_stack_pointer(task, regs);
__unwind_start(state, task, regs, sp);
}
static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
{
return unwind_done(state) ? NULL : state->regs;
}
#define unwind_for_each_frame(state, task, regs, first_frame) \
for (unwind_start(state, task, regs, first_frame); \
!unwind_done(state); \
unwind_next_frame(state))
static inline void unwind_init(void) {}
static inline void unwind_module_init(struct module *mod, void *orc_ip,
size_t orc_ip_size, void *orc,
size_t orc_size) {}
#ifdef CONFIG_KASAN
/*
* This disables KASAN checking when reading a value from another task's stack,
* since the other task could be running on another CPU and could have poisoned
* the stack in the meantime.
*/
#define READ_ONCE_TASK_STACK(task, x) \
({ \
unsigned long val; \
if (task == current) \
val = READ_ONCE(x); \
else \
val = READ_ONCE_NOCHECK(x); \
val; \
})
#else
#define READ_ONCE_TASK_STACK(task, x) READ_ONCE(x)
#endif
#endif /* _ASM_S390_UNWIND_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Ultravisor Interfaces
*
* Copyright IBM Corp. 2019
*
* Author(s):
* Vasily Gorbik <gor@linux.ibm.com>
* Janosch Frank <frankja@linux.ibm.com>
*/
#ifndef _ASM_S390_UV_H
#define _ASM_S390_UV_H
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bug.h>
#include <asm/page.h>
#define UVC_RC_EXECUTED 0x0001
#define UVC_RC_INV_CMD 0x0002
#define UVC_RC_INV_STATE 0x0003
#define UVC_RC_INV_LEN 0x0005
#define UVC_RC_NO_RESUME 0x0007
#define UVC_CMD_QUI 0x0001
#define UVC_CMD_SET_SHARED_ACCESS 0x1000
#define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001
/* Bits in installed uv calls */
enum uv_cmds_inst {
BIT_UVC_CMD_QUI = 0,
BIT_UVC_CMD_SET_SHARED_ACCESS = 8,
BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9,
};
struct uv_cb_header {
u16 len;
u16 cmd; /* Command Code */
u16 rc; /* Response Code */
u16 rrc; /* Return Reason Code */
} __packed __aligned(8);
struct uv_cb_qui {
struct uv_cb_header header;
u64 reserved08;
u64 inst_calls_list[4];
u64 reserved30[15];
} __packed __aligned(8);
struct uv_cb_share {
struct uv_cb_header header;
u64 reserved08[3];
u64 paddr;
u64 reserved28;
} __packed __aligned(8);
static inline int uv_call(unsigned long r1, unsigned long r2)
{
int cc;
asm volatile(
"0: .insn rrf,0xB9A40000,%[r1],%[r2],0,0\n"
" brc 3,0b\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc)
: [r1] "a" (r1), [r2] "a" (r2)
: "memory", "cc");
return cc;
}
#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
extern int prot_virt_guest;
static inline int is_prot_virt_guest(void)
{
return prot_virt_guest;
}
static inline int share(unsigned long addr, u16 cmd)
{
struct uv_cb_share uvcb = {
.header.cmd = cmd,
.header.len = sizeof(uvcb),
.paddr = addr
};
if (!is_prot_virt_guest())
return -ENOTSUPP;
/*
* Sharing is page wise, if we encounter addresses that are
* not page aligned, we assume something went wrong. If
* malloced structs are passed to this function, we could leak
* data to the hypervisor.
*/
BUG_ON(addr & ~PAGE_MASK);
if (!uv_call(0, (u64)&uvcb))
return 0;
return -EINVAL;
}
/*
* Guest 2 request to the Ultravisor to make a page shared with the
* hypervisor for IO.
*
* @addr: Real or absolute address of the page to be shared
*/
static inline int uv_set_shared(unsigned long addr)
{
return share(addr, UVC_CMD_SET_SHARED_ACCESS);
}
/*
* Guest 2 request to the Ultravisor to make a page unshared.
*
* @addr: Real or absolute address of the page to be unshared
*/
static inline int uv_remove_shared(unsigned long addr)
{
return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
}
void uv_query_info(void);
#else
#define is_prot_virt_guest() 0
static inline int uv_set_shared(unsigned long addr) { return 0; }
static inline int uv_remove_shared(unsigned long addr) { return 0; }
static inline void uv_query_info(void) {}
#endif
#endif /* _ASM_S390_UV_H */
......@@ -18,3 +18,16 @@
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.boot.data*))) \
__boot_data_end = .; \
}
/*
* .boot.preserved.data is similar to .boot.data, but it is not part of the
* .init section and thus will be preserved for later use in the decompressed
* kernel.
*/
#define BOOT_DATA_PRESERVED \
. = ALIGN(PAGE_SIZE); \
.boot.preserved.data : { \
__boot_data_preserved_start = .; \
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.boot.preserved.data*))) \
__boot_data_preserved_end = .; \
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_UAPI_IPL_H
#define _ASM_S390_UAPI_IPL_H
#include <linux/types.h>
/* IPL Parameter List header */
struct ipl_pl_hdr {
__u32 len;
__u8 flags;
__u8 reserved1[2];
__u8 version;
} __packed;
#define IPL_PL_FLAG_IPLPS 0x80
#define IPL_PL_FLAG_SIPL 0x40
#define IPL_PL_FLAG_IPLSR 0x20
/* IPL Parameter Block header */
struct ipl_pb_hdr {
__u32 len;
__u8 pbt;
} __packed;
/* IPL Parameter Block types */
enum ipl_pbt {
IPL_PBT_FCP = 0,
IPL_PBT_SCP_DATA = 1,
IPL_PBT_CCW = 2,
};
/* IPL Parameter Block 0 with common fields */
struct ipl_pb0_common {
__u32 len;
__u8 pbt;
__u8 flags;
__u8 reserved1[2];
__u8 loadparm[8];
__u8 reserved2[84];
} __packed;
#define IPL_PB0_FLAG_LOADPARM 0x80
/* IPL Parameter Block 0 for FCP */
struct ipl_pb0_fcp {
__u32 len;
__u8 pbt;
__u8 reserved1[3];
__u8 loadparm[8];
__u8 reserved2[304];
__u8 opt;
__u8 reserved3[3];
__u8 cssid;
__u8 reserved4[1];
__u16 devno;
__u8 reserved5[4];
__u64 wwpn;
__u64 lun;
__u32 bootprog;
__u8 reserved6[12];
__u64 br_lba;
__u32 scp_data_len;
__u8 reserved7[260];
__u8 scp_data[];
} __packed;
#define IPL_PB0_FCP_OPT_IPL 0x10
#define IPL_PB0_FCP_OPT_DUMP 0x20
/* IPL Parameter Block 0 for CCW */
struct ipl_pb0_ccw {
__u32 len;
__u8 pbt;
__u8 flags;
__u8 reserved1[2];
__u8 loadparm[8];
__u8 reserved2[84];
__u16 reserved3 : 13;
__u8 ssid : 3;
__u16 devno;
__u8 vm_flags;
__u8 reserved4[3];
__u32 vm_parm_len;
__u8 nss_name[8];
__u8 vm_parm[64];
__u8 reserved5[8];
} __packed;
#define IPL_PB0_CCW_VM_FLAG_NSS 0x80
#define IPL_PB0_CCW_VM_FLAG_VP 0x40
/* IPL Parameter Block 1 for additional SCP data */
struct ipl_pb1_scp_data {
__u32 len;
__u8 pbt;
__u8 scp_data[];
} __packed;
/* IPL Report List header */
struct ipl_rl_hdr {
__u32 len;
__u8 flags;
__u8 reserved1[2];
__u8 version;
__u8 reserved2[8];
} __packed;
/* IPL Report Block header */
struct ipl_rb_hdr {
__u32 len;
__u8 rbt;
__u8 reserved1[11];
} __packed;
/* IPL Report Block types */
enum ipl_rbt {
IPL_RBT_CERTIFICATES = 1,
IPL_RBT_COMPONENTS = 2,
};
/* IPL Report Block for the certificate list */
struct ipl_rb_certificate_entry {
__u64 addr;
__u64 len;
} __packed;
struct ipl_rb_certificates {
__u32 len;
__u8 rbt;
__u8 reserved1[11];
struct ipl_rb_certificate_entry entries[];
} __packed;
/* IPL Report Block for the component list */
struct ipl_rb_component_entry {
__u64 addr;
__u64 len;
__u8 flags;
__u8 reserved1[5];
__u16 certificate_index;
__u8 reserved2[8];
};
#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80
#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40
struct ipl_rb_components {
__u32 len;
__u8 rbt;
__u8 reserved1[11];
struct ipl_rb_component_entry entries[];
} __packed;
#endif
......@@ -39,6 +39,7 @@ CFLAGS_smp.o := -Wno-nonnull
#
CFLAGS_stacktrace.o += -fno-optimize-sibling-calls
CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls
#
# Pass UTS_MACHINE for user_regset definition
......@@ -51,7 +52,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o
obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
obj-y += nospec-branch.o ipl_vmparm.o
obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o
extra-y += head64.o vmlinux.lds
......@@ -77,6 +78,8 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o
obj-$(CONFIG_IMA) += ima_arch.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o
......@@ -86,7 +89,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace.o
# vdso
obj-y += vdso64/
obj-$(CONFIG_COMPAT) += vdso32/
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
chkbss := head64.o early_nobss.o
include $(srctree)/arch/s390/scripts/Makefile.chkbss
......@@ -16,6 +16,7 @@
#include <asm/pgtable.h>
#include <asm/gmap.h>
#include <asm/nmi.h>
#include <asm/stacktrace.h>
int main(void)
{
......
......@@ -28,6 +28,7 @@ ENTRY(s390_base_mcck_handler)
1: la %r1,4095
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
lpswe __LC_MCK_OLD_PSW
ENDPROC(s390_base_mcck_handler)
.section .bss
.align 8
......@@ -48,6 +49,7 @@ ENTRY(s390_base_ext_handler)
1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC
ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
lpswe __LC_EXT_OLD_PSW
ENDPROC(s390_base_ext_handler)
.section .bss
.align 8
......@@ -68,6 +70,7 @@ ENTRY(s390_base_pgm_handler)
lmg %r0,%r15,__LC_SAVE_AREA_SYNC
lpswe __LC_PGM_OLD_PSW
1: lpswe disabled_wait_psw-0b(%r13)
ENDPROC(s390_base_pgm_handler)
.align 8
disabled_wait_psw:
......@@ -79,71 +82,3 @@ disabled_wait_psw:
s390_base_pgm_handler_fn:
.quad 0
.previous
#
# Calls diag 308 subcode 1 and continues execution
#
ENTRY(diag308_reset)
larl %r4,.Lctlregs # Save control registers
stctg %c0,%c15,0(%r4)
lg %r2,0(%r4) # Disable lowcore protection
nilh %r2,0xefff
larl %r4,.Lctlreg0
stg %r2,0(%r4)
lctlg %c0,%c0,0(%r4)
larl %r4,.Lfpctl # Floating point control register
stfpc 0(%r4)
larl %r4,.Lprefix # Save prefix register
stpx 0(%r4)
larl %r4,.Lprefix_zero # Set prefix register to 0
spx 0(%r4)
larl %r4,.Lcontinue_psw # Save PSW flags
epsw %r2,%r3
stm %r2,%r3,0(%r4)
larl %r4,.Lrestart_psw # Setup restart PSW at absolute 0
lghi %r3,0
lg %r4,0(%r4) # Save PSW
sturg %r4,%r3 # Use sturg, because of large pages
lghi %r1,1
lghi %r0,0
diag %r0,%r1,0x308
.Lrestart_part2:
lhi %r0,0 # Load r0 with zero
lhi %r1,2 # Use mode 2 = ESAME (dump)
sigp %r1,%r0,SIGP_SET_ARCHITECTURE # Switch to ESAME mode
sam64 # Switch to 64 bit addressing mode
larl %r4,.Lctlregs # Restore control registers
lctlg %c0,%c15,0(%r4)
larl %r4,.Lfpctl # Restore floating point ctl register
lfpc 0(%r4)
larl %r4,.Lprefix # Restore prefix register
spx 0(%r4)
larl %r4,.Lcontinue_psw # Restore PSW flags
lpswe 0(%r4)
.Lcontinue:
BR_EX %r14
.align 16
.Lrestart_psw:
.long 0x00080000,0x80000000 + .Lrestart_part2
.section .data..nosave,"aw",@progbits
.align 8
.Lcontinue_psw:
.quad 0,.Lcontinue
.previous
.section .bss
.align 8
.Lctlreg0:
.quad 0
.Lctlregs:
.rept 16
.quad 0
.endr
.Lfpctl:
.long 0
.Lprefix:
.long 0
.Lprefix_zero:
.long 0
.previous
......@@ -13,6 +13,7 @@
#include <linux/debugfs.h>
#include <asm/diag.h>
#include <asm/trace/diag.h>
#include <asm/sections.h>
struct diag_stat {
unsigned int counter[NR_DIAG_STAT];
......@@ -49,6 +50,9 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = {
[DIAG_STAT_X500] = { .code = 0x500, .name = "Virtio Service" },
};
struct diag_ops __bootdata_preserved(diag_dma_ops);
struct diag210 *__bootdata_preserved(__diag210_tmp_dma);
static int show_diag_stat(struct seq_file *m, void *v)
{
struct diag_stat *stat;
......@@ -139,30 +143,10 @@ EXPORT_SYMBOL(diag_stat_inc_norecursion);
/*
* Diagnose 14: Input spool file manipulation
*/
static inline int __diag14(unsigned long rx, unsigned long ry1,
unsigned long subcode)
{
register unsigned long _ry1 asm("2") = ry1;
register unsigned long _ry2 asm("3") = subcode;
int rc = 0;
asm volatile(
" sam31\n"
" diag %2,2,0x14\n"
" sam64\n"
" ipm %0\n"
" srl %0,28\n"
: "=d" (rc), "+d" (_ry2)
: "d" (rx), "d" (_ry1)
: "cc");
return rc;
}
int diag14(unsigned long rx, unsigned long ry1, unsigned long subcode)
{
diag_stat_inc(DIAG_STAT_X014);
return __diag14(rx, ry1, subcode);
return diag_dma_ops.diag14(rx, ry1, subcode);
}
EXPORT_SYMBOL(diag14);
......@@ -195,30 +179,17 @@ EXPORT_SYMBOL(diag204);
*/
int diag210(struct diag210 *addr)
{
/*
* diag 210 needs its data below the 2GB border, so we
* use a static data area to be sure
*/
static struct diag210 diag210_tmp;
static DEFINE_SPINLOCK(diag210_lock);
unsigned long flags;
int ccode;
spin_lock_irqsave(&diag210_lock, flags);
diag210_tmp = *addr;
*__diag210_tmp_dma = *addr;
diag_stat_inc(DIAG_STAT_X210);
asm volatile(
" lhi %0,-1\n"
" sam31\n"
" diag %1,0,0x210\n"
"0: ipm %0\n"
" srl %0,28\n"
"1: sam64\n"
EX_TABLE(0b, 1b)
: "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory");
*addr = diag210_tmp;
ccode = diag_dma_ops.diag210(__diag210_tmp_dma);
*addr = *__diag210_tmp_dma;
spin_unlock_irqrestore(&diag210_lock, flags);
return ccode;
......@@ -243,27 +214,9 @@ EXPORT_SYMBOL(diag224);
/*
* Diagnose 26C: Access Certain System Information
*/
static inline int __diag26c(void *req, void *resp, enum diag26c_sc subcode)
{
register unsigned long _req asm("2") = (addr_t) req;
register unsigned long _resp asm("3") = (addr_t) resp;
register unsigned long _subcode asm("4") = subcode;
register unsigned long _rc asm("5") = -EOPNOTSUPP;
asm volatile(
" sam31\n"
" diag %[rx],%[ry],0x26c\n"
"0: sam64\n"
EX_TABLE(0b,0b)
: "+d" (_rc)
: [rx] "d" (_req), "d" (_resp), [ry] "d" (_subcode)
: "cc", "memory");
return _rc;
}
int diag26c(void *req, void *resp, enum diag26c_sc subcode)
{
diag_stat_inc(DIAG_STAT_X26C);
return __diag26c(req, resp, subcode);
return diag_dma_ops.diag26c(req, resp, subcode);
}
EXPORT_SYMBOL(diag26c);
......@@ -21,95 +21,124 @@
#include <asm/debug.h>
#include <asm/dis.h>
#include <asm/ipl.h>
#include <asm/unwind.h>
/*
* For dump_trace we have tree different stack to consider:
* - the panic stack which is used if the kernel stack has overflown
* - the asynchronous interrupt stack (cpu related)
* - the synchronous kernel stack (process related)
* The stack trace can start at any of the three stacks and can potentially
* touch all of them. The order is: panic stack, async stack, sync stack.
*/
static unsigned long __no_sanitize_address
__dump_trace(dump_trace_func_t func, void *data, unsigned long sp,
unsigned long low, unsigned long high)
const char *stack_type_name(enum stack_type type)
{
struct stack_frame *sf;
struct pt_regs *regs;
while (1) {
if (sp < low || sp > high - sizeof(*sf))
return sp;
sf = (struct stack_frame *) sp;
if (func(data, sf->gprs[8], 0))
return sp;
/* Follow the backchain. */
while (1) {
low = sp;
sp = sf->back_chain;
if (!sp)
break;
if (sp <= low || sp > high - sizeof(*sf))
return sp;
sf = (struct stack_frame *) sp;
if (func(data, sf->gprs[8], 1))
return sp;
}
/* Zero backchain detected, check for interrupt frame. */
sp = (unsigned long) (sf + 1);
if (sp <= low || sp > high - sizeof(*regs))
return sp;
regs = (struct pt_regs *) sp;
if (!user_mode(regs)) {
if (func(data, regs->psw.addr, 1))
return sp;
}
low = sp;
sp = regs->gprs[15];
switch (type) {
case STACK_TYPE_TASK:
return "task";
case STACK_TYPE_IRQ:
return "irq";
case STACK_TYPE_NODAT:
return "nodat";
case STACK_TYPE_RESTART:
return "restart";
default:
return "unknown";
}
}
void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task,
unsigned long sp)
static inline bool in_stack(unsigned long sp, struct stack_info *info,
enum stack_type type, unsigned long low,
unsigned long high)
{
if (sp < low || sp >= high)
return false;
info->type = type;
info->begin = low;
info->end = high;
return true;
}
static bool in_task_stack(unsigned long sp, struct task_struct *task,
struct stack_info *info)
{
unsigned long stack;
stack = (unsigned long) task_stack_page(task);
return in_stack(sp, info, STACK_TYPE_TASK, stack, stack + THREAD_SIZE);
}
static bool in_irq_stack(unsigned long sp, struct stack_info *info)
{
unsigned long frame_size;
unsigned long frame_size, top;
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
#ifdef CONFIG_CHECK_STACK
sp = __dump_trace(func, data, sp,
S390_lowcore.nodat_stack + frame_size - THREAD_SIZE,
S390_lowcore.nodat_stack + frame_size);
#endif
sp = __dump_trace(func, data, sp,
S390_lowcore.async_stack + frame_size - THREAD_SIZE,
S390_lowcore.async_stack + frame_size);
task = task ?: current;
__dump_trace(func, data, sp,
(unsigned long)task_stack_page(task),
(unsigned long)task_stack_page(task) + THREAD_SIZE);
top = S390_lowcore.async_stack + frame_size;
return in_stack(sp, info, STACK_TYPE_IRQ, top - THREAD_SIZE, top);
}
static bool in_nodat_stack(unsigned long sp, struct stack_info *info)
{
unsigned long frame_size, top;
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
top = S390_lowcore.nodat_stack + frame_size;
return in_stack(sp, info, STACK_TYPE_NODAT, top - THREAD_SIZE, top);
}
EXPORT_SYMBOL_GPL(dump_trace);
static int show_address(void *data, unsigned long address, int reliable)
static bool in_restart_stack(unsigned long sp, struct stack_info *info)
{
if (reliable)
printk(" [<%016lx>] %pSR \n", address, (void *)address);
else
printk("([<%016lx>] %pSR)\n", address, (void *)address);
unsigned long frame_size, top;
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
top = S390_lowcore.restart_stack + frame_size;
return in_stack(sp, info, STACK_TYPE_RESTART, top - THREAD_SIZE, top);
}
int get_stack_info(unsigned long sp, struct task_struct *task,
struct stack_info *info, unsigned long *visit_mask)
{
if (!sp)
goto unknown;
task = task ? : current;
/* Check per-task stack */
if (in_task_stack(sp, task, info))
goto recursion_check;
if (task != current)
goto unknown;
/* Check per-cpu stacks */
if (!in_irq_stack(sp, info) &&
!in_nodat_stack(sp, info) &&
!in_restart_stack(sp, info))
goto unknown;
recursion_check:
/*
* Make sure we don't iterate through any given stack more than once.
* If it comes up a second time then there's something wrong going on:
* just break out and report an unknown stack type.
*/
if (*visit_mask & (1UL << info->type)) {
printk_deferred_once(KERN_WARNING
"WARNING: stack recursion on stack type %d\n",
info->type);
goto unknown;
}
*visit_mask |= 1UL << info->type;
return 0;
unknown:
info->type = STACK_TYPE_UNKNOWN;
return -EINVAL;
}
void show_stack(struct task_struct *task, unsigned long *stack)
{
unsigned long sp = (unsigned long) stack;
struct unwind_state state;
if (!sp)
sp = task ? task->thread.ksp : current_stack_pointer();
printk("Call Trace:\n");
dump_trace(show_address, NULL, task, sp);
if (!task)
task = current;
debug_show_held_locks(task);
unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
printk(state.reliable ? " [<%016lx>] %pSR \n" :
"([<%016lx>] %pSR)\n",
state.ip, (void *) state.ip);
debug_show_held_locks(task ? : current);
}
static void show_last_breaking_event(struct pt_regs *regs)
......
......@@ -30,6 +30,7 @@
#include <asm/sclp.h>
#include <asm/facility.h>
#include <asm/boot_data.h>
#include <asm/pci_insn.h>
#include "entry.h"
/*
......@@ -138,9 +139,9 @@ static void early_pgm_check_handler(void)
unsigned long addr;
addr = S390_lowcore.program_old_psw.addr;
fixup = search_exception_tables(addr);
fixup = s390_search_extables(addr);
if (!fixup)
disabled_wait(0);
disabled_wait();
/* Disable low address protection before storing into lowcore. */
__ctl_store(cr0, 0, 0);
cr0_new = cr0 & ~(1UL << 28);
......@@ -235,6 +236,7 @@ static __init void detect_machine_facilities(void)
clock_comparator_max = -1ULL >> 1;
__ctl_set_bit(0, 53);
}
enable_mio_ctl();
}
static inline void save_vector_registers(void)
......@@ -296,7 +298,7 @@ static void __init check_image_bootable(void)
sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n");
sclp_early_printk("This image does not contain all parts necessary for starting up. Use\n");
sclp_early_printk("bzImage or arch/s390/boot/compressed/vmlinux instead.\n");
disabled_wait(0xbadb007);
disabled_wait();
}
void __init startup_init(void)
......@@ -309,7 +311,6 @@ void __init startup_init(void)
setup_facility_list();
detect_machine_type();
setup_arch_string();
ipl_store_parameters();
setup_boot_command_line();
detect_diag9c();
detect_diag44();
......
......@@ -25,7 +25,7 @@ static void __init reset_tod_clock(void)
return;
/* TOD clock not running. Set the clock to Unix Epoch. */
if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0)
disabled_wait(0);
disabled_wait();
memset(tod_clock_base, 0, 16);
*(__u64 *) &tod_clock_base[1] = TOD_UNIX_EPOCH;
......
......@@ -224,6 +224,7 @@ ENTRY(__bpon)
.globl __bpon
BPON
BR_EX %r14
ENDPROC(__bpon)
/*
* Scheduler resume function, called by switch_to
......@@ -248,6 +249,7 @@ ENTRY(__switch_to)
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
BR_EX %r14
ENDPROC(__switch_to)
.L__critical_start:
......@@ -324,6 +326,7 @@ sie_exit:
EX_TABLE(.Lrewind_pad4,.Lsie_fault)
EX_TABLE(.Lrewind_pad2,.Lsie_fault)
EX_TABLE(sie_exit,.Lsie_fault)
ENDPROC(sie64a)
EXPORT_SYMBOL(sie64a)
EXPORT_SYMBOL(sie_exit)
#endif
......@@ -358,19 +361,19 @@ ENTRY(system_call)
# load address of system call table
lg %r10,__THREAD_sysc_table(%r13,%r12)
llgh %r8,__PT_INT_CODE+2(%r11)
slag %r8,%r8,2 # shift and test for svc 0
slag %r8,%r8,3 # shift and test for svc 0
jnz .Lsysc_nr_ok
# svc 0: system call number in %r1
llgfr %r1,%r1 # clear high word in r1
cghi %r1,NR_syscalls
jnl .Lsysc_nr_ok
sth %r1,__PT_INT_CODE+2(%r11)
slag %r8,%r1,2
slag %r8,%r1,3
.Lsysc_nr_ok:
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
stg %r2,__PT_ORIG_GPR2(%r11)
stg %r7,STACK_FRAME_OVERHEAD(%r15)
lgf %r9,0(%r8,%r10) # get system call add.
lg %r9,0(%r8,%r10) # get system call add.
TSTMSK __TI_flags(%r12),_TIF_TRACE
jnz .Lsysc_tracesys
BASR_EX %r14,%r9 # call sys_xxxx
......@@ -556,8 +559,8 @@ ENTRY(system_call)
lghi %r0,NR_syscalls
clgr %r0,%r2
jnh .Lsysc_tracenogo
sllg %r8,%r2,2
lgf %r9,0(%r8,%r10)
sllg %r8,%r2,3
lg %r9,0(%r8,%r10)
.Lsysc_tracego:
lmg %r3,%r7,__PT_R3(%r11)
stg %r7,STACK_FRAME_OVERHEAD(%r15)
......@@ -570,6 +573,7 @@ ENTRY(system_call)
lgr %r2,%r11 # pass pointer to pt_regs
larl %r14,.Lsysc_return
jg do_syscall_trace_exit
ENDPROC(system_call)
#
# a new process exits the kernel with ret_from_fork
......@@ -584,10 +588,16 @@ ENTRY(ret_from_fork)
jne .Lsysc_tracenogo
# it's a kernel thread
lmg %r9,%r10,__PT_R9(%r11) # load gprs
la %r2,0(%r10)
BASR_EX %r14,%r9
j .Lsysc_tracenogo
ENDPROC(ret_from_fork)
ENTRY(kernel_thread_starter)
la %r2,0(%r10)
BASR_EX %r14,%r9
j .Lsysc_tracenogo
ENDPROC(kernel_thread_starter)
/*
* Program check handler routine
......@@ -665,9 +675,9 @@ ENTRY(pgm_check_handler)
larl %r1,pgm_check_table
llgh %r10,__PT_INT_CODE+2(%r11)
nill %r10,0x007f
sll %r10,2
sll %r10,3
je .Lpgm_return
lgf %r9,0(%r10,%r1) # load address of handler routine
lg %r9,0(%r10,%r1) # load address of handler routine
lgr %r2,%r11 # pass pointer to pt_regs
BASR_EX %r14,%r9 # branch to interrupt-handler
.Lpgm_return:
......@@ -698,6 +708,7 @@ ENTRY(pgm_check_handler)
stg %r14,__LC_RETURN_PSW+8
lghi %r14,_PIF_SYSCALL | _PIF_PER_TRAP
lpswe __LC_RETURN_PSW # branch to .Lsysc_per and enable irqs
ENDPROC(pgm_check_handler)
/*
* IO interrupt handler routine
......@@ -926,6 +937,7 @@ ENTRY(io_int_handler)
ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
TRACE_IRQS_OFF
j .Lio_return
ENDPROC(io_int_handler)
/*
* External interrupt handler routine
......@@ -965,6 +977,7 @@ ENTRY(ext_int_handler)
lghi %r3,EXT_INTERRUPT
brasl %r14,do_IRQ
j .Lio_return
ENDPROC(ext_int_handler)
/*
* Load idle PSW. The second "half" of this function is in .Lcleanup_idle.
......@@ -989,6 +1002,7 @@ ENTRY(psw_idle)
lpswe __SF_EMPTY(%r15)
BR_EX %r14
.Lpsw_idle_end:
ENDPROC(psw_idle)
/*
* Store floating-point controls and floating-point or vector register
......@@ -1031,6 +1045,7 @@ ENTRY(save_fpu_regs)
.Lsave_fpu_regs_exit:
BR_EX %r14
.Lsave_fpu_regs_end:
ENDPROC(save_fpu_regs)
EXPORT_SYMBOL(save_fpu_regs)
/*
......@@ -1077,6 +1092,7 @@ load_fpu_regs:
.Lload_fpu_regs_exit:
BR_EX %r14
.Lload_fpu_regs_end:
ENDPROC(load_fpu_regs)
.L__critical_end:
......@@ -1206,6 +1222,7 @@ ENTRY(mcck_int_handler)
lg %r15,__LC_NODAT_STACK
la %r11,STACK_FRAME_OVERHEAD(%r15)
j .Lmcck_skip
ENDPROC(mcck_int_handler)
#
# PSW restart interrupt handler
......@@ -1232,6 +1249,7 @@ ENTRY(restart_int_handler)
2: sigp %r4,%r3,SIGP_STOP # sigp stop to current cpu
brc 2,2b
3: j 3b
ENDPROC(restart_int_handler)
.section .kprobes.text, "ax"
......@@ -1241,7 +1259,7 @@ ENTRY(restart_int_handler)
* No need to properly save the registers, we are going to panic anyway.
* Setup a pt_regs so that show_trace can provide a good call trace.
*/
stack_overflow:
ENTRY(stack_overflow)
lg %r15,__LC_NODAT_STACK # change to panic stack
la %r11,STACK_FRAME_OVERHEAD(%r15)
stmg %r0,%r7,__PT_R0(%r11)
......@@ -1251,9 +1269,10 @@ stack_overflow:
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
lgr %r2,%r11 # pass pointer to pt_regs
jg kernel_stack_overflow
ENDPROC(stack_overflow)
#endif
cleanup_critical:
ENTRY(cleanup_critical)
#if IS_ENABLED(CONFIG_KVM)
clg %r9,BASED(.Lcleanup_table_sie) # .Lsie_gmap
jl 0f
......@@ -1289,6 +1308,7 @@ cleanup_critical:
clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end
jl .Lcleanup_load_fpu_regs
0: BR_EX %r14,%r11
ENDPROC(cleanup_critical)
.align 8
.Lcleanup_table:
......@@ -1512,7 +1532,7 @@ cleanup_critical:
.quad .Lsie_skip - .Lsie_entry
#endif
.section .rodata, "a"
#define SYSCALL(esame,emu) .long __s390x_ ## esame
#define SYSCALL(esame,emu) .quad __s390x_ ## esame
.globl sys_call_table
sys_call_table:
#include "asm/syscall_table.h"
......@@ -1520,7 +1540,7 @@ sys_call_table:
#ifdef CONFIG_COMPAT
#define SYSCALL(esame,emu) .long __s390_ ## emu
#define SYSCALL(esame,emu) .quad __s390_ ## emu
.globl sys_call_table_emu
sys_call_table_emu:
#include "asm/syscall_table.h"
......
......@@ -65,7 +65,7 @@ int setup_profiling_timer(unsigned int multiplier);
void __init time_init(void);
int pfn_is_nosave(unsigned long);
void s390_early_resume(void);
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip);
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long sp, unsigned long ip);
struct s390_mmap_arg_struct;
struct fadvise64_64_args;
......
......@@ -201,17 +201,18 @@ device_initcall(ftrace_plt_init);
* Hook the return address and push it in the stack of return addresses
* in current thread info.
*/
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
unsigned long prepare_ftrace_return(unsigned long ra, unsigned long sp,
unsigned long ip)
{
if (unlikely(ftrace_graph_is_dead()))
goto out;
if (unlikely(atomic_read(&current->tracing_graph_pause)))
goto out;
ip -= MCOUNT_INSN_SIZE;
if (!function_graph_enter(parent, ip, 0, NULL))
parent = (unsigned long) return_to_handler;
if (!function_graph_enter(ra, ip, 0, (void *) sp))
ra = (unsigned long) return_to_handler;
out:
return parent;
return ra;
}
NOKPROBE_SYMBOL(prepare_ftrace_return);
......
......@@ -26,7 +26,6 @@ ENTRY(startup_continue)
0: larl %r1,tod_clock_base
mvc 0(16,%r1),__LC_BOOT_CLOCK
larl %r13,.LPG1 # get base
lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
larl %r0,boot_vdso_data
stg %r0,__LC_VDSO_PER_CPU
#
......@@ -61,22 +60,6 @@ ENTRY(startup_continue)
.align 16
.LPG1:
.Lctl: .quad 0x04040000 # cr0: AFP registers & secondary space
.quad 0 # cr1: primary space segment table
.quad .Lduct # cr2: dispatchable unit control table
.quad 0 # cr3: instruction authorization
.quad 0xffff # cr4: instruction authorization
.quad .Lduct # cr5: primary-aste origin
.quad 0 # cr6: I/O interrupts
.quad 0 # cr7: secondary space segment table
.quad 0 # cr8: access registers translation
.quad 0 # cr9: tracing off
.quad 0 # cr10: tracing off
.quad 0 # cr11: tracing off
.quad 0 # cr12: tracing off
.quad 0 # cr13: home space segment table
.quad 0xc0000000 # cr14: machine check handling off
.quad .Llinkage_stack # cr15: linkage stack operations
.Lpcmsk:.quad 0x0000000180000000
.L4malign:.quad 0xffffffffffc00000
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
......@@ -84,14 +67,5 @@ ENTRY(startup_continue)
.Lparmaddr:
.quad PARMAREA
.align 64
.Lduct: .long 0,.Laste,.Laste,0,.Lduald,0,0,0
.long 0,0,0,0,0,0,0,0
.Laste: .quad 0,0xffffffffffffffff,0,0,0,0,0,0
.align 128
.Lduald:.rept 8
.long 0x80000000,0,0,0 # invalid access-list entries
.endr
.Llinkage_stack:
.long 0,0,0x89000000,0,0,0,0x8a000000,0
.Ldw: .quad 0x0002000180000000,0x0000000000000000
.Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
// SPDX-License-Identifier: GPL-2.0
#include <linux/ima.h>
#include <asm/boot_data.h>
bool arch_ima_get_secureboot(void)
{
return ipl_secure_flag;
}
const char * const *arch_get_ima_policy(void)
{
return NULL;
}
This diff is collapsed.
......@@ -11,11 +11,11 @@ size_t ipl_block_get_ascii_vmparm(char *dest, size_t size,
char has_lowercase = 0;
len = 0;
if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
(ipb->ipl_info.ccw.vm_parm_len > 0)) {
if ((ipb->ccw.vm_flags & IPL_PB0_CCW_VM_FLAG_VP) &&
(ipb->ccw.vm_parm_len > 0)) {
len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len);
memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
len = min_t(size_t, size - 1, ipb->ccw.vm_parm_len);
memcpy(dest, ipb->ccw.vm_parm, len);
/* If at least one character is lowercase, we assume mixed
* case; otherwise we convert everything to lowercase.
*/
......
......@@ -26,6 +26,7 @@
#include <asm/lowcore.h>
#include <asm/irq.h>
#include <asm/hw_irq.h>
#include <asm/stacktrace.h>
#include "entry.h"
DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
......@@ -73,7 +74,6 @@ static const struct irq_class irqclass_sub_desc[] = {
{.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
{.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"},
{.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
{.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
{.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"},
{.irq = IRQIO_C15, .name = "C15", .desc = "[I/O] 3215"},
{.irq = IRQIO_C70, .name = "C70", .desc = "[I/O] 3270"},
......@@ -81,14 +81,16 @@ static const struct irq_class irqclass_sub_desc[] = {
{.irq = IRQIO_VMR, .name = "VMR", .desc = "[I/O] Unit Record Devices"},
{.irq = IRQIO_LCS, .name = "LCS", .desc = "[I/O] LCS"},
{.irq = IRQIO_CTC, .name = "CTC", .desc = "[I/O] CTC"},
{.irq = IRQIO_APB, .name = "APB", .desc = "[I/O] AP Bus"},
{.irq = IRQIO_ADM, .name = "ADM", .desc = "[I/O] EADM Subchannel"},
{.irq = IRQIO_CSC, .name = "CSC", .desc = "[I/O] CHSC Subchannel"},
{.irq = IRQIO_PCI, .name = "PCI", .desc = "[I/O] PCI Interrupt" },
{.irq = IRQIO_MSI, .name = "MSI", .desc = "[I/O] MSI Interrupt" },
{.irq = IRQIO_VIR, .name = "VIR", .desc = "[I/O] Virtual I/O Devices"},
{.irq = IRQIO_VAI, .name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"},
{.irq = IRQIO_GAL, .name = "GAL", .desc = "[I/O] GIB Alert"},
{.irq = IRQIO_QAI, .name = "QAI", .desc = "[AIO] QDIO Adapter Interrupt"},
{.irq = IRQIO_APB, .name = "APB", .desc = "[AIO] AP Bus"},
{.irq = IRQIO_PCF, .name = "PCF", .desc = "[AIO] PCI Floating Interrupt"},
{.irq = IRQIO_PCD, .name = "PCD", .desc = "[AIO] PCI Directed Interrupt"},
{.irq = IRQIO_MSI, .name = "MSI", .desc = "[AIO] MSI Interrupt"},
{.irq = IRQIO_VAI, .name = "VAI", .desc = "[AIO] Virtual I/O Devices AI"},
{.irq = IRQIO_GAL, .name = "GAL", .desc = "[AIO] GIB Alert"},
{.irq = NMI_NMI, .name = "NMI", .desc = "[NMI] Machine Check"},
{.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"},
};
......@@ -116,6 +118,34 @@ void do_IRQ(struct pt_regs *regs, int irq)
set_irq_regs(old_regs);
}
static void show_msi_interrupt(struct seq_file *p, int irq)
{
struct irq_desc *desc;
unsigned long flags;
int cpu;
irq_lock_sparse();
desc = irq_to_desc(irq);
if (!desc)
goto out;
raw_spin_lock_irqsave(&desc->lock, flags);
seq_printf(p, "%3d: ", irq);
for_each_online_cpu(cpu)
seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu));
if (desc->irq_data.chip)
seq_printf(p, " %8s", desc->irq_data.chip->name);
if (desc->action)
seq_printf(p, " %s", desc->action->name);
seq_putc(p, '\n');
raw_spin_unlock_irqrestore(&desc->lock, flags);
out:
irq_unlock_sparse();
}
/*
* show_interrupts is needed by /proc/interrupts.
*/
......@@ -128,7 +158,7 @@ int show_interrupts(struct seq_file *p, void *v)
if (index == 0) {
seq_puts(p, " ");
for_each_online_cpu(cpu)
seq_printf(p, "CPU%d ", cpu);
seq_printf(p, "CPU%-8d", cpu);
seq_putc(p, '\n');
}
if (index < NR_IRQS_BASE) {
......@@ -139,9 +169,10 @@ int show_interrupts(struct seq_file *p, void *v)
seq_putc(p, '\n');
goto out;
}
if (index > NR_IRQS_BASE)
if (index < nr_irqs) {
show_msi_interrupt(p, index);
goto out;
}
for (index = 0; index < NR_ARCH_IRQS; index++) {
seq_printf(p, "%s: ", irqclass_sub_desc[index].name);
irq = irqclass_sub_desc[index].irq;
......
......@@ -10,19 +10,26 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <asm/ipl.h>
#include <asm/setup.h>
static int kexec_file_add_elf_kernel(struct kimage *image,
struct s390_load_data *data,
char *kernel, unsigned long kernel_len)
static int kexec_file_add_kernel_elf(struct kimage *image,
struct s390_load_data *data)
{
struct kexec_buf buf;
const Elf_Ehdr *ehdr;
const Elf_Phdr *phdr;
Elf_Addr entry;
void *kernel;
int i, ret;
kernel = image->kernel_buf;
ehdr = (Elf_Ehdr *)kernel;
buf.image = image;
if (image->type == KEXEC_TYPE_CRASH)
entry = STARTUP_KDUMP_OFFSET;
else
entry = ehdr->e_entry;
phdr = (void *)ehdr + ehdr->e_phoff;
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
......@@ -33,30 +40,27 @@ static int kexec_file_add_elf_kernel(struct kimage *image,
buf.bufsz = phdr->p_filesz;
buf.mem = ALIGN(phdr->p_paddr, phdr->p_align);
if (image->type == KEXEC_TYPE_CRASH)
buf.mem += crashk_res.start;
buf.memsz = phdr->p_memsz;
data->memsz = ALIGN(data->memsz, phdr->p_align) + buf.memsz;
if (phdr->p_paddr == 0) {
if (entry - phdr->p_paddr < phdr->p_memsz) {
data->kernel_buf = buf.buffer;
data->memsz += STARTUP_NORMAL_OFFSET;
buf.buffer += STARTUP_NORMAL_OFFSET;
buf.bufsz -= STARTUP_NORMAL_OFFSET;
buf.mem += STARTUP_NORMAL_OFFSET;
buf.memsz -= STARTUP_NORMAL_OFFSET;
data->kernel_mem = buf.mem;
data->parm = buf.buffer + PARMAREA;
}
if (image->type == KEXEC_TYPE_CRASH)
buf.mem += crashk_res.start;
ipl_report_add_component(data->report, &buf,
IPL_RB_COMPONENT_FLAG_SIGNED |
IPL_RB_COMPONENT_FLAG_VERIFIED,
IPL_RB_CERT_UNKNOWN);
ret = kexec_add_buffer(&buf);
if (ret)
return ret;
data->memsz += buf.memsz;
}
return 0;
return data->memsz ? 0 : -EINVAL;
}
static void *s390_elf_load(struct kimage *image,
......@@ -64,11 +68,10 @@ static void *s390_elf_load(struct kimage *image,
char *initrd, unsigned long initrd_len,
char *cmdline, unsigned long cmdline_len)
{
struct s390_load_data data = {0};
const Elf_Ehdr *ehdr;
const Elf_Phdr *phdr;
size_t size;
int i, ret;
int i;
/* image->fobs->probe already checked for valid ELF magic number. */
ehdr = (Elf_Ehdr *)kernel;
......@@ -101,24 +104,7 @@ static void *s390_elf_load(struct kimage *image,
if (size > kernel_len)
return ERR_PTR(-EINVAL);
ret = kexec_file_add_elf_kernel(image, &data, kernel, kernel_len);
if (ret)
return ERR_PTR(ret);
if (!data.memsz)
return ERR_PTR(-EINVAL);
if (initrd) {
ret = kexec_file_add_initrd(image, &data, initrd, initrd_len);
if (ret)
return ERR_PTR(ret);
}
ret = kexec_file_add_purgatory(image, &data);
if (ret)
return ERR_PTR(ret);
return kexec_file_update_kernel(image, &data);
return kexec_file_add_components(image, kexec_file_add_kernel_elf);
}
static int s390_elf_probe(const char *buf, unsigned long len)
......@@ -144,4 +130,7 @@ static int s390_elf_probe(const char *buf, unsigned long len)
const struct kexec_file_ops s390_kexec_elf_ops = {
.probe = s390_elf_probe,
.load = s390_elf_load,
#ifdef CONFIG_KEXEC_VERIFY_SIG
.verify_sig = s390_verify_sig,
#endif /* CONFIG_KEXEC_VERIFY_SIG */
};
......@@ -10,31 +10,34 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <asm/ipl.h>
#include <asm/setup.h>
static int kexec_file_add_image_kernel(struct kimage *image,
struct s390_load_data *data,
char *kernel, unsigned long kernel_len)
static int kexec_file_add_kernel_image(struct kimage *image,
struct s390_load_data *data)
{
struct kexec_buf buf;
int ret;
buf.image = image;
buf.buffer = kernel + STARTUP_NORMAL_OFFSET;
buf.bufsz = kernel_len - STARTUP_NORMAL_OFFSET;
buf.buffer = image->kernel_buf;
buf.bufsz = image->kernel_buf_len;
buf.mem = STARTUP_NORMAL_OFFSET;
buf.mem = 0;
if (image->type == KEXEC_TYPE_CRASH)
buf.mem += crashk_res.start;
buf.memsz = buf.bufsz;
ret = kexec_add_buffer(&buf);
data->kernel_buf = image->kernel_buf;
data->kernel_mem = buf.mem;
data->parm = image->kernel_buf + PARMAREA;
data->memsz += buf.memsz;
data->kernel_buf = kernel;
data->memsz += buf.memsz + STARTUP_NORMAL_OFFSET;
return ret;
ipl_report_add_component(data->report, &buf,
IPL_RB_COMPONENT_FLAG_SIGNED |
IPL_RB_COMPONENT_FLAG_VERIFIED,
IPL_RB_CERT_UNKNOWN);
return kexec_add_buffer(&buf);
}
static void *s390_image_load(struct kimage *image,
......@@ -42,24 +45,7 @@ static void *s390_image_load(struct kimage *image,
char *initrd, unsigned long initrd_len,
char *cmdline, unsigned long cmdline_len)
{
struct s390_load_data data = {0};
int ret;
ret = kexec_file_add_image_kernel(image, &data, kernel, kernel_len);
if (ret)
return ERR_PTR(ret);
if (initrd) {
ret = kexec_file_add_initrd(image, &data, initrd, initrd_len);
if (ret)
return ERR_PTR(ret);
}
ret = kexec_file_add_purgatory(image, &data);
if (ret)
return ERR_PTR(ret);
return kexec_file_update_kernel(image, &data);
return kexec_file_add_components(image, kexec_file_add_kernel_image);
}
static int s390_image_probe(const char *buf, unsigned long len)
......@@ -73,4 +59,7 @@ static int s390_image_probe(const char *buf, unsigned long len)
const struct kexec_file_ops s390_kexec_image_ops = {
.probe = s390_image_probe,
.load = s390_image_load,
#ifdef CONFIG_KEXEC_VERIFY_SIG
.verify_sig = s390_verify_sig,
#endif /* CONFIG_KEXEC_VERIFY_SIG */
};
......@@ -27,29 +27,30 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
struct kretprobe_blackpoint kretprobe_blacklist[] = { };
DEFINE_INSN_CACHE_OPS(dmainsn);
DEFINE_INSN_CACHE_OPS(s390_insn);
static void *alloc_dmainsn_page(void)
{
void *page;
static int insn_page_in_use;
static char insn_page[PAGE_SIZE] __aligned(PAGE_SIZE);
page = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
if (page)
set_memory_x((unsigned long) page, 1);
return page;
static void *alloc_s390_insn_page(void)
{
if (xchg(&insn_page_in_use, 1) == 1)
return NULL;
set_memory_x((unsigned long) &insn_page, 1);
return &insn_page;
}
static void free_dmainsn_page(void *page)
static void free_s390_insn_page(void *page)
{
set_memory_nx((unsigned long) page, 1);
free_page((unsigned long)page);
xchg(&insn_page_in_use, 0);
}
struct kprobe_insn_cache kprobe_dmainsn_slots = {
.mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
.alloc = alloc_dmainsn_page,
.free = free_dmainsn_page,
.pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
struct kprobe_insn_cache kprobe_s390_insn_slots = {
.mutex = __MUTEX_INITIALIZER(kprobe_s390_insn_slots.mutex),
.alloc = alloc_s390_insn_page,
.free = free_s390_insn_page,
.pages = LIST_HEAD_INIT(kprobe_s390_insn_slots.pages),
.insn_size = MAX_INSN_SIZE,
};
......@@ -102,7 +103,7 @@ static int s390_get_insn_slot(struct kprobe *p)
*/
p->ainsn.insn = NULL;
if (is_kernel_addr(p->addr))
p->ainsn.insn = get_dmainsn_slot();
p->ainsn.insn = get_s390_insn_slot();
else if (is_module_addr(p->addr))
p->ainsn.insn = get_insn_slot();
return p->ainsn.insn ? 0 : -ENOMEM;
......@@ -114,7 +115,7 @@ static void s390_free_insn_slot(struct kprobe *p)
if (!p->ainsn.insn)
return;
if (is_kernel_addr(p->addr))
free_dmainsn_slot(p->ainsn.insn, 0);
free_s390_insn_slot(p->ainsn.insn, 0);
else
free_insn_slot(p->ainsn.insn, 0);
p->ainsn.insn = NULL;
......@@ -572,7 +573,7 @@ static int kprobe_trap_handler(struct pt_regs *regs, int trapnr)
* In case the user-specified fault handler returned
* zero, try to fix up.
*/
entry = search_exception_tables(regs->psw.addr);
entry = s390_search_extables(regs->psw.addr);
if (entry) {
regs->psw.addr = extable_fixup(entry);
return 1;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -125,7 +125,7 @@ void nmi_free_per_cpu(struct lowcore *lc)
static notrace void s390_handle_damage(void)
{
smp_emergency_stop();
disabled_wait((unsigned long) __builtin_return_address(0));
disabled_wait();
while (1);
}
NOKPROBE_SYMBOL(s390_handle_damage);
......
This diff is collapsed.
......@@ -15,7 +15,7 @@ ssize_t cpu_show_spectre_v2(struct device *dev,
{
if (test_facility(156))
return sprintf(buf, "Mitigation: etokens\n");
if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable)
return sprintf(buf, "Mitigation: execute trampolines\n");
if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
return sprintf(buf, "Mitigation: limited branch prediction\n");
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment