diff options
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r-- | arch/powerpc/lib/Makefile | 12 | ||||
-rw-r--r-- | arch/powerpc/lib/asm-offsets.c | 21 | ||||
-rw-r--r-- | arch/powerpc/lib/bat_rw.c | 129 | ||||
-rw-r--r-- | arch/powerpc/lib/board.c | 65 | ||||
-rw-r--r-- | arch/powerpc/lib/crtsavres.S | 232 | ||||
-rw-r--r-- | arch/powerpc/lib/extable.c | 79 | ||||
-rw-r--r-- | arch/powerpc/lib/kgdb.c | 323 | ||||
-rw-r--r-- | arch/powerpc/lib/misc.S | 41 | ||||
-rw-r--r-- | arch/powerpc/lib/module.c | 294 | ||||
-rw-r--r-- | arch/powerpc/lib/ppclinux.c | 113 | ||||
-rw-r--r-- | arch/powerpc/lib/ppcstring.S | 216 | ||||
-rw-r--r-- | arch/powerpc/lib/reloc.S | 43 | ||||
-rw-r--r-- | arch/powerpc/lib/ticks.S | 38 |
13 files changed, 1606 insertions, 0 deletions
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile new file mode 100644 index 0000000000..ba2f078b62 --- /dev/null +++ b/arch/powerpc/lib/Makefile @@ -0,0 +1,12 @@ +obj-y += bat_rw.o +obj-y += board.o +obj-y += extable.o +obj-y += kgdb.o +obj-y += ppcstring.o +obj-y += ticks.o +obj-y += misc.o +obj-$(CONFIG_CMD_BOOTM) += ppclinux.o +obj-$(CONFIG_MODULES) += module.o +obj-y += crtsavres.o +obj-y += reloc.o + diff --git a/arch/powerpc/lib/asm-offsets.c b/arch/powerpc/lib/asm-offsets.c new file mode 100644 index 0000000000..bef280edd7 --- /dev/null +++ b/arch/powerpc/lib/asm-offsets.c @@ -0,0 +1,21 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + * + * 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. + */ + +#include <linux/kbuild.h> + +int main(void) +{ + return 0; +} diff --git a/arch/powerpc/lib/bat_rw.c b/arch/powerpc/lib/bat_rw.c new file mode 100644 index 0000000000..3e8882eaf4 --- /dev/null +++ b/arch/powerpc/lib/bat_rw.c @@ -0,0 +1,129 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + * + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/mmu.h> + +int write_bat (ppc_bat_t bat, unsigned long upper, unsigned long lower) +{ + switch (bat) { + case IBAT0: + mtspr (IBAT0L, lower); + mtspr (IBAT0U, upper); + break; + + case IBAT1: + mtspr (IBAT1L, lower); + mtspr (IBAT1U, upper); + break; + + case IBAT2: + mtspr (IBAT2L, lower); + mtspr (IBAT2U, upper); + break; + + case IBAT3: + mtspr (IBAT3L, lower); + mtspr (IBAT3U, upper); + break; + + case DBAT0: + mtspr (DBAT0L, lower); + mtspr (DBAT0U, upper); + break; + + case DBAT1: + mtspr (DBAT1L, lower); + mtspr (DBAT1U, upper); + break; + + case DBAT2: + mtspr (DBAT2L, lower); + mtspr (DBAT2U, upper); + break; + + case DBAT3: + mtspr (DBAT3L, lower); + mtspr (DBAT3U, upper); + break; + + default: + return (-1); + } + + return (0); +} + +int read_bat (ppc_bat_t bat, unsigned long *upper, unsigned long *lower) +{ + unsigned long register u; + unsigned long register l; + + switch (bat) { + case IBAT0: + l = mfspr (IBAT0L); + u = mfspr (IBAT0U); + break; + + case IBAT1: + l = mfspr (IBAT1L); + u = mfspr (IBAT1U); + break; + + case IBAT2: + l = mfspr (IBAT2L); + u = mfspr (IBAT2U); + break; + + case IBAT3: + l = mfspr (IBAT3L); + u = mfspr (IBAT3U); + break; + + case DBAT0: + l = mfspr (DBAT0L); + u = mfspr (DBAT0U); + break; + + case DBAT1: + l = mfspr (DBAT1L); + u = mfspr (DBAT1U); + break; + + case DBAT2: + l = mfspr (DBAT2L); + u = mfspr (DBAT2U); + break; + + case DBAT3: + l = mfspr (DBAT3L); + u = mfspr (DBAT3U); + break; + + default: + return (-1); + } + + *upper = u; + *lower = l; + + return (0); +} diff --git a/arch/powerpc/lib/board.c b/arch/powerpc/lib/board.c new file mode 100644 index 0000000000..3f13db9218 --- /dev/null +++ b/arch/powerpc/lib/board.c @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + */ + +#include <debug_ll.h> +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <memory.h> +#include <init.h> +#include <net.h> +#include <asm-generic/memory_layout.h> + +/************************************************************************ + * + * This is the next part if the initialization sequence: we are now + * running from RAM and have a "normal" C environment, i. e. global + * data can be written, BSS has been cleared, the stack size in not + * that critical any more, etc. + * + ************************************************************************ + */ + +void board_init_r (ulong end_of_ram) +{ + unsigned long malloc_end; + + asm ("sync ; isync"); + +#ifdef CONFIG_MPC85xx + _text_base = end_of_ram; +#endif + + malloc_end = (_text_base - STACK_SIZE) & ~(4095); + + debug("malloc_end: 0x%08lx\n", malloc_end); + debug("TEXT_BASE after relocation: 0x%08lx\n", _text_base); + + mem_malloc_init((void *)(malloc_end - MALLOC_SIZE), (void *)(malloc_end - 1)); + + /* + * Setup trap handlers + */ + trap_init (0); + + /* Initialization complete - start the monitor */ + + start_barebox(); +} + diff --git a/arch/powerpc/lib/crtsavres.S b/arch/powerpc/lib/crtsavres.S new file mode 100644 index 0000000000..b0fbbfc997 --- /dev/null +++ b/arch/powerpc/lib/crtsavres.S @@ -0,0 +1,232 @@ +/* + * Special support for eabi and SVR4 + * + * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. + * Copyright 2008 Freescale Semiconductor, Inc. + * Written By Michael Meissner + * + * Based on gcc/config/rs6000/crtsavres.asm from gcc + * 64 bit additions from reading the PPC elf64abi document. + * + * This file 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, or (at your option) any + * later version. + * + * In addition to the permissions in the GNU General Public License, the + * Free Software Foundation gives you unlimited permission to link the + * compiled version of this file with other programs, and to distribute + * those programs without any restriction coming from the use of this + * file. (The General Public License restrictions do apply in other + * respects; for example, they cover modification of the file, and + * distribution when not linked into another program.) + * + * This file 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. + * + * As a special exception, if you link this library with files + * compiled with GCC to produce an executable, this does not cause + * the resulting executable to be covered by the GNU General Public License. + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + */ + +#include <linux/stringify.h> +#include <asm/ppc_asm.tmpl> + +#define N_FUN 36 + +#define _GLOBAL(n) \ + .text; \ + .stabs __stringify(n:F-1),N_FUN,0,0,n;\ + .globl n; \ +n: + + .file "crtsavres.S" + .section ".text" + +/* Routines for saving integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer save area. */ + +_GLOBAL(_savegpr_14) +_GLOBAL(_save32gpr_14) + stw 14,-72(11) /* save gp registers */ +_GLOBAL(_savegpr_15) +_GLOBAL(_save32gpr_15) + stw 15,-68(11) +_GLOBAL(_savegpr_16) +_GLOBAL(_save32gpr_16) + stw 16,-64(11) +_GLOBAL(_savegpr_17) +_GLOBAL(_save32gpr_17) + stw 17,-60(11) +_GLOBAL(_savegpr_18) +_GLOBAL(_save32gpr_18) + stw 18,-56(11) +_GLOBAL(_savegpr_19) +_GLOBAL(_save32gpr_19) + stw 19,-52(11) +_GLOBAL(_savegpr_20) +_GLOBAL(_save32gpr_20) + stw 20,-48(11) +_GLOBAL(_savegpr_21) +_GLOBAL(_save32gpr_21) + stw 21,-44(11) +_GLOBAL(_savegpr_22) +_GLOBAL(_save32gpr_22) + stw 22,-40(11) +_GLOBAL(_savegpr_23) +_GLOBAL(_save32gpr_23) + stw 23,-36(11) +_GLOBAL(_savegpr_24) +_GLOBAL(_save32gpr_24) + stw 24,-32(11) +_GLOBAL(_savegpr_25) +_GLOBAL(_save32gpr_25) + stw 25,-28(11) +_GLOBAL(_savegpr_26) +_GLOBAL(_save32gpr_26) + stw 26,-24(11) +_GLOBAL(_savegpr_27) +_GLOBAL(_save32gpr_27) + stw 27,-20(11) +_GLOBAL(_savegpr_28) +_GLOBAL(_save32gpr_28) + stw 28,-16(11) +_GLOBAL(_savegpr_29) +_GLOBAL(_save32gpr_29) + stw 29,-12(11) +_GLOBAL(_savegpr_30) +_GLOBAL(_save32gpr_30) + stw 30,-8(11) +_GLOBAL(_savegpr_31) +_GLOBAL(_save32gpr_31) + stw 31,-4(11) + blr + +/* Routines for restoring integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer restore area. */ + +_GLOBAL(_restgpr_14) +_GLOBAL(_rest32gpr_14) + lwz 14,-72(11) /* restore gp registers */ +_GLOBAL(_restgpr_15) +_GLOBAL(_rest32gpr_15) + lwz 15,-68(11) +_GLOBAL(_restgpr_16) +_GLOBAL(_rest32gpr_16) + lwz 16,-64(11) +_GLOBAL(_restgpr_17) +_GLOBAL(_rest32gpr_17) + lwz 17,-60(11) +_GLOBAL(_restgpr_18) +_GLOBAL(_rest32gpr_18) + lwz 18,-56(11) +_GLOBAL(_restgpr_19) +_GLOBAL(_rest32gpr_19) + lwz 19,-52(11) +_GLOBAL(_restgpr_20) +_GLOBAL(_rest32gpr_20) + lwz 20,-48(11) +_GLOBAL(_restgpr_21) +_GLOBAL(_rest32gpr_21) + lwz 21,-44(11) +_GLOBAL(_restgpr_22) +_GLOBAL(_rest32gpr_22) + lwz 22,-40(11) +_GLOBAL(_restgpr_23) +_GLOBAL(_rest32gpr_23) + lwz 23,-36(11) +_GLOBAL(_restgpr_24) +_GLOBAL(_rest32gpr_24) + lwz 24,-32(11) +_GLOBAL(_restgpr_25) +_GLOBAL(_rest32gpr_25) + lwz 25,-28(11) +_GLOBAL(_restgpr_26) +_GLOBAL(_rest32gpr_26) + lwz 26,-24(11) +_GLOBAL(_restgpr_27) +_GLOBAL(_rest32gpr_27) + lwz 27,-20(11) +_GLOBAL(_restgpr_28) +_GLOBAL(_rest32gpr_28) + lwz 28,-16(11) +_GLOBAL(_restgpr_29) +_GLOBAL(_rest32gpr_29) + lwz 29,-12(11) +_GLOBAL(_restgpr_30) +_GLOBAL(_rest32gpr_30) + lwz 30,-8(11) +_GLOBAL(_restgpr_31) +_GLOBAL(_rest32gpr_31) + lwz 31,-4(11) + blr + +/* Routines for restoring integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer restore area. */ + +_GLOBAL(_restgpr_14_x) +_GLOBAL(_rest32gpr_14_x) + lwz 14,-72(11) /* restore gp registers */ +_GLOBAL(_restgpr_15_x) +_GLOBAL(_rest32gpr_15_x) + lwz 15,-68(11) +_GLOBAL(_restgpr_16_x) +_GLOBAL(_rest32gpr_16_x) + lwz 16,-64(11) +_GLOBAL(_restgpr_17_x) +_GLOBAL(_rest32gpr_17_x) + lwz 17,-60(11) +_GLOBAL(_restgpr_18_x) +_GLOBAL(_rest32gpr_18_x) + lwz 18,-56(11) +_GLOBAL(_restgpr_19_x) +_GLOBAL(_rest32gpr_19_x) + lwz 19,-52(11) +_GLOBAL(_restgpr_20_x) +_GLOBAL(_rest32gpr_20_x) + lwz 20,-48(11) +_GLOBAL(_restgpr_21_x) +_GLOBAL(_rest32gpr_21_x) + lwz 21,-44(11) +_GLOBAL(_restgpr_22_x) +_GLOBAL(_rest32gpr_22_x) + lwz 22,-40(11) +_GLOBAL(_restgpr_23_x) +_GLOBAL(_rest32gpr_23_x) + lwz 23,-36(11) +_GLOBAL(_restgpr_24_x) +_GLOBAL(_rest32gpr_24_x) + lwz 24,-32(11) +_GLOBAL(_restgpr_25_x) +_GLOBAL(_rest32gpr_25_x) + lwz 25,-28(11) +_GLOBAL(_restgpr_26_x) +_GLOBAL(_rest32gpr_26_x) + lwz 26,-24(11) +_GLOBAL(_restgpr_27_x) +_GLOBAL(_rest32gpr_27_x) + lwz 27,-20(11) +_GLOBAL(_restgpr_28_x) +_GLOBAL(_rest32gpr_28_x) + lwz 28,-16(11) +_GLOBAL(_restgpr_29_x) +_GLOBAL(_rest32gpr_29_x) + lwz 29,-12(11) +_GLOBAL(_restgpr_30_x) +_GLOBAL(_rest32gpr_30_x) + lwz 30,-8(11) +_GLOBAL(_restgpr_31_x) +_GLOBAL(_rest32gpr_31_x) + lwz 0,4(11) + lwz 31,-4(11) + mtlr 0 + mr 1,11 + blr + diff --git a/arch/powerpc/lib/extable.c b/arch/powerpc/lib/extable.c new file mode 100644 index 0000000000..1e9ad1e63c --- /dev/null +++ b/arch/powerpc/lib/extable.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + */ +#include <common.h> + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +int ex_tab_message = 1; + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ex_tab_message) + printf("Bus Fault @ 0x%08lx, fixup 0x%08lx\n", addr, ret); + if (ret) return ret; + + return 0; +} diff --git a/arch/powerpc/lib/kgdb.c b/arch/powerpc/lib/kgdb.c new file mode 100644 index 0000000000..762cc7a88c --- /dev/null +++ b/arch/powerpc/lib/kgdb.c @@ -0,0 +1,323 @@ +#include <common.h> +#include <command.h> + +#ifdef CONFIG_KGDB + +#include <kgdb.h> +#include <asm/signal.h> +#include <asm/processor.h> + +#define PC_REGNUM 64 +#define SP_REGNUM 1 + +void breakinst(void); + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} + +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} + +static inline unsigned long +get_msr(void) +{ + unsigned long msr; + asm volatile("mfmsr %0" : "=r" (msr):); + return msr; +} + +static inline void +set_msr(unsigned long msr) +{ + asm volatile("mtmsr %0" : : "r" (msr)); +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int +computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +void +kgdb_enter(struct pt_regs *regs, kgdb_data *kdp) +{ + unsigned long msr; + + kdp->private[0] = msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + regs->msr &= ~MSR_SE; + + /* reply to host that an exception has occurred */ + kdp->sigval = computeSignal(regs->trap); + + kdp->nregs = 2; + + kdp->regs[0].num = PC_REGNUM; + kdp->regs[0].val = regs->nip; + + kdp->regs[1].num = SP_REGNUM; + kdp->regs[1].val = regs->gpr[SP_REGNUM]; +} + +void +kgdb_exit(struct pt_regs *regs, kgdb_data *kdp) +{ + unsigned long msr = kdp->private[0]; + + if (kdp->extype & KGDBEXIT_WITHADDR) + regs->nip = kdp->exaddr; + + switch (kdp->extype & KGDBEXIT_TYPEMASK) { + + case KGDBEXIT_KILL: + case KGDBEXIT_CONTINUE: + set_msr(msr); + break; + + case KGDBEXIT_SINGLE: + regs->msr |= MSR_SE; + break; + } +} + +int +kgdb_trap(struct pt_regs *regs) +{ + return (regs->trap); +} + +/* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + +#define SPACE_REQUIRED ((32*4)+(32*8)+(6*4)) + +#ifdef CONFIG_8260 +/* store floating double indexed */ +#define STFDI(n,p) __asm__ __volatile__ ("stfd " #n ",%0" : "=o"(p[2*n])) +/* store floating double multiple */ +#define STFDM(p) { STFDI( 0,p); STFDI( 1,p); STFDI( 2,p); STFDI( 3,p); \ + STFDI( 4,p); STFDI( 5,p); STFDI( 6,p); STFDI( 7,p); \ + STFDI( 8,p); STFDI( 9,p); STFDI(10,p); STFDI(11,p); \ + STFDI(12,p); STFDI(13,p); STFDI(14,p); STFDI(15,p); \ + STFDI(16,p); STFDI(17,p); STFDI(18,p); STFDI(19,p); \ + STFDI(20,p); STFDI(21,p); STFDI(22,p); STFDI(23,p); \ + STFDI(24,p); STFDI(25,p); STFDI(26,p); STFDI(27,p); \ + STFDI(28,p); STFDI(29,p); STFDI(30,p); STFDI(31,p); } +#endif + +int +kgdb_getregs(struct pt_regs *regs, char *buf, int max) +{ + int i; + unsigned long *ptr = (unsigned long *)buf; + + if (max < SPACE_REQUIRED) + kgdb_error(KGDBERR_NOSPACE); + + if ((unsigned long)ptr & 3) + kgdb_error(KGDBERR_ALIGNFAULT); + + /* General Purpose Regs */ + for (i = 0; i < 32; i++) + *ptr++ = regs->gpr[i]; + + /* Floating Point Regs */ +#ifdef CONFIG_8260 + STFDM(ptr); + ptr += 32*2; +#else + for (i = 0; i < 32; i++) { + *ptr++ = 0; + *ptr++ = 0; + } +#endif + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + *ptr++ = regs->nip; + *ptr++ = regs->msr; + *ptr++ = regs->ccr; + *ptr++ = regs->link; + *ptr++ = regs->ctr; + *ptr++ = regs->xer; + + return (SPACE_REQUIRED); +} + +/* set the value of the CPU registers */ + +#ifdef CONFIG_8260 +/* load floating double */ +#define LFD(n,v) __asm__ __volatile__ ("lfd " #n ",%0" :: "o"(v)) +/* load floating double indexed */ +#define LFDI(n,p) __asm__ __volatile__ ("lfd " #n ",%0" :: "o"((p)[2*n])) +/* load floating double multiple */ +#define LFDM(p) { LFDI( 0,p); LFDI( 1,p); LFDI( 2,p); LFDI( 3,p); \ + LFDI( 4,p); LFDI( 5,p); LFDI( 6,p); LFDI( 7,p); \ + LFDI( 8,p); LFDI( 9,p); LFDI(10,p); LFDI(11,p); \ + LFDI(12,p); LFDI(13,p); LFDI(14,p); LFDI(15,p); \ + LFDI(16,p); LFDI(17,p); LFDI(18,p); LFDI(19,p); \ + LFDI(20,p); LFDI(21,p); LFDI(22,p); LFDI(23,p); \ + LFDI(24,p); LFDI(25,p); LFDI(26,p); LFDI(27,p); \ + LFDI(28,p); LFDI(29,p); LFDI(30,p); LFDI(31,p); } +#endif + +void +kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length) +{ + unsigned long *ptr = (unsigned long *)buf; + + if (regno < 0 || regno >= 70) + kgdb_error(KGDBERR_BADPARAMS); + else if (regno >= 32 && regno < 64) { + if (length < 8) + kgdb_error(KGDBERR_NOSPACE); + } + else { + if (length < 4) + kgdb_error(KGDBERR_NOSPACE); + } + + if ((unsigned long)ptr & 3) + kgdb_error(KGDBERR_ALIGNFAULT); + + if (regno >= 0 && regno < 32) + regs->gpr[regno] = *ptr; + else switch (regno) { + +#ifdef CONFIG_8260 +#define caseF(n) \ + case (n) + 32: LFD(n, *ptr); break; + +caseF( 0) caseF( 1) caseF( 2) caseF( 3) caseF( 4) caseF( 5) caseF( 6) caseF( 7) +caseF( 8) caseF( 9) caseF(10) caseF(11) caseF(12) caseF(13) caseF(14) caseF(15) +caseF(16) caseF(17) caseF(18) caseF(19) caseF(20) caseF(21) caseF(22) caseF(23) +caseF(24) caseF(25) caseF(26) caseF(27) caseF(28) caseF(29) caseF(30) caseF(31) + +#undef caseF +#endif + + case 64: regs->nip = *ptr; break; + case 65: regs->msr = *ptr; break; + case 66: regs->ccr = *ptr; break; + case 67: regs->link = *ptr; break; + case 68: regs->ctr = *ptr; break; + case 69: regs->ctr = *ptr; break; + + default: + kgdb_error(KGDBERR_BADPARAMS); + } +} + +void +kgdb_putregs(struct pt_regs *regs, char *buf, int length) +{ + int i; + unsigned long *ptr = (unsigned long *)buf; + + if (length < SPACE_REQUIRED) + kgdb_error(KGDBERR_NOSPACE); + + if ((unsigned long)ptr & 3) + kgdb_error(KGDBERR_ALIGNFAULT); + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + for (i = 0; i < 32; i++) + regs->gpr[i] = *ptr++; + + /* Floating Point Regs */ +#ifdef CONFIG_8260 + LFDM(ptr); +#endif + ptr += 32*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + regs->nip = *ptr++; + regs->msr = *ptr++; + regs->ccr = *ptr++; + regs->link = *ptr++; + regs->ctr = *ptr++; + regs->xer = *ptr++; +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +kgdb_breakpoint(int argc, char *argv[]) +{ + asm(" .globl breakinst\n\ + breakinst: .long 0x7d821008\n\ + "); +} + +#endif /* CFG_CMD_KGDB */ diff --git a/arch/powerpc/lib/misc.S b/arch/powerpc/lib/misc.S new file mode 100644 index 0000000000..bd06ff2062 --- /dev/null +++ b/arch/powerpc/lib/misc.S @@ -0,0 +1,41 @@ + +#include <asm/ppc_asm.tmpl> +#include <asm-generic/errno.h> + + .globl __ashrdi3 +__ashrdi3: + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + rlwinm r8,r7,0,32 # t3 = (count < 32) ? 32 : 0 + sraw r7,r3,r7 # t2 = MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + slw r7,r7,r8 # t2 = (count < 32) ? 0 : t2 + sraw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr + + .globl __ashldi3 +__ashldi3: + subfic r6,r5,32 + slw r3,r3,r5 # MSW = count > 31 ? 0 : MSW << count + addi r7,r5,32 # could be xori, or addi with -32 + srw r6,r4,r6 # t1 = count > 31 ? 0 : LSW >> (32-count) + slw r7,r4,r7 # t2 = count < 32 ? 0 : LSW << (count-32) + or r3,r3,r6 # MSW |= t1 + slw r4,r4,r5 # LSW = LSW << count + or r3,r3,r7 # MSW |= t2 + blr + + .globl __lshrdi3 +__lshrdi3: + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + srw r7,r3,r7 # t2 = count < 32 ? 0 : MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + srw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr diff --git a/arch/powerpc/lib/module.c b/arch/powerpc/lib/module.c new file mode 100644 index 0000000000..9ec6095571 --- /dev/null +++ b/arch/powerpc/lib/module.c @@ -0,0 +1,294 @@ +/* Kernel module help for PPC. + Copyright (C) 2001 Rusty Russell. + + 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. + +*/ + +#define DEBUG + +#include <common.h> +#include <elf.h> +#include <module.h> +#include <malloc.h> +#include <errno.h> + +//#include "setup.h" + +//LIST_HEAD(module_bug_list); + +/* Count how many different relocations (different symbol, different + addend) */ +static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) +{ + unsigned int i, j, ret = 0; + + /* Sure, this is order(n^2), but it's usually short, and not + time critical */ + for (i = 0; i < num; i++) { + for (j = 0; j < i; j++) { + /* If this addend appeared before, it's + already been counted */ + if (ELF32_R_SYM(rela[i].r_info) + == ELF32_R_SYM(rela[j].r_info) + && rela[i].r_addend == rela[j].r_addend) + break; + } + if (j == i) ret++; + } + return ret; +} + +#if 0 +/* Get the potential trampolines size required of the init and + non-init sections */ +static unsigned long get_plt_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + int is_init) +{ + unsigned long ret = 0; + unsigned i; + + /* Everything marked ALLOC (this includes the exported + symbols) */ + for (i = 1; i < hdr->e_shnum; i++) { + /* If it's called *.init*, and we're not init, we're + not interested */ + if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) + != is_init) + continue; + + /* We don't want to look at debug sections. */ + if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != 0) + continue; + + if (sechdrs[i].sh_type == SHT_RELA) { + debug("Found relocations in section %u\n", i); + debug("Ptr: %p. Number: %u\n", + (void *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size / sizeof(Elf32_Rela)); + ret += count_relocs((void *)hdr + + sechdrs[i].sh_offset, + sechdrs[i].sh_size + / sizeof(Elf32_Rela)) + * sizeof(struct ppc_plt_entry); + } + } + + return ret; +} +#endif +#if 0 +int module_frob_arch_sections(Elf32_Ehdr *hdr, + Elf32_Shdr *sechdrs, + char *secstrings, + struct module *me) +{ + unsigned int i; + + /* Find .plt and .init.plt sections */ + for (i = 0; i < hdr->e_shnum; i++) { + if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) + me->arch.init_plt_section = i; + else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) + me->arch.core_plt_section = i; + } + if (!me->arch.core_plt_section || !me->arch.init_plt_section) { + printf("Module doesn't contain .plt or .init.plt sections.\n"); + return -ENOEXEC; + } + + /* Override their sizes */ + sechdrs[me->arch.core_plt_section].sh_size + = get_plt_size(hdr, sechdrs, secstrings, 0); + sechdrs[me->arch.init_plt_section].sh_size + = get_plt_size(hdr, sechdrs, secstrings, 1); + return 0; +} +#endif + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + printf("%s: Non-ADD RELOCATION unsupported\n", + module->name); + return -ENOEXEC; +} + +static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) +{ + if (entry->jump[0] == 0x3d600000 + ((val + 0x8000) >> 16) + && entry->jump[1] == 0x396b0000 + (val & 0xffff)) + return 1; + return 0; +} + +/* Set up a trampoline in the PLT to bounce us to the distant function */ +static uint32_t do_plt_call(void *location, + Elf32_Addr val, + Elf32_Shdr *sechdrs, + struct module *mod) +{ + struct ppc_plt_entry *entry; + + debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); + /* Init, or core PLT? */ + if (location >= mod->module_core + && location < mod->module_core + mod->core_size) + entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; + else + entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; + + /* Find this entry, or if that fails, the next avail. entry */ + while (entry->jump[0]) { + if (entry_matches(entry, val)) return (uint32_t)entry; + entry++; + } + + /* Stolen from Paul Mackerras as well... */ + entry->jump[0] = 0x3d600000+((val+0x8000)>>16); /* lis r11,sym@ha */ + entry->jump[1] = 0x396b0000 + (val&0xffff); /* addi r11,r11,sym@l*/ + entry->jump[2] = 0x7d6903a6; /* mtctr r11 */ + entry->jump[3] = 0x4e800420; /* bctr */ + + debug("Initialized plt for 0x%x at %p\n", val, entry); + return (uint32_t)entry; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + uint32_t value; + + debug("Applying ADD relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rela[i].r_offset; + /* 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(rela[i].r_info); + /* `Everything is relative'. */ + value = sym->st_value + rela[i].r_addend; + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_PPC_ADDR32: + /* Simply set it */ + *(uint32_t *)location = value; + break; + + case R_PPC_ADDR16_LO: + /* Low half of the symbol */ + *(uint16_t *)location = value; + break; + + case R_PPC_ADDR16_HI: + /* Higher half of the symbol */ + *(uint16_t *)location = (value >> 16); + break; + + case R_PPC_ADDR16_HA: + /* Sign-adjusted lower 16 bits: PPC ELF ABI says: + (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. + This is the same, only sane. + */ + *(uint16_t *)location = (value + 0x8000) >> 16; + break; + + case R_PPC_REL24: + if ((int)(value - (uint32_t)location) < -0x02000000 + || (int)(value - (uint32_t)location) >= 0x02000000) + value = do_plt_call(location, value, + sechdrs, module); + + /* Only replace bits 2 through 26 */ + debug("REL24 value = %08X. location = %08X\n", + value, (uint32_t)location); + debug("Location before: %08X.\n", + *(uint32_t *)location); + *(uint32_t *)location + = (*(uint32_t *)location & ~0x03fffffc) + | ((value - (uint32_t)location) + & 0x03fffffc); + debug("Location after: %08X.\n", + *(uint32_t *)location); + debug("ie. jump to %08X+%08X = %08X\n", + *(uint32_t *)location & 0x03fffffc, + (uint32_t)location, + (*(uint32_t *)location & 0x03fffffc) + + (uint32_t)location); + break; + + case R_PPC_REL32: + /* 32-bit relative jump. */ + *(uint32_t *)location = value - (uint32_t)location; + break; + + default: + printf("%s: unknown ADD relocation: %u\n", + module->name, + ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + const char *name) +{ + char *secstrings; + unsigned int i; + + secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + for (i = 1; i < hdr->e_shnum; i++) + if (strcmp(secstrings+sechdrs[i].sh_name, name) == 0) + return &sechdrs[i]; + return NULL; +} + +#if 0 +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + const Elf_Shdr *sect; + int err; + + err = module_bug_finalize(hdr, sechdrs, me); + if (err) /* never true, currently */ + return err; + + /* Apply feature fixups */ + sect = find_section(hdr, sechdrs, "__ftr_fixup"); + if (sect != NULL) + do_feature_fixups(cur_cpu_spec->cpu_features, + (void *)sect->sh_addr, + (void *)sect->sh_addr + sect->sh_size); + + return 0; +} +#endif + diff --git a/arch/powerpc/lib/ppclinux.c b/arch/powerpc/lib/ppclinux.c new file mode 100644 index 0000000000..05c29be7da --- /dev/null +++ b/arch/powerpc/lib/ppclinux.c @@ -0,0 +1,113 @@ +#define DEBUG + +#include <common.h> +#include <command.h> +#include <image.h> +#include <init.h> +#include <malloc.h> +#include <environment.h> +#include <asm/bitops.h> +#include <asm/processor.h> +#include <boot.h> +#include <bootm.h> +#include <errno.h> +#include <restart.h> +#include <fs.h> + +static struct fdt_header *bootm_relocate_fdt(struct image_data *data, + struct fdt_header *fdt) +{ + void *os = (void *)data->os_address; + void *newfdt; + + if (os < LINUX_TLB1_MAX_ADDR) { + /* The kernel is within the boot TLB mapping. + * Put the DTB above if there is no space + * below. + */ + if (os < (void *)fdt->totalsize) { + os = (void *)PAGE_ALIGN((phys_addr_t)os + + data->os->header.ih_size); + os += fdt->totalsize; + if (os < LINUX_TLB1_MAX_ADDR) + os = LINUX_TLB1_MAX_ADDR; + } + } + + if (os > LINUX_TLB1_MAX_ADDR) { + pr_crit("Unable to relocate DTB to Linux TLB\n"); + return NULL; + } + + newfdt = (void *)PAGE_ALIGN_DOWN((phys_addr_t)os - fdt->totalsize); + memcpy(newfdt, fdt, fdt->totalsize); + free(fdt); + + pr_info("Relocating device tree to 0x%p\n", newfdt); + return newfdt; +} + +static int do_bootm_linux(struct image_data *data) +{ + void (*kernel)(void *, void *, unsigned long, + unsigned long, unsigned long); + int ret; + struct fdt_header *fdt; + + ret = bootm_load_os(data, data->os_address); + if (ret) + return ret; + + fdt = of_get_fixed_tree(data->of_root_node); + if (!fdt) { + pr_err("bootm: No devicetree given.\n"); + return -EINVAL; + } + + if (data->dryrun) + return 0; + + /* Relocate the device tree if outside the initial + * Linux mapped TLB. + */ + if (IS_ENABLED(CONFIG_MPC85xx)) { + if (((void *)fdt + fdt->totalsize) > LINUX_TLB1_MAX_ADDR) { + fdt = bootm_relocate_fdt(data, fdt); + if (!fdt) + goto error; + } + } + + fdt_add_reserve_map(fdt); + + kernel = (void *)(data->os_address + data->os_entry); + + /* + * Linux Kernel Parameters (passing device tree): + * r3: ptr to OF flat tree, followed by the board info data + * r4: physical pointer to the kernel itself + * r5: NULL + * r6: NULL + * r7: NULL + */ + kernel(fdt, kernel, 0, 0, 0); + + restart_machine(); + +error: + return -1; +} + +static struct image_handler handler = { + .name = "PowerPC Linux", + .bootm = do_bootm_linux, + .filetype = filetype_uimage, + .ih_os = IH_OS_LINUX, +}; + +static int ppclinux_register_image_handler(void) +{ + return register_image_handler(&handler); +} + +late_initcall(ppclinux_register_image_handler); diff --git a/arch/powerpc/lib/ppcstring.S b/arch/powerpc/lib/ppcstring.S new file mode 100644 index 0000000000..cce85e3252 --- /dev/null +++ b/arch/powerpc/lib/ppcstring.S @@ -0,0 +1,216 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * 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. + */ +#include <asm/ppc_asm.tmpl> +#include <asm-generic/errno.h> + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + + .global memchr +memchr: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr diff --git a/arch/powerpc/lib/reloc.S b/arch/powerpc/lib/reloc.S new file mode 100644 index 0000000000..7ef2ea3340 --- /dev/null +++ b/arch/powerpc/lib/reloc.S @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 Wolfgang Denk <wd@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + */ + +#include <asm/ppc_asm.tmpl> + + .file "reloc.S" + + .text + /* + * Function: relocate entries for one exception vector + */ + .globl trap_reloc + .type trap_reloc, @function +trap_reloc: + lwz r0, 0(r7) /* hdlr ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 0(r7) + + lwz r0, 4(r7) /* int_return ... */ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 4(r7) + + lwz r0, 8(r7) /* transfer_to_handler ...*/ + add r0, r0, r3 /* ... += dest_addr */ + stw r0, 8(r7) + + blr + .size trap_reloc, .-trap_reloc diff --git a/arch/powerpc/lib/ticks.S b/arch/powerpc/lib/ticks.S new file mode 100644 index 0000000000..a41fe8b781 --- /dev/null +++ b/arch/powerpc/lib/ticks.S @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2000, 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * base on code by + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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. + * + */ + +#include <asm/ppc_asm.tmpl> +#include <asm/ppc_defs.h> +#include <config.h> + +/* + * unsigned long long get_ticks(void); + * + * read timebase as "long long" + */ + .globl get_ticks +get_ticks: +1: mftbu r3 + mftb r4 + mftbu r5 + cmp 0,r3,r5 + bne 1b + blr |