diff --git a/README.md b/README.md index 29696f842..3b0a98d34 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Supported Architectures - [x] x86-64 - [x] ppc64le - [ ] arm64 -- [ ] s390 +- [x] s390 [upstream prerequisites](doc/s390-upstream-prerequisites.md) Installation ------------ diff --git a/doc/s390-upstream-prerequisites.md b/doc/s390-upstream-prerequisites.md new file mode 100644 index 000000000..11173ef57 --- /dev/null +++ b/doc/s390-upstream-prerequisites.md @@ -0,0 +1,33 @@ +### s390 backporting + +**Prerequisite gcc patches (all backported to releases/gcc-11 branch):** +- gcc-mirror/gcc@a1c1b7a IBM Z: Define NO_PROFILE_COUNTERS +- gcc-mirror/gcc@0990d93 IBM Z: Use @PLT symbols for local functions in 64-bit mode +- gcc-mirror/gcc@935b522 S/390: New option -mpic-data-is-text-relative +- gcc-mirror/gcc@8753b13 IBM Z: fix section type conflict with -mindirect-branch-table + +**Prerequisite kernel patches:** +**v5.19:** +- 69505e3d9a39 bug: Use normal relative pointers in 'struct bug_entry' + +**v5.18:** +- 602bf1687e6f s390/nospec: align and size extern thunks +- 1d2ad084800e s390/nospec: add an option to use thunk-extern +- eed38cd2f46f s390/nospec: generate single register thunks if possible +- 2268169c14e5 s390: remove unused expoline to BC instructions +- f0003a9e4c18 s390/entry: remove unused expoline thunk + +**v5.16:** +- torvalds/linux@f6ac18f sched: Improve try_invoke_on_locked_down_task() +- torvalds/linux@9b3c4ab sched,rcu: Rework try_invoke_on_locked_down_task() +- torvalds/linux@00619f7 sched,livepatch: Use task_call_func() +- torvalds/linux@8850cb6 sched: Simplify wake_up_*idle*() +- torvalds/linux@5de62ea sched,livepatch: Use wake_up_if_idle() +- torvalds/linux@96611c2 sched: Improve wake_up_all_idle_cpus() take #2 + +**v5.15** +- torvalds/linux@de5012b s390/ftrace: implement hotpatching +- torvalds/linux@67ccddf ftrace: Introduce ftrace_need_init_nop() + +**v5.14:** +- torvalds/linux@7561c14 s390/vdso: add .got.plt in vdso linker script diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index da96edff0..bebf3cd96 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -21,7 +21,8 @@ GCC_PLUGINS_DIR := $(shell $(CROSS_COMPILE)gcc -print-file-name=plugin) PLUGIN_CFLAGS := $(filter-out -Wconversion, $(CFLAGS)) PLUGIN_CFLAGS += -shared -I$(GCC_PLUGINS_DIR)/include \ -Igcc-plugins -fPIC -fno-rtti -O2 -Wall -else ifneq ($(ARCH),x86_64) +endif +ifeq ($(filter $(ARCH),s390x x86_64 ppc64le),) $(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures) endif diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index bebe3bd15..5f9cd1903 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -118,6 +118,16 @@ static bool is_bundleable(struct symbol *sym) !strcmp(sym->sec->name + 13, sym->name)) return true; + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.ro.local.", 19) && + !strcmp(sym->sec->name + 19, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.local.", 16) && + !strcmp(sym->sec->name + 16, sym->name)) + return 1; + if (sym->type == STT_OBJECT && !strncmp(sym->sec->name, ".rodata.",8) && !strcmp(sym->sec->name + 8, sym->name)) @@ -163,6 +173,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf, sym->sym.st_value == 8); case X86_64: return false; + case S390: + return false; default: ERROR("unsupported arch"); } @@ -304,8 +316,7 @@ static bool is_dynamic_debug_symbol(struct symbol *sym) static bool is_string_literal_section(struct section *sec) { - return !strncmp(sec->name, ".rodata.", 8) && - strstr(sec->name, ".str1."); + return !strncmp(sec->name, ".rodata.", 8) && strstr(sec->name, ".str"); } /* @@ -2104,6 +2115,17 @@ static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset) return 8; } +/* + * .s390_indirect_jump, .s390_indirect_call, .s390_indirect_branches, + * .s390_return_reg, .s390_return_mem contains indirect branch locations. This + * is an array of 32 bit elements. These sections could be used during runtime + * to replace the expolines with the normal indirect jump. + */ +static int s390_expolines_group_size(struct kpatch_elf *kelf, int offset) +{ + return 4; +} + /* * The rela groups in the .fixup section vary in size. The beginning of each * .fixup rela group is referenced by the __ex_table section. To find the size @@ -2157,27 +2179,27 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset) static struct special_section special_sections[] = { { .name = "__bug_table", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = bug_table_group_size, }, { .name = ".fixup", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = fixup_group_size, }, { .name = "__ex_table", /* must come after .fixup */ - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = ex_table_group_size, }, { .name = "__jump_table", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = jump_table_group_size, }, { .name = ".printk_index", - .arch = X86_64 | PPC64, + .arch = X86_64 | PPC64 | S390, .group_size = printk_index_group_size, }, { @@ -2192,7 +2214,7 @@ static struct special_section special_sections[] = { }, { .name = ".altinstructions", - .arch = X86_64, + .arch = X86_64 | S390, .group_size = altinstructions_group_size, }, { @@ -2230,6 +2252,31 @@ static struct special_section special_sections[] = { .arch = PPC64, .group_size = fixup_barrier_nospec_group_size, }, + { + .name = ".s390_return_mem", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_return_reg", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_call", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_branches", + .arch = S390, + .group_size = s390_expolines_group_size, + }, + { + .name = ".s390_indirect_jump", + .arch = S390, + .group_size = s390_expolines_group_size, + }, {}, }; @@ -3024,6 +3071,11 @@ static bool kpatch_is_core_module_symbol(char *name) !strcmp(name, "kpatch_shadow_get")); } +static bool is_expoline(struct kpatch_elf *kelf, char *name) +{ + return kelf->arch == S390 && !strncmp(name, "__s390_indirect_jump_r", 22); +} + static int function_ptr_rela(const struct rela *rela) { const struct rela *rela_toc = toc_rela(rela); @@ -3082,6 +3134,13 @@ static bool need_dynrela(struct kpatch_elf *kelf, struct lookup_table *table, if (kpatch_is_core_module_symbol(rela->sym->name)) return false; + /* + * Allow references to s390 expolines to remain as normal relas. They + * will be generated in the module by the kernel module link. + */ + if (is_expoline(kelf, rela->sym->name)) + return false; + if (rela->sym->sec) { /* * Internal symbols usually don't need dynrelas, because they @@ -3548,6 +3607,10 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) rela->type = R_X86_64_PC32; break; } + case S390: { + insn_offset = sym->sym.st_value; + break; + } default: ERROR("unsupported arch"); } @@ -3713,6 +3776,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) { struct symbol *sym; struct rela *rela; + unsigned char *insn; list_for_each_entry(sym, &kelf->symbols, list) { if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela) continue; @@ -3737,6 +3801,14 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) sym->has_func_profiling = 1; break; + case S390: + /* Check for compiler generated fentry nop - jgnop 0 */ + insn = sym->sec->data->d_buf; + if (insn[0] == 0xc0 && insn[1] == 0x04 && + insn[2] == 0x00 && insn[3] == 0x00 && + insn[4] == 0x00 && insn[5] == 0x00) + sym->has_func_profiling = 1; + break; default: ERROR("unsupported arch"); } diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 9f0fc6db5..02504e60b 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -343,12 +343,18 @@ find_special_section_data() { check[e]=true # exception_table_entry # Arch-specific features, without kernel CONFIG_ toggle - if [[ "$ARCH" = "x86_64" ]]; then - check[a]=true # alt_instr - kernel_version_gte 5.10.0 && check[s]=true # static_call_site - elif [[ "$ARCH" = "ppc64le" ]]; then - check[f]=true # fixup_entry - fi + case "$ARCH" in + "x86_64") + check[a]=true # alt_instr + kernel_version_gte 5.10.0 && check[s]=true # static_call_site + ;; + "ppc64le") + check[f]=true # fixup_entry + ;; + "s390x") + check[a]=true # alt_instr + ;; + esac # Kernel CONFIG_ features [[ -n "$CONFIG_PRINTK_INDEX" ]] && check[i]=true # pi_entry @@ -856,6 +862,7 @@ trace_on die "openEuler kernel doesn't have 'CONFIG_LIVEPATCH_PER_TASK_CONSISTENCY' enabled" [[ -z "$CONFIG_DEBUG_INFO" ]] && die "kernel doesn't have 'CONFIG_DEBUG_INFO' enabled" +[[ "$ARCH" = "s390x" ]] && [[ -z "$CONFIG_EXPOLINE_EXTERN" ]] && [[ -n "$CONFIG_EXPOLINE" ]] && die "kernel doesn't have 'CONFIG_EXPOLINE_EXTERN' enabled" # Build variables - Set some defaults, then adjust features # according to .config and kernel version @@ -937,6 +944,10 @@ if [[ "$ARCH" = "ppc64le" ]]; then ARCH_KCFLAGS="-mcmodel=large -fplugin=$PLUGINDIR/ppc64le-plugin.so" fi +if [[ "$ARCH" = "s390x" ]]; then + ARCH_KCFLAGS="-mno-pic-data-is-text-relative -fno-section-anchors" +fi + export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \ $ARCH_KCFLAGS $DEBUG_KCFLAGS" diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc index 476436fd7..5474a13bb 100755 --- a/kpatch-build/kpatch-cc +++ b/kpatch-build/kpatch-cc @@ -43,6 +43,9 @@ if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; th drivers/firmware/efi/libstub/*|\ arch/powerpc/kernel/prom_init.o|\ arch/powerpc/kernel/vdso64/*|\ + arch/s390/boot/*|\ + arch/s390/purgatory/*|\ + arch/s390/kernel/vdso64/*|\ lib/*|\ .*.o|\ */.lib_exports.o) diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 6a43ef5b9..92881e915 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -140,6 +140,8 @@ unsigned int absolute_rela_type(struct kpatch_elf *kelf) return R_PPC64_ADDR64; case X86_64: return R_X86_64_64; + case S390: + return R_390_64; default: ERROR("unsupported arch"); } @@ -222,6 +224,23 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec, } else ERROR("unhandled rela type %d", rela->type); break; + case S390: + /* + * For branch and relative load instructions, + * add_off is -2. + */ + if (rela->type == R_390_GOTENT || + rela->type == R_390_PLT32DBL || + rela->type == R_390_PC32DBL) + add_off = -2; + else if (rela->type == R_390_32 || + rela->type == R_390_64 || + rela->type == R_390_PC32 || + rela->type == R_390_PC64) + add_off = 0; + else + ERROR("unhandled rela type %d", rela->type); + break; default: ERROR("unsupported arch\n"); } @@ -479,6 +498,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name) case EM_X86_64: kelf->arch = X86_64; break; + case EM_S390: + kelf->arch = S390; + break; default: ERROR("Unsupported target architecture"); } diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c index 06f4b3848..c9e6cc7f1 100644 --- a/kpatch-build/lookup.c +++ b/kpatch-build/lookup.c @@ -272,12 +272,14 @@ static void symtab_read(struct lookup_table *table, char *path) * tables. .dynsym is just a subset of .symtab, so skip it to * avoid duplicates. */ - if (strstr(line, ".dynsym")) { - skip = true; - continue; - } else if (strstr(line, ".symtab")) { - skip = false; - continue; + if (!strncmp(line, "Symbol table ", 13)) { + if (strstr(line, ".dynsym")) { + skip = true; + continue; + } else if (strstr(line, ".symtab")) { + skip = false; + continue; + } } if (skip) continue;