Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S390x kpatch support #1203

Merged
merged 7 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Supported Architectures
- [x] x86-64
- [x] ppc64le
- [ ] arm64
- [ ] s390
- [x] s390 [upstream prerequisites](doc/s390-upstream-prerequisites.md)

Installation
------------
Expand Down
33 changes: 33 additions & 0 deletions doc/s390-upstream-prerequisites.md
Original file line number Diff line number Diff line change
@@ -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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most likely this will end up in 5.19, so it's probably safe to add a v5.19 header for this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added


**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
3 changes: 2 additions & 1 deletion kpatch-build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on how confident we feel after this PR, we could update the referenced README.md #supported-architectures section to include s390x. Or would you prefer we merge and test internally before advertising support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this is merged, we can update s390x as supported in README.md.

endif

Expand Down
88 changes: 80 additions & 8 deletions kpatch-build/create-diff-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}

/*
Expand Down Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I wish x86 gcc had this feature. We had to implement it manually with objtool.

*/
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
Expand Down Expand Up @@ -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,
},
{
Expand All @@ -2192,7 +2214,7 @@ static struct special_section special_sections[] = {
},
{
.name = ".altinstructions",
.arch = X86_64,
.arch = X86_64 | S390,
.group_size = altinstructions_group_size,
},
{
Expand Down Expand Up @@ -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,
},
{},
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A short comment here with the name of the instruction would be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

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");
}
Expand Down
23 changes: 17 additions & 6 deletions kpatch-build/kpatch-build
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"

Expand Down
3 changes: 3 additions & 0 deletions kpatch-build/kpatch-cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions kpatch-build/kpatch-elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand Down
14 changes: 8 additions & 6 deletions kpatch-build/lookup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down