mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-02 17:26:42 +09:00
sparc64: Patch sun4v code sequences properly on module load.
[ Upstream commit 0b64120cce ]
Some of the sun4v code patching occurs in inline functions visible
to, and usable by, modules.
Therefore we have to patch them up during module load.
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
fde9394955
commit
cff6d2096e
@@ -42,6 +42,9 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
|
||||
extern void fpload(unsigned long *fpregs, unsigned long *fsr);
|
||||
|
||||
#else /* CONFIG_SPARC32 */
|
||||
|
||||
#include <asm/trap_block.h>
|
||||
|
||||
struct popc_3insn_patch_entry {
|
||||
unsigned int addr;
|
||||
unsigned int insns[3];
|
||||
@@ -57,6 +60,10 @@ extern struct popc_6insn_patch_entry __popc_6insn_patch,
|
||||
__popc_6insn_patch_end;
|
||||
|
||||
extern void __init per_cpu_patch(void);
|
||||
extern void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *,
|
||||
struct sun4v_1insn_patch_entry *);
|
||||
extern void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *,
|
||||
struct sun4v_2insn_patch_entry *);
|
||||
extern void __init sun4v_patch(void);
|
||||
extern void __init boot_cpu_id_too_large(int cpu);
|
||||
extern unsigned int dcache_parity_tl1_occurred;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/spitfire.h>
|
||||
|
||||
#include "entry.h"
|
||||
|
||||
#ifdef CONFIG_SPARC64
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
@@ -220,6 +222,29 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARC64
|
||||
static void do_patch_sections(const Elf_Ehdr *hdr,
|
||||
const Elf_Shdr *sechdrs)
|
||||
{
|
||||
const Elf_Shdr *s, *sun4v_1insn = NULL, *sun4v_2insn = NULL;
|
||||
char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
||||
|
||||
for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
|
||||
if (!strcmp(".sun4v_1insn_patch", secstrings + s->sh_name))
|
||||
sun4v_1insn = s;
|
||||
if (!strcmp(".sun4v_2insn_patch", secstrings + s->sh_name))
|
||||
sun4v_2insn = s;
|
||||
}
|
||||
|
||||
if (sun4v_1insn && tlb_type == hypervisor) {
|
||||
void *p = (void *) sun4v_1insn->sh_addr;
|
||||
sun4v_patch_1insn_range(p, p + sun4v_1insn->sh_size);
|
||||
}
|
||||
if (sun4v_2insn && tlb_type == hypervisor) {
|
||||
void *p = (void *) sun4v_2insn->sh_addr;
|
||||
sun4v_patch_2insn_range(p, p + sun4v_2insn->sh_size);
|
||||
}
|
||||
}
|
||||
|
||||
int module_finalize(const Elf_Ehdr *hdr,
|
||||
const Elf_Shdr *sechdrs,
|
||||
struct module *me)
|
||||
@@ -227,6 +252,8 @@ int module_finalize(const Elf_Ehdr *hdr,
|
||||
/* make jump label nops */
|
||||
jump_label_apply_nops(me);
|
||||
|
||||
do_patch_sections(hdr, sechdrs);
|
||||
|
||||
/* Cheetah's I-cache is fully coherent. */
|
||||
if (tlb_type == spitfire) {
|
||||
unsigned long va;
|
||||
|
||||
@@ -234,40 +234,50 @@ void __init per_cpu_patch(void)
|
||||
}
|
||||
}
|
||||
|
||||
void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *start,
|
||||
struct sun4v_1insn_patch_entry *end)
|
||||
{
|
||||
while (start < end) {
|
||||
unsigned long addr = start->addr;
|
||||
|
||||
*(unsigned int *) (addr + 0) = start->insn;
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
|
||||
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *start,
|
||||
struct sun4v_2insn_patch_entry *end)
|
||||
{
|
||||
while (start < end) {
|
||||
unsigned long addr = start->addr;
|
||||
|
||||
*(unsigned int *) (addr + 0) = start->insns[0];
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
|
||||
|
||||
*(unsigned int *) (addr + 4) = start->insns[1];
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 4));
|
||||
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
void __init sun4v_patch(void)
|
||||
{
|
||||
extern void sun4v_hvapi_init(void);
|
||||
struct sun4v_1insn_patch_entry *p1;
|
||||
struct sun4v_2insn_patch_entry *p2;
|
||||
|
||||
if (tlb_type != hypervisor)
|
||||
return;
|
||||
|
||||
p1 = &__sun4v_1insn_patch;
|
||||
while (p1 < &__sun4v_1insn_patch_end) {
|
||||
unsigned long addr = p1->addr;
|
||||
sun4v_patch_1insn_range(&__sun4v_1insn_patch,
|
||||
&__sun4v_1insn_patch_end);
|
||||
|
||||
*(unsigned int *) (addr + 0) = p1->insn;
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
|
||||
|
||||
p1++;
|
||||
}
|
||||
|
||||
p2 = &__sun4v_2insn_patch;
|
||||
while (p2 < &__sun4v_2insn_patch_end) {
|
||||
unsigned long addr = p2->addr;
|
||||
|
||||
*(unsigned int *) (addr + 0) = p2->insns[0];
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 0));
|
||||
|
||||
*(unsigned int *) (addr + 4) = p2->insns[1];
|
||||
wmb();
|
||||
__asm__ __volatile__("flush %0" : : "r" (addr + 4));
|
||||
|
||||
p2++;
|
||||
}
|
||||
sun4v_patch_2insn_range(&__sun4v_2insn_patch,
|
||||
&__sun4v_2insn_patch_end);
|
||||
|
||||
sun4v_hvapi_init();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user