diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-02-10 16:52:55 +0100 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-02-10 17:14:49 +0100 |
commit | 9edd576d89a5b6d3e136d7dcab654d887c0d25b7 (patch) | |
tree | d19670de2256f8187321de3a41fa4a10d3c8e402 /arch/c6x/kernel/module.c | |
parent | e21af88d39796c907c38648c824be3d646ffbe35 (diff) | |
parent | 28a4d5675857f6386930a324317281cb8ed1e5d0 (diff) |
Merge remote-tracking branch 'airlied/drm-fixes' into drm-intel-next-queued
Back-merge from drm-fixes into drm-intel-next to sort out two things:
- interlaced support: -fixes contains a bugfix to correctly clear
interlaced configuration bits in case the bios sets up an interlaced
mode and we want to set up the progressive mode (current kernels
don't support interlaced). The actual feature work to support
interlaced depends upon (and conflicts with) this bugfix.
- forcewake voodoo to workaround missed IRQ issues: -fixes only enabled
this for ivybridge, but some recent bug reports indicate that we
need this on Sandybridge, too. But in a slightly different flavour
and with other fixes and reworks on top. Additionally there are some
forcewake cleanup patches heading to -next that would conflict with
currrent -fixes.
Signed-Off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'arch/c6x/kernel/module.c')
-rw-r--r-- | arch/c6x/kernel/module.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/arch/c6x/kernel/module.c b/arch/c6x/kernel/module.c new file mode 100644 index 00000000000..5fc03f18f56 --- /dev/null +++ b/arch/c6x/kernel/module.c @@ -0,0 +1,123 @@ +/* + * Port on Texas Instruments TMS320C6x architecture + * + * Copyright (C) 2005, 2009, 2010, 2011 Texas Instruments Incorporated + * Author: Thomas Charleux (thomas.charleux@jaluna.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/kernel.h> + +static inline int fixup_pcr(u32 *ip, Elf32_Addr dest, u32 maskbits, int shift) +{ + u32 opcode; + long ep = (long)ip & ~31; + long delta = ((long)dest - ep) >> 2; + long mask = (1 << maskbits) - 1; + + if ((delta >> (maskbits - 1)) == 0 || + (delta >> (maskbits - 1)) == -1) { + opcode = *ip; + opcode &= ~(mask << shift); + opcode |= ((delta & mask) << shift); + *ip = opcode; + + pr_debug("REL PCR_S%d[%p] dest[%p] opcode[%08x]\n", + maskbits, ip, (void *)dest, opcode); + + return 0; + } + pr_err("PCR_S%d reloc %p -> %p out of range!\n", + maskbits, ip, (void *)dest); + + return -1; +} + +/* + * apply a RELA relocation + */ +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + Elf32_Rela *rel = (void *) sechdrs[relsec].sh_addr; + Elf_Sym *sym; + u32 *location, opcode; + unsigned int i; + Elf32_Addr v; + Elf_Addr offset = 0; + + pr_debug("Applying relocate section %u to %u with offset 0x%x\n", + relsec, sechdrs[relsec].sh_info, offset); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset - offset; + + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + /* this is the adjustment to be made */ + v = sym->st_value + rel[i].r_addend; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_C6000_ABS32: + pr_debug("RELA ABS32: [%p] = 0x%x\n", location, v); + *location = v; + break; + case R_C6000_ABS16: + pr_debug("RELA ABS16: [%p] = 0x%x\n", location, v); + *(u16 *)location = v; + break; + case R_C6000_ABS8: + pr_debug("RELA ABS8: [%p] = 0x%x\n", location, v); + *(u8 *)location = v; + break; + case R_C6000_ABS_L16: + opcode = *location; + opcode &= ~0x7fff80; + opcode |= ((v & 0xffff) << 7); + pr_debug("RELA ABS_L16[%p] v[0x%x] opcode[0x%x]\n", + location, v, opcode); + *location = opcode; + break; + case R_C6000_ABS_H16: + opcode = *location; + opcode &= ~0x7fff80; + opcode |= ((v >> 9) & 0x7fff80); + pr_debug("RELA ABS_H16[%p] v[0x%x] opcode[0x%x]\n", + location, v, opcode); + *location = opcode; + break; + case R_C6000_PCR_S21: + if (fixup_pcr(location, v, 21, 7)) + return -ENOEXEC; + break; + case R_C6000_PCR_S12: + if (fixup_pcr(location, v, 12, 16)) + return -ENOEXEC; + break; + case R_C6000_PCR_S10: + if (fixup_pcr(location, v, 10, 13)) + return -ENOEXEC; + break; + default: + pr_err("module %s: Unknown RELA relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + + return 0; +} |