Module Name: src Committed By: martin Date: Fri Nov 7 21:28:32 UTC 2014
Modified Files: src/sys/arch/arm/arm32: kobj_machdep.c Log Message: PR port-arm/49299: add support for BE8 byte swapped instructions. To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/sys/arch/arm/arm32/kobj_machdep.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/arm32/kobj_machdep.c diff -u src/sys/arch/arm/arm32/kobj_machdep.c:1.9 src/sys/arch/arm/arm32/kobj_machdep.c:1.10 --- src/sys/arch/arm/arm32/kobj_machdep.c:1.9 Tue Aug 27 06:41:05 2013 +++ src/sys/arch/arm/arm32/kobj_machdep.c Fri Nov 7 21:28:32 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: kobj_machdep.c,v 1.9 2013/08/27 06:41:05 skrll Exp $ */ +/* $NetBSD: kobj_machdep.c,v 1.10 2014/11/07 21:28:32 martin Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -52,7 +52,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kobj_machdep.c,v 1.9 2013/08/27 06:41:05 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kobj_machdep.c,v 1.10 2014/11/07 21:28:32 martin Exp $"); #define ELFSIZE ARCH_ELFSIZE @@ -61,8 +61,12 @@ __KERNEL_RCSID(0, "$NetBSD: kobj_machdep #include <sys/kobj.h> #include <sys/exec.h> #include <sys/exec_elf.h> +#include <sys/kmem.h> +#include <sys/ksyms.h> +#include <sys/kobj_impl.h> #include <arm/cpufunc.h> +#include <arm/locore.h> int kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, @@ -203,11 +207,201 @@ kobj_reloc(kobj_t ko, uintptr_t relocbas return -1; } +#if __ARMEB__ + +enum be8_magic_sym_type { + Other, ArmStart, ThumbStart, DataStart +}; + +struct be8_marker { + enum be8_magic_sym_type type; + void *addr; +}; + +struct be8_marker_list { + size_t cnt; + struct be8_marker *markers; +}; + +/* + * See ELF for the ARM Architecture, Section 4.5.5: Mapping Symbols + * ARM reserves $a/$d/$t (and variants like $a.2) to mark start of + * arm/thumb code sections to allow conversion from ARM32-EB to -BE8 + * format. + */ +static enum be8_magic_sym_type +be8_sym_type(const char *name, int info) +{ + if (ELF_ST_BIND(info) != STB_LOCAL) + return Other; + if (ELF_ST_TYPE(info) != STT_NOTYPE) + return Other; + if (name[0] != '$' || name[1] == '\0' || + (name[2] != '\0' && name[2] != '.')) + return Other; + + switch (name[1]) { + case 'a': + return ArmStart; + case 'd': + return DataStart; + case 't': + return ThumbStart; + default: + return Other; + } +} + +static int +be8_ksym_count(const char *name, int symindex, void *value, uint32_t size, + int info, void *cookie) +{ + size_t *res = cookie; + enum be8_magic_sym_type t = be8_sym_type(name, info); + + if (t != Other) + (*res)++; + return 0; +} + +static int +be8_ksym_add(const char *name, int symindex, void *value, uint32_t size, + int info, void *cookie) +{ + size_t ndx; + struct be8_marker_list *list = cookie; + enum be8_magic_sym_type t = be8_sym_type(name, info); + + if (t == Other) + return 0; + + ndx = list->cnt++; + list->markers[ndx].type = t; + list->markers[ndx].addr = value; + + return 0; +} + +static int +be8_ksym_comp(const void *a, const void *b) +{ + const struct be8_marker *ma = a, *mb = b; + uintptr_t va = (uintptr_t)ma->addr, vb = (uintptr_t)mb->addr; + + if (va == vb) + return 0; + if (va < vb) + return -1; + return 1; +} + +static void +be8_ksym_swap(void *start, size_t size, const struct be8_marker_list *list) +{ + uintptr_t va_end = (uintptr_t)start + size; + size_t i; + uint32_t *p32, *p32_end, v32; + uint16_t *p16, *p16_end, v16; + + /* find first relevant list entry */ + for (i = 0; i < list->cnt; i++) + if (start <= list->markers[i].addr) + break; + + /* swap all arm and thumb code parts of this section */ + for ( ; i < list->cnt; i++) { + switch (list->markers[i].type) { + case ArmStart: + p32 = (uint32_t*)list->markers[i].addr; + p32_end = (uint32_t*)va_end; + if (i+1 < list->cnt) { + if ((uintptr_t)list->markers[i+1].addr + < va_end) + p32_end = (uint32_t*) + list->markers[i+1].addr; + } + while (p32 < p32_end) { + v32 = bswap32(*p32); + *p32++ = v32; + } + break; + case ThumbStart: + p16 = (uint16_t*)list->markers[i].addr; + p16_end = (uint16_t*)va_end; + if (i+1 < list->cnt) { + if ((uintptr_t)list->markers[i+1].addr + < va_end) + p16_end = (uint16_t*) + list->markers[i+1].addr; + } + while (p16 < p16_end) { + v16 = bswap16(*p16); + *p16++ = v16; + } + break; + default: + break; + } + } +} + +static void +kobj_be8_fixup(kobj_t ko) +{ + size_t relsym_cnt = 0, i, msize; + struct be8_marker_list list; + struct be8_marker tmp; + + /* + * Count all special relocations symbols + */ + ksyms_mod_foreach(ko->ko_name, be8_ksym_count, &relsym_cnt); + + /* + * Provide storage for the address list and add the symbols + */ + list.cnt = 0; + msize = relsym_cnt*sizeof(*list.markers); + list.markers = kmem_alloc(msize, KM_SLEEP); + ksyms_mod_foreach(ko->ko_name, be8_ksym_add, &list); + KASSERT(list.cnt == relsym_cnt); + + /* + * Sort symbols by ascending address + */ + if (kheapsort(list.markers, relsym_cnt, sizeof(*list.markers), + be8_ksym_comp, &tmp) != 0) + panic("could not sort be8 marker symbols"); + + /* + * Apply swaps to the .text section (XXX we do not have the + * section header available any more, it has been jetisoned + * already, so we can not check for all PROGBIT sections). + */ + for (i = 0; i < ko->ko_nprogtab; i++) { + if (strcmp(ko->ko_progtab[i].name, ".text") != 0) + continue; + be8_ksym_swap(ko->ko_progtab[i].addr, + (size_t)ko->ko_progtab[i].size, + &list); + } + + /* + * Done, free list + */ + kmem_free(list.markers, msize); +} +#endif + int kobj_machdep(kobj_t ko, void *base, size_t size, bool load) { if (load) { +#if __ARMEB__ + if (CPU_IS_ARMV7_P()) + kobj_be8_fixup(ko); +#endif #ifndef _RUMPKERNEL cpu_idcache_wbinv_range((vaddr_t)base, size); cpu_tlb_flushID();