![Bryan Wu](/assets/img/avatar_default.png)
This adds support for the Analog Devices Blackfin processor architecture, and currently supports the BF533, BF532, BF531, BF537, BF536, BF534, and BF561 (Dual Core) devices, with a variety of development platforms including those avaliable from Analog Devices (BF533-EZKit, BF533-STAMP, BF537-STAMP, BF561-EZKIT), and Bluetechnix! Tinyboards. The Blackfin architecture was jointly developed by Intel and Analog Devices Inc. (ADI) as the Micro Signal Architecture (MSA) core and introduced it in December of 2000. Since then ADI has put this core into its Blackfin processor family of devices. The Blackfin core has the advantages of a clean, orthogonal,RISC-like microprocessor instruction set. It combines a dual-MAC (Multiply/Accumulate), state-of-the-art signal processing engine and single-instruction, multiple-data (SIMD) multimedia capabilities into a single instruction-set architecture. The Blackfin architecture, including the instruction set, is described by the ADSP-BF53x/BF56x Blackfin Processor Programming Reference http://blackfin.uclinux.org/gf/download/frsrelease/29/2549/Blackfin_PRM.pdf The Blackfin processor is already supported by major releases of gcc, and there are binary and source rpms/tarballs for many architectures at: http://blackfin.uclinux.org/gf/project/toolchain/frs There is complete documentation, including "getting started" guides available at: http://docs.blackfin.uclinux.org/ which provides links to the sources and patches you will need in order to set up a cross-compiling environment for bfin-linux-uclibc This patch, as well as the other patches (toolchain, distribution, uClibc) are actively supported by Analog Devices Inc, at: http://blackfin.uclinux.org/ We have tested this on LTP, and our test plan (including pass/fails) can be found at: http://docs.blackfin.uclinux.org/doku.php?id=testing_the_linux_kernel [m.kozlowski@tuxland.pl: balance parenthesis in blackfin header files] Signed-off-by: Bryan Wu <bryan.wu@analog.com> Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl> Signed-off-by: Aubrey Li <aubrey.li@analog.com> Signed-off-by: Jie Zhang <jie.zhang@analog.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
430 lines
12 KiB
C
430 lines
12 KiB
C
/*
|
|
* File: arch/blackfin/kernel/module.c
|
|
* Based on:
|
|
* Author:
|
|
*
|
|
* Created:
|
|
* Description:
|
|
*
|
|
* Modified:
|
|
* Copyright 2004-2006 Analog Devices Inc.
|
|
*
|
|
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see the file COPYING, or write
|
|
* to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
|
|
#include <linux/moduleloader.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/string.h>
|
|
#include <linux/kernel.h>
|
|
#include <asm/dma.h>
|
|
#include <asm/cacheflush.h>
|
|
|
|
/*
|
|
* handle arithmetic relocations.
|
|
* See binutils/bfd/elf32-bfin.c for more details
|
|
*/
|
|
#define RELOC_STACK_SIZE 100
|
|
static uint32_t reloc_stack[RELOC_STACK_SIZE];
|
|
static unsigned int reloc_stack_tos;
|
|
|
|
#define is_reloc_stack_empty() ((reloc_stack_tos > 0)?0:1)
|
|
|
|
static void reloc_stack_push(uint32_t value)
|
|
{
|
|
reloc_stack[reloc_stack_tos++] = value;
|
|
}
|
|
|
|
static uint32_t reloc_stack_pop(void)
|
|
{
|
|
return reloc_stack[--reloc_stack_tos];
|
|
}
|
|
|
|
static uint32_t reloc_stack_operate(unsigned int oper, struct module *mod)
|
|
{
|
|
uint32_t value;
|
|
|
|
switch (oper) {
|
|
case R_add:
|
|
value = reloc_stack[reloc_stack_tos - 2] +
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_sub:
|
|
value = reloc_stack[reloc_stack_tos - 2] -
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_mult:
|
|
value = reloc_stack[reloc_stack_tos - 2] *
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_div:
|
|
value = reloc_stack[reloc_stack_tos - 2] /
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_mod:
|
|
value = reloc_stack[reloc_stack_tos - 2] %
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_lshift:
|
|
value = reloc_stack[reloc_stack_tos - 2] <<
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_rshift:
|
|
value = reloc_stack[reloc_stack_tos - 2] >>
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_and:
|
|
value = reloc_stack[reloc_stack_tos - 2] &
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_or:
|
|
value = reloc_stack[reloc_stack_tos - 2] |
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_xor:
|
|
value = reloc_stack[reloc_stack_tos - 2] ^
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_land:
|
|
value = reloc_stack[reloc_stack_tos - 2] &&
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_lor:
|
|
value = reloc_stack[reloc_stack_tos - 2] ||
|
|
reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 2;
|
|
break;
|
|
case R_neg:
|
|
value = -reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos--;
|
|
break;
|
|
case R_comp:
|
|
value = ~reloc_stack[reloc_stack_tos - 1];
|
|
reloc_stack_tos -= 1;
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING "module %s: unhandled reloction\n",
|
|
mod->name);
|
|
return 0;
|
|
}
|
|
|
|
/* now push the new value back on stack */
|
|
reloc_stack_push(value);
|
|
|
|
return value;
|
|
}
|
|
|
|
void *module_alloc(unsigned long size)
|
|
{
|
|
if (size == 0)
|
|
return NULL;
|
|
return vmalloc(size);
|
|
}
|
|
|
|
/* Free memory returned from module_alloc */
|
|
void module_free(struct module *mod, void *module_region)
|
|
{
|
|
vfree(module_region);
|
|
}
|
|
|
|
/* Transfer the section to the L1 memory */
|
|
int
|
|
module_frob_arch_sections(Elf_Ehdr * hdr, Elf_Shdr * sechdrs,
|
|
char *secstrings, struct module *mod)
|
|
{
|
|
Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum;
|
|
void *dest = NULL;
|
|
|
|
for (s = sechdrs; s < sechdrs_end; ++s) {
|
|
if ((strcmp(".l1.text", secstrings + s->sh_name) == 0) ||
|
|
((strcmp(".text", secstrings + s->sh_name)==0) &&
|
|
(hdr->e_flags & FLG_CODE_IN_L1) && (s->sh_size > 0))) {
|
|
mod->arch.text_l1 = s;
|
|
dest = l1_inst_sram_alloc(s->sh_size);
|
|
if (dest == NULL) {
|
|
printk(KERN_ERR
|
|
"module %s: L1 instruction memory allocation failed\n",
|
|
mod->name);
|
|
return -1;
|
|
}
|
|
dma_memcpy(dest, (void *)s->sh_addr, s->sh_size);
|
|
s->sh_flags &= ~SHF_ALLOC;
|
|
s->sh_addr = (unsigned long)dest;
|
|
}
|
|
if ((strcmp(".l1.data", secstrings + s->sh_name) == 0)||
|
|
((strcmp(".data", secstrings + s->sh_name)==0) &&
|
|
(hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) {
|
|
mod->arch.data_a_l1 = s;
|
|
dest = l1_data_sram_alloc(s->sh_size);
|
|
if (dest == NULL) {
|
|
printk(KERN_ERR
|
|
"module %s: L1 data memory allocation failed\n",
|
|
mod->name);
|
|
return -1;
|
|
}
|
|
memcpy(dest, (void *)s->sh_addr, s->sh_size);
|
|
s->sh_flags &= ~SHF_ALLOC;
|
|
s->sh_addr = (unsigned long)dest;
|
|
}
|
|
if (strcmp(".l1.bss", secstrings + s->sh_name) == 0 ||
|
|
((strcmp(".bss", secstrings + s->sh_name)==0) &&
|
|
(hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) {
|
|
mod->arch.bss_a_l1 = s;
|
|
dest = l1_data_sram_alloc(s->sh_size);
|
|
if (dest == NULL) {
|
|
printk(KERN_ERR
|
|
"module %s: L1 data memory allocation failed\n",
|
|
mod->name);
|
|
return -1;
|
|
}
|
|
memset(dest, 0, s->sh_size);
|
|
s->sh_flags &= ~SHF_ALLOC;
|
|
s->sh_addr = (unsigned long)dest;
|
|
}
|
|
if (strcmp(".l1.data.B", secstrings + s->sh_name) == 0) {
|
|
mod->arch.data_b_l1 = s;
|
|
dest = l1_data_B_sram_alloc(s->sh_size);
|
|
if (dest == NULL) {
|
|
printk(KERN_ERR
|
|
"module %s: L1 data memory allocation failed\n",
|
|
mod->name);
|
|
return -1;
|
|
}
|
|
memcpy(dest, (void *)s->sh_addr, s->sh_size);
|
|
s->sh_flags &= ~SHF_ALLOC;
|
|
s->sh_addr = (unsigned long)dest;
|
|
}
|
|
if (strcmp(".l1.bss.B", secstrings + s->sh_name) == 0) {
|
|
mod->arch.bss_b_l1 = s;
|
|
dest = l1_data_B_sram_alloc(s->sh_size);
|
|
if (dest == NULL) {
|
|
printk(KERN_ERR
|
|
"module %s: L1 data memory allocation failed\n",
|
|
mod->name);
|
|
return -1;
|
|
}
|
|
memset(dest, 0, s->sh_size);
|
|
s->sh_flags &= ~SHF_ALLOC;
|
|
s->sh_addr = (unsigned long)dest;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
apply_relocate(Elf_Shdr * sechdrs, const char *strtab,
|
|
unsigned int symindex, unsigned int relsec, struct module *me)
|
|
{
|
|
printk(KERN_ERR "module %s: .rel unsupported\n", me->name);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* FUNCTION : apply_relocate_add */
|
|
/* ABSTRACT : Blackfin specific relocation handling for the loadable */
|
|
/* modules. Modules are expected to be .o files. */
|
|
/* Arithmetic relocations are handled. */
|
|
/* We do not expect LSETUP to be split and hence is not */
|
|
/* handled. */
|
|
/* R_byte and R_byte2 are also not handled as the gas */
|
|
/* does not generate it. */
|
|
/*************************************************************************/
|
|
int
|
|
apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
|
|
unsigned int symindex, unsigned int relsec,
|
|
struct module *mod)
|
|
{
|
|
unsigned int i;
|
|
unsigned short tmp;
|
|
Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
|
|
Elf32_Sym *sym;
|
|
uint32_t *location32;
|
|
uint16_t *location16;
|
|
uint32_t value;
|
|
|
|
pr_debug("Applying relocate section %u to %u\n", relsec,
|
|
sechdrs[relsec].sh_info);
|
|
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
|
|
/* This is where to make the change */
|
|
location16 =
|
|
(uint16_t *) (sechdrs[sechdrs[relsec].sh_info].sh_addr +
|
|
rel[i].r_offset);
|
|
location32 = (uint32_t *) location16;
|
|
/* This is the symbol it is referring to. Note that all
|
|
undefined symbols have been resolved. */
|
|
sym = (Elf32_Sym *) sechdrs[symindex].sh_addr
|
|
+ ELF32_R_SYM(rel[i].r_info);
|
|
if (is_reloc_stack_empty()) {
|
|
value = sym->st_value;
|
|
} else {
|
|
value = reloc_stack_pop();
|
|
}
|
|
value += rel[i].r_addend;
|
|
pr_debug("location is %x, value is %x type is %d \n",
|
|
(unsigned int) location32, value,
|
|
ELF32_R_TYPE(rel[i].r_info));
|
|
|
|
switch (ELF32_R_TYPE(rel[i].r_info)) {
|
|
|
|
case R_pcrel24:
|
|
case R_pcrel24_jump_l:
|
|
/* Add the value, subtract its postition */
|
|
location16 =
|
|
(uint16_t *) (sechdrs[sechdrs[relsec].sh_info].
|
|
sh_addr + rel[i].r_offset - 2);
|
|
location32 = (uint32_t *) location16;
|
|
value -= (uint32_t) location32;
|
|
value >>= 1;
|
|
pr_debug("value is %x, before %x-%x after %x-%x\n", value,
|
|
*location16, *(location16 + 1),
|
|
(*location16 & 0xff00) | (value >> 16 & 0x00ff),
|
|
value & 0xffff);
|
|
*location16 =
|
|
(*location16 & 0xff00) | (value >> 16 & 0x00ff);
|
|
*(location16 + 1) = value & 0xffff;
|
|
break;
|
|
case R_pcrel12_jump:
|
|
case R_pcrel12_jump_s:
|
|
value -= (uint32_t) location32;
|
|
value >>= 1;
|
|
*location16 = (value & 0xfff);
|
|
break;
|
|
case R_pcrel10:
|
|
value -= (uint32_t) location32;
|
|
value >>= 1;
|
|
*location16 = (value & 0x3ff);
|
|
break;
|
|
case R_luimm16:
|
|
pr_debug("before %x after %x\n", *location16,
|
|
(value & 0xffff));
|
|
tmp = (value & 0xffff);
|
|
if((unsigned long)location16 >= L1_CODE_START) {
|
|
dma_memcpy(location16, &tmp, 2);
|
|
} else
|
|
*location16 = tmp;
|
|
break;
|
|
case R_huimm16:
|
|
pr_debug("before %x after %x\n", *location16,
|
|
((value >> 16) & 0xffff));
|
|
tmp = ((value >> 16) & 0xffff);
|
|
if((unsigned long)location16 >= L1_CODE_START) {
|
|
dma_memcpy(location16, &tmp, 2);
|
|
} else
|
|
*location16 = tmp;
|
|
break;
|
|
case R_rimm16:
|
|
*location16 = (value & 0xffff);
|
|
break;
|
|
case R_byte4_data:
|
|
pr_debug("before %x after %x\n", *location32, value);
|
|
*location32 = value;
|
|
break;
|
|
case R_push:
|
|
reloc_stack_push(value);
|
|
break;
|
|
case R_const:
|
|
reloc_stack_push(rel[i].r_addend);
|
|
break;
|
|
case R_add:
|
|
case R_sub:
|
|
case R_mult:
|
|
case R_div:
|
|
case R_mod:
|
|
case R_lshift:
|
|
case R_rshift:
|
|
case R_and:
|
|
case R_or:
|
|
case R_xor:
|
|
case R_land:
|
|
case R_lor:
|
|
case R_neg:
|
|
case R_comp:
|
|
reloc_stack_operate(ELF32_R_TYPE(rel[i].r_info), mod);
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "module %s: Unknown relocation: %u\n",
|
|
mod->name, ELF32_R_TYPE(rel[i].r_info));
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
module_finalize(const Elf_Ehdr * hdr,
|
|
const Elf_Shdr * sechdrs, struct module *mod)
|
|
{
|
|
unsigned int i, strindex = 0, symindex = 0;
|
|
char *secstrings;
|
|
|
|
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
|
|
|
for (i = 1; i < hdr->e_shnum; i++) {
|
|
/* Internal symbols and strings. */
|
|
if (sechdrs[i].sh_type == SHT_SYMTAB) {
|
|
symindex = i;
|
|
strindex = sechdrs[i].sh_link;
|
|
}
|
|
}
|
|
|
|
for (i = 1; i < hdr->e_shnum; i++) {
|
|
const char *strtab = (char *)sechdrs[strindex].sh_addr;
|
|
unsigned int info = sechdrs[i].sh_info;
|
|
|
|
/* Not a valid relocation section? */
|
|
if (info >= hdr->e_shnum)
|
|
continue;
|
|
|
|
if ((sechdrs[i].sh_type == SHT_RELA) &&
|
|
((strcmp(".rela.l1.text", secstrings + sechdrs[i].sh_name) == 0)||
|
|
((strcmp(".rela.text", secstrings + sechdrs[i].sh_name) == 0) &&
|
|
(hdr->e_flags & FLG_CODE_IN_L1)))) {
|
|
apply_relocate_add((Elf_Shdr *) sechdrs, strtab,
|
|
symindex, i, mod);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void module_arch_cleanup(struct module *mod)
|
|
{
|
|
if ((mod->arch.text_l1) && (mod->arch.text_l1->sh_addr))
|
|
l1_inst_sram_free((void*)mod->arch.text_l1->sh_addr);
|
|
if ((mod->arch.data_a_l1) && (mod->arch.data_a_l1->sh_addr))
|
|
l1_data_sram_free((void*)mod->arch.data_a_l1->sh_addr);
|
|
if ((mod->arch.bss_a_l1) && (mod->arch.bss_a_l1->sh_addr))
|
|
l1_data_sram_free((void*)mod->arch.bss_a_l1->sh_addr);
|
|
if ((mod->arch.data_b_l1) && (mod->arch.data_b_l1->sh_addr))
|
|
l1_data_B_sram_free((void*)mod->arch.data_b_l1->sh_addr);
|
|
if ((mod->arch.bss_b_l1) && (mod->arch.bss_b_l1->sh_addr))
|
|
l1_data_B_sram_free((void*)mod->arch.bss_b_l1->sh_addr);
|
|
}
|