Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 7a8e7da4 authored by Palmer Dabbelt's avatar Palmer Dabbelt
Browse files

RISC-V: Fixes to module loading

This cleans up the module support that was commited earlier to work with
what's actually emitted from our GCC port as it lands upstream.  Most of
the work here is adding new relocations to the kernel.

There's some limitations on module loading imposed by the kernel:

* The kernel doesn't support linker relaxation, which is necessary to
  support R_RISCV_ALIGN.  In order to get reliable module building
  you're going to need to a GCC that supports the new '-mno-relax',
  which IIRC isn't going to be out until 8.1.0.  It's somewhat unlikely
  that R_RISCV_ALIGN will appear in a module even without '-mno-relax'
  support, so issues shouldn't be common.

* There is no large code model for RISC-V, which means modules must be
  loaded within a 32-bit signed offset of the kernel.  We don't
  currently have any mechanism for ensuring this memory remains free or
  moving pages around, so issues here might be common.

I fixed a singcle merge conflict in arch/riscv/kernel/Makefile.
parents 2c9046b7 e21d5421
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -134,6 +134,10 @@ choice
		bool "medium any code model"
endchoice

config MODULE_SECTIONS
	bool
	select HAVE_MOD_ARCH_SPECIFIC

choice
	prompt "Maximum Physical Memory"
	default MAXPHYSMEM_2GB if 32BIT
@@ -144,6 +148,7 @@ choice
		bool "2GiB"
	config MAXPHYSMEM_128GB
		depends on 64BIT && CMODEL_MEDANY
		select MODULE_SECTIONS if MODULES
		bool "128GiB"
endchoice

+5 −0
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ endif
ifeq ($(CONFIG_CMODEL_MEDANY),y)
	KBUILD_CFLAGS += -mcmodel=medany
endif
ifeq ($(CONFIG_MODULE_SECTIONS),y)
	KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/riscv/kernel/module.lds
endif

KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)

# GCC versions that support the "-mstrict-align" option default to allowing
# unaligned accesses.  While unaligned accesses are explicitly allowed in the
+2 −0
Original line number Diff line number Diff line
@@ -73,3 +73,5 @@ CONFIG_NFS_V4_2=y
CONFIG_ROOT_NFS=y
# CONFIG_RCU_TRACE is not set
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
+113 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2017 Andes Technology Corporation */

#ifndef _ASM_RISCV_MODULE_H
#define _ASM_RISCV_MODULE_H

#include <asm-generic/module.h>

#define MODULE_ARCH_VERMAGIC    "riscv"

u64 module_emit_got_entry(struct module *mod, u64 val);
u64 module_emit_plt_entry(struct module *mod, u64 val);

#ifdef CONFIG_MODULE_SECTIONS
struct mod_section {
	struct elf64_shdr *shdr;
	int num_entries;
	int max_entries;
};

struct mod_arch_specific {
	struct mod_section got;
	struct mod_section plt;
	struct mod_section got_plt;
};

struct got_entry {
	u64 symbol_addr;	/* the real variable address */
};

static inline struct got_entry emit_got_entry(u64 val)
{
	return (struct got_entry) {val};
}

static inline struct got_entry *get_got_entry(u64 val,
					      const struct mod_section *sec)
{
	struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr;
	int i;
	for (i = 0; i < sec->num_entries; i++) {
		if (got[i].symbol_addr == val)
			return &got[i];
	}
	return NULL;
}

struct plt_entry {
	/*
	 * Trampoline code to real target address. The return address
	 * should be the original (pc+4) before entring plt entry.
	 */
	u32 insn_auipc;		/* auipc t0, 0x0                       */
	u32 insn_ld;		/* ld    t1, 0x10(t0)                  */
	u32 insn_jr;		/* jr    t1                            */
};

#define OPC_AUIPC  0x0017
#define OPC_LD     0x3003
#define OPC_JALR   0x0067
#define REG_T0     0x5
#define REG_T1     0x6

static inline struct plt_entry emit_plt_entry(u64 val, u64 plt, u64 got_plt)
{
	/*
	 * U-Type encoding:
	 * +------------+----------+----------+
	 * | imm[31:12] | rd[11:7] | opc[6:0] |
	 * +------------+----------+----------+
	 *
	 * I-Type encoding:
	 * +------------+------------+--------+----------+----------+
	 * | imm[31:20] | rs1[19:15] | funct3 | rd[11:7] | opc[6:0] |
	 * +------------+------------+--------+----------+----------+
	 *
	 */
	u64 offset = got_plt - plt;
	u32 hi20 = (offset + 0x800) & 0xfffff000;
	u32 lo12 = (offset - hi20);
	return (struct plt_entry) {
		OPC_AUIPC | (REG_T0 << 7) | hi20,
		OPC_LD | (lo12 << 20) | (REG_T0 << 15) | (REG_T1 << 7),
		OPC_JALR | (REG_T1 << 15)
	};
}

static inline int get_got_plt_idx(u64 val, const struct mod_section *sec)
{
	struct got_entry *got_plt = (struct got_entry *)sec->shdr->sh_addr;
	int i;
	for (i = 0; i < sec->num_entries; i++) {
		if (got_plt[i].symbol_addr == val)
			return i;
	}
	return -1;
}

static inline struct plt_entry *get_plt_entry(u64 val,
				      const struct mod_section *sec_plt,
				      const struct mod_section *sec_got_plt)
{
	struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr;
	int got_plt_idx = get_got_plt_idx(val, sec_got_plt);
	if (got_plt_idx >= 0)
		return plt + got_plt_idx;
	else
		return NULL;
}

#endif /* CONFIG_MODULE_SECTIONS */

#endif /* _ASM_RISCV_MODULE_H */
+7 −0
Original line number Diff line number Diff line
@@ -79,5 +79,12 @@ typedef union __riscv_fp_state elf_fpregset_t;
#define R_RISCV_TPREL_I		49
#define R_RISCV_TPREL_S		50
#define R_RISCV_RELAX		51
#define R_RISCV_SUB6		52
#define R_RISCV_SET6		53
#define R_RISCV_SET8		54
#define R_RISCV_SET16		55
#define R_RISCV_SET32		56
#define R_RISCV_32_PCREL	57


#endif /* _UAPI_ASM_ELF_H */
Loading