From: Ard Biesheuvel <ard.biesheuvel@linaro.org>

Date: Mon, 11 Aug 2014 15:39:16 +0200
Subject: [PATCH] Add support for 32-bit ARM

This adds support for 32-bit ARM using an approach similar to the one used for
64-bit ARM (AArch64), i.e., it does not rely on an objcopy that is aware of EFI
or PE/COFF, but lays out the entire PE/COFF header using the assembler.

In the 32-bit ARM case (which does not have a division instruction), some code
has been imported from the Linux kernel to perform the division operations in
software.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Nigel Croxon <nigel.croxon@hp.com>
This commit is contained in:
Nigel Croxon 2014-08-19 12:07:00 -04:00
parent b28143d4fb
commit 16d65c0669
13 changed files with 1046 additions and 1 deletions

View File

@ -112,8 +112,18 @@ endif
# otherwise we need to compose the PE/COFF header using the assembler
#
ifneq ($(ARCH),aarch64)
ifneq ($(ARCH),arm)
export HAVE_EFI_OBJCOPY=y
endif
endif
ifneq ($(ARCH),arm)
export LIBGCC=$(shell $(CC) $(ARCH3264) -print-libgcc-file-name)
endif
ifeq ($(ARCH),arm)
CFLAGS += -marm
endif
# Generic compilation flags
INCDIR += -I$(SRCDIR) -I$(TOPDIR)/inc -I$(TOPDIR)/inc/$(ARCH) \

View File

@ -55,7 +55,7 @@ endif
LDFLAGS += -shared -Bsymbolic -L../lib -L../gnuefi $(CRTOBJS)
LOADLIBES += -lefi -lgnuefi
LOADLIBES += $(shell $(CC) $(ARCH3264) -print-libgcc-file-name)
LOADLIBES += $(LIBGCC)
LOADLIBES += -T $(LDSCRIPT)
TARGET_APPS = t.efi t2.efi t3.efi t4.efi t5.efi t6.efi \

View File

@ -0,0 +1,138 @@
/*
* crt0-efi-arm.S - PE/COFF header for ARM EFI applications
*
* Copright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.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.
*/
.section .text.head
/*
* Magic "MZ" signature for PE/COFF
*/
.globl ImageBase
ImageBase:
.ascii "MZ"
.skip 58 // 'MZ' + pad + offset == 64
.long pe_header - ImageBase // Offset to the PE header.
pe_header:
.ascii "PE"
.short 0
coff_header:
.short 0x1c2 // Mixed ARM/Thumb
.short 2 // nr_sections
.long 0 // TimeDateStamp
.long 0 // PointerToSymbolTable
.long 1 // NumberOfSymbols
.short section_table - optional_header // SizeOfOptionalHeader
.short 0x306 // Characteristics.
// IMAGE_FILE_32BIT_MACHINE |
// IMAGE_FILE_DEBUG_STRIPPED |
// IMAGE_FILE_EXECUTABLE_IMAGE |
// IMAGE_FILE_LINE_NUMS_STRIPPED
optional_header:
.short 0x10b // PE32+ format
.byte 0x02 // MajorLinkerVersion
.byte 0x14 // MinorLinkerVersion
.long _edata - _start // SizeOfCode
.long 0 // SizeOfInitializedData
.long 0 // SizeOfUninitializedData
.long _start - ImageBase // AddressOfEntryPoint
.long _start - ImageBase // BaseOfCode
.long 0 // BaseOfData
extra_header_fields:
.long 0 // ImageBase
.long 0x20 // SectionAlignment
.long 0x8 // FileAlignment
.short 0 // MajorOperatingSystemVersion
.short 0 // MinorOperatingSystemVersion
.short 0 // MajorImageVersion
.short 0 // MinorImageVersion
.short 0 // MajorSubsystemVersion
.short 0 // MinorSubsystemVersion
.long 0 // Win32VersionValue
.long _edata - ImageBase // SizeOfImage
// Everything before the kernel image is considered part of the header
.long _start - ImageBase // SizeOfHeaders
.long 0 // CheckSum
.short EFI_SUBSYSTEM // Subsystem
.short 0 // DllCharacteristics
.long 0 // SizeOfStackReserve
.long 0 // SizeOfStackCommit
.long 0 // SizeOfHeapReserve
.long 0 // SizeOfHeapCommit
.long 0 // LoaderFlags
.long 0x6 // NumberOfRvaAndSizes
.quad 0 // ExportTable
.quad 0 // ImportTable
.quad 0 // ResourceTable
.quad 0 // ExceptionTable
.quad 0 // CertificationTable
.quad 0 // BaseRelocationTable
// Section table
section_table:
/*
* The EFI application loader requires a relocation section
* because EFI applications must be relocatable. This is a
* dummy section as far as we are concerned.
*/
.ascii ".reloc"
.byte 0
.byte 0 // end of 0 padding of section name
.long 0
.long 0
.long 0 // SizeOfRawData
.long 0 // PointerToRawData
.long 0 // PointerToRelocations
.long 0 // PointerToLineNumbers
.short 0 // NumberOfRelocations
.short 0 // NumberOfLineNumbers
.long 0x42100040 // Characteristics (section flags)
.ascii ".text"
.byte 0
.byte 0
.byte 0 // end of 0 padding of section name
.long _edata - _start // VirtualSize
.long _start - ImageBase // VirtualAddress
.long _edata - _start // SizeOfRawData
.long _start - ImageBase // PointerToRawData
.long 0 // PointerToRelocations (0 for executables)
.long 0 // PointerToLineNumbers (0 for executables)
.short 0 // NumberOfRelocations (0 for executables)
.short 0 // NumberOfLineNumbers (0 for executables)
.long 0xe0500020 // Characteristics (section flags)
_start:
stmfd sp!, {r0-r2, lr}
mov r2, r0
mov r3, r1
adr r1, .L_DYNAMIC
ldr r0, [r1]
add r1, r0, r1
adr r0, ImageBase
bl _relocate
teq r0, #0
bne 0f
ldmfd sp, {r0-r1}
bl efi_main
0: add sp, sp, #12
ldr pc, [sp], #4
.L_DYNAMIC:
.word _DYNAMIC - .

View File

@ -0,0 +1,57 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.text 0x0 : {
*(.text.head)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.srodata)
*(.rodata*)
. = ALIGN(16);
_etext = .;
}
.dynamic : { *(.dynamic) }
.data :
{
*(.sdata)
*(.data)
*(.data1)
*(.data)
*(.got.plt)
*(.got)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
. = ALIGN(16);
_bss = .;
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
. = ALIGN(16);
_bss_end = .;
}
.rel.dyn : { *(.rel.dyn) }
.rel.plt : { *(.rel.plt) }
.rel.got : { *(.rel.got) }
.rel.data : { *(.rel.data) *(.rel.data*) }
_edata = .;
_data_size = . - _etext;
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
/DISCARD/ :
{
*(.rel.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@ -0,0 +1,95 @@
/* reloc_arm.c - position independent x86 ELF shared object relocator
Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
Copyright (C) 1999 Hewlett-Packard Co.
Contributed by David Mosberger <davidm@hpl.hp.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of Hewlett-Packard Co. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANYDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
#include <efi.h>
#include <efilib.h>
#include <elf.h>
EFI_STATUS _relocate (long ldbase, Elf32_Dyn *dyn, EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{
long relsz = 0, relent = 0;
Elf32_Rel *rel = 0;
unsigned long *addr;
int i;
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_REL:
rel = (Elf32_Rel*)
((unsigned long)dyn[i].d_un.d_ptr
+ ldbase);
break;
case DT_RELSZ:
relsz = dyn[i].d_un.d_val;
break;
case DT_RELENT:
relent = dyn[i].d_un.d_val;
break;
default:
break;
}
}
if (!rel && relent == 0)
return EFI_SUCCESS;
if (!rel || relent == 0)
return EFI_LOAD_ERROR;
while (relsz > 0) {
/* apply the relocs */
switch (ELF32_R_TYPE (rel->r_info)) {
case R_ARM_NONE:
break;
case R_ARM_RELATIVE:
addr = (unsigned long *)
(ldbase + rel->r_offset);
*addr += ldbase;
break;
default:
break;
}
rel = (Elf32_Rel*) ((char *) rel + relent);
relsz -= relent;
}
return EFI_SUCCESS;
}

View File

@ -0,0 +1,145 @@
#include <stdint.h>
/*
* This prevents GCC from emitting GOT based relocations, and use R_ARM_REL32
* relative relocations instead, which are more suitable for static binaries.
*/
#pragma GCC visibility push (hidden)
//
// Basic EFI types of various widths
//
#ifndef __WCHAR_TYPE__
# define __WCHAR_TYPE__ short
#endif
typedef uint64_t UINT64;
typedef int64_t INT64;
typedef uint32_t UINT32;
typedef int32_t INT32;
typedef uint16_t UINT16;
typedef int16_t INT16;
typedef uint8_t UINT8;
typedef int8_t INT8;
typedef __WCHAR_TYPE__ WCHAR;
#undef VOID
#define VOID void
typedef int32_t INTN;
typedef uint32_t UINTN;
#define EFIERR(a) (0x80000000 | a)
#define EFI_ERROR_MASK 0x80000000
#define EFIERR_OEM(a) (0xc0000000 | a)
#define BAD_POINTER 0xFBFBFBFB
#define MAX_ADDRESS 0xFFFFFFFF
#define BREAKPOINT() while (TRUE);
//
// Pointers must be aligned to these address to function
//
#define MIN_ALIGNMENT_SIZE 4
#define ALIGN_VARIABLE(Value ,Adjustment) \
(UINTN)Adjustment = 0; \
if((UINTN)Value % MIN_ALIGNMENT_SIZE) \
(UINTN)Adjustment = MIN_ALIGNMENT_SIZE - ((UINTN)Value % MIN_ALIGNMENT_SIZE); \
Value = (UINTN)Value + (UINTN)Adjustment
//
// Define macros to build data structure signatures from characters.
//
#define EFI_SIGNATURE_16(A,B) ((A) | (B<<8))
#define EFI_SIGNATURE_32(A,B,C,D) (EFI_SIGNATURE_16(A,B) | (EFI_SIGNATURE_16(C,D) << 16))
#define EFI_SIGNATURE_64(A,B,C,D,E,F,G,H) (EFI_SIGNATURE_32(A,B,C,D) | ((UINT64)(EFI_SIGNATURE_32(E,F,G,H)) << 32))
//
// EFIAPI - prototype calling convention for EFI function pointers
// BOOTSERVICE - prototype for implementation of a boot service interface
// RUNTIMESERVICE - prototype for implementation of a runtime service interface
// RUNTIMEFUNCTION - prototype for implementation of a runtime function that is not a service
// RUNTIME_CODE - pragma macro for declaring runtime code
//
#ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options
#define EFIAPI // Substitute expresion to force C calling convention
#endif
#define BOOTSERVICE
#define RUNTIMESERVICE
#define RUNTIMEFUNCTION
#define RUNTIME_CODE(a) alloc_text("rtcode", a)
#define BEGIN_RUNTIME_DATA() data_seg("rtdata")
#define END_RUNTIME_DATA() data_seg("")
#define VOLATILE volatile
#define MEMORY_FENCE __sync_synchronize
//
// When build similiar to FW, then link everything together as
// one big module.
//
#define EFI_DRIVER_ENTRY_POINT(InitFunction) \
UINTN \
InitializeDriver ( \
VOID *ImageHandle, \
VOID *SystemTable \
) \
{ \
return InitFunction(ImageHandle, \
SystemTable); \
} \
\
EFI_STATUS efi_main( \
EFI_HANDLE image, \
EFI_SYSTEM_TABLE *systab \
) __attribute__((weak, \
alias ("InitializeDriver")));
#define LOAD_INTERNAL_DRIVER(_if, type, name, entry) \
(_if)->LoadInternal(type, name, entry)
//
// Some compilers don't support the forward reference construct:
// typedef struct XXXXX
//
// The following macro provide a workaround for such cases.
#define INTERFACE_DECL(x) struct x
#define uefi_call_wrapper(func, va_num, ...) func(__VA_ARGS__)
#define EFI_FUNCTION
extern UINT64 __DivU64x32(UINT64 Dividend, UINTN Divisor, UINTN *Remainder);
static inline UINT64 DivU64x32(UINT64 Dividend, UINTN Divisor, UINTN *Remainder)
{
if (Dividend >> 32)
return __DivU64x32(Dividend, Divisor, Remainder);
/*
* GCC turns a division into a multiplication and shift with precalculated
* constants if the divisor is constant and the dividend fits into a 32 bit
* variable. Otherwise, it will turn this into calls into the 32-bit div
* library functions.
*/
if (Remainder)
*Remainder = (UINTN)Dividend % Divisor;
Dividend = (UINTN)Dividend / Divisor;
return Dividend;
}

View File

@ -0,0 +1,25 @@
/*++
Copyright (c) 1998 Intel Corporation
Module Name:
efilibplat.h
Abstract:
EFI to compile bindings
Revision History
--*/
VOID
InitializeLibPlatform (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);

View File

@ -57,6 +57,10 @@ ifeq ($(ARCH),x86_64)
FILES += $(ARCH)/callwrap $(ARCH)/efi_stub
endif
ifeq ($(ARCH),arm)
FILES += $(ARCH)/lib1funcs $(ARCH)/div64
endif
OBJS = $(FILES:%=%.o)
SUBDIRS = ia32 x86_64 ia64 runtime

193
gnu-efi-3.0/lib/arm/div64.S Normal file
View File

@ -0,0 +1,193 @@
/*
* linux/arch/arm/lib/div64.S
*
* Optimized computation of 64-bit dividend / 32-bit divisor
*
* Author: Nicolas Pitre
* Created: Oct 5, 2003
* Copyright: Monta Vista Software, Inc.
*
* 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.
*/
#define xl r0
#define xh r1
#define yl r2
#define yh r3
#define UNWIND(x...)
#define ARM(x...) x
#define THUMB(x...)
#define ENTRY(__f) \
.align 3 ;\
.globl __f ;\
.type __f,%function ;\
__f:
#define ENDPROC(__f) ;\
.size __f, . - __f
/*
UINT64
DivU64x32 (
IN UINT64 Dividend,
IN UINTN Divisor,
OUT UINTN *Remainder OPTIONAL
)
// divide 64bit by 32bit and get a 64bit result
// N.B. only works for 31bit divisors!!
{
}
*/
ENTRY(__DivU64x32)
stmfd sp!, {r4-r6, lr}
mov r5, r4 @ preserve Remainder
mov r4, r2 @ divisor in r4
bl __do_div64
teq r5, #0
strne xh, [r5]
mov r0, yl
mov r1, yh
ldmfd sp!, {r4-r6, pc}
ENDPROC(__DivU64x32)
/*
* __do_div64: perform a division with 64-bit dividend and 32-bit divisor.
*
* Note: Calling convention is totally non standard for optimal code.
* This is meant to be used by do_div() from include/asm/div64.h only.
*
* Input parameters:
* xh-xl = dividend (clobbered)
* r4 = divisor (preserved)
*
* Output values:
* yh-yl = result
* xh = remainder
*
* Clobbered regs: xl, ip
*/
ENTRY(__do_div64)
UNWIND(.fnstart)
@ Test for easy paths first.
subs ip, r4, #1
bls 9f @ divisor is 0 or 1
tst ip, r4
beq 8f @ divisor is power of 2
@ See if we need to handle upper 32-bit result.
cmp xh, r4
mov yh, #0
blo 3f
@ Align divisor with upper part of dividend.
@ The aligned divisor is stored in yl preserving the original.
@ The bit position is stored in ip.
clz yl, r4
clz ip, xh
sub yl, yl, ip
mov ip, #1
mov ip, ip, lsl yl
mov yl, r4, lsl yl
@ The division loop for needed upper bit positions.
@ Break out early if dividend reaches 0.
2: cmp xh, yl
orrcs yh, yh, ip
subcss xh, xh, yl
movnes ip, ip, lsr #1
mov yl, yl, lsr #1
bne 2b
@ See if we need to handle lower 32-bit result.
3: cmp xh, #0
mov yl, #0
cmpeq xl, r4
movlo xh, xl
movlo pc, lr
@ The division loop for lower bit positions.
@ Here we shift remainer bits leftwards rather than moving the
@ divisor for comparisons, considering the carry-out bit as well.
mov ip, #0x80000000
4: movs xl, xl, lsl #1
adcs xh, xh, xh
beq 6f
cmpcc xh, r4
5: orrcs yl, yl, ip
subcs xh, xh, r4
movs ip, ip, lsr #1
bne 4b
mov pc, lr
@ The top part of remainder became zero. If carry is set
@ (the 33th bit) this is a false positive so resume the loop.
@ Otherwise, if lower part is also null then we are done.
6: bcs 5b
cmp xl, #0
moveq pc, lr
@ We still have remainer bits in the low part. Bring them up.
clz xh, xl @ we know xh is zero here so...
add xh, xh, #1
mov xl, xl, lsl xh
mov ip, ip, lsr xh
@ Current remainder is now 1. It is worthless to compare with
@ divisor at this point since divisor can not be smaller than 3 here.
@ If possible, branch for another shift in the division loop.
@ If no bit position left then we are done.
movs ip, ip, lsr #1
mov xh, #1
bne 4b
mov pc, lr
8: @ Division by a power of 2: determine what that divisor order is
@ then simply shift values around
clz ip, r4
rsb ip, ip, #31
mov yh, xh, lsr ip
mov yl, xl, lsr ip
rsb ip, ip, #32
ARM( orr yl, yl, xh, lsl ip )
THUMB( lsl xh, xh, ip )
THUMB( orr yl, yl, xh )
mov xh, xl, lsl ip
mov xh, xh, lsr ip
mov pc, lr
@ eq -> division by 1: obvious enough...
9: moveq yl, xl
moveq yh, xh
moveq xh, #0
moveq pc, lr
UNWIND(.fnend)
UNWIND(.fnstart)
UNWIND(.pad #4)
UNWIND(.save {lr})
Ldiv0_64:
@ Division by 0:
str lr, [sp, #-8]!
bl __div0
@ as wrong as it could be...
mov yl, #0
mov yh, #0
mov xh, #0
ldr pc, [sp], #8
UNWIND(.fnend)
ENDPROC(__do_div64)

View File

@ -0,0 +1 @@
/* This stub is a stub to make the build happy */

View File

@ -0,0 +1,54 @@
/*
* aarch64/initplat.c
*
* Copright (C) 2014 Linaro Ltd.
* Author: Ard Biesheuvel <ard.biesheuvel@linaro.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.
*/
#include "lib.h"
VOID
InitializeLibPlatform (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
}
/*
* Calls to these functions may be emitted implicitly by GCC even when
* -ffreestanding is in effect.
*/
void *memset(void *s, int c, __SIZE_TYPE__ n)
{
unsigned char *p = s;
while (n--)
*p++ = c;
return s;
}
void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n)
{
unsigned char *p = dest;
unsigned char const *q = src;
while (n--)
*p++ = *q++;
return dest;
}
void __div0(void)
{
// TODO handle divide by zero fault
while (1);
}

View File

@ -0,0 +1,279 @@
/*
* linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines
*
* Author: Nicolas Pitre <nico@fluxnic.net>
* - contributed to gcc-3.4 on Sep 30, 2003
* - adapted for the Linux kernel on Oct 2, 2003
*/
/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc.
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 into combinations with other programs,
and to distribute those combinations 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 a combine
executable.)
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.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define UNWIND(x...)
#define ENTRY(__f) \
.align 3 ;\
.globl __f ;\
.type __f,%function ;\
__f:
#define ENDPROC(__f) ;\
.size __f, . - __f
.macro ARM_DIV_BODY dividend, divisor, result, curbit
clz \curbit, \divisor
clz \result, \dividend
sub \result, \curbit, \result
mov \curbit, #1
mov \divisor, \divisor, lsl \result
mov \curbit, \curbit, lsl \result
mov \result, #0
@ Division loop
1: cmp \dividend, \divisor
subhs \dividend, \dividend, \divisor
orrhs \result, \result, \curbit
cmp \dividend, \divisor, lsr #1
subhs \dividend, \dividend, \divisor, lsr #1
orrhs \result, \result, \curbit, lsr #1
cmp \dividend, \divisor, lsr #2
subhs \dividend, \dividend, \divisor, lsr #2
orrhs \result, \result, \curbit, lsr #2
cmp \dividend, \divisor, lsr #3
subhs \dividend, \dividend, \divisor, lsr #3
orrhs \result, \result, \curbit, lsr #3
cmp \dividend, #0 @ Early termination?
movnes \curbit, \curbit, lsr #4 @ No, any more bits to do?
movne \divisor, \divisor, lsr #4
bne 1b
.endm
.macro ARM_DIV2_ORDER divisor, order
clz \order, \divisor
rsb \order, \order, #31
.endm
.macro ARM_MOD_BODY dividend, divisor, order, spare
clz \order, \divisor
clz \spare, \dividend
sub \order, \order, \spare
mov \divisor, \divisor, lsl \order
@ Perform all needed substractions to keep only the reminder.
@ Do comparisons in batch of 4 first.
subs \order, \order, #3 @ yes, 3 is intended here
blt 2f
1: cmp \dividend, \divisor
subhs \dividend, \dividend, \divisor
cmp \dividend, \divisor, lsr #1
subhs \dividend, \dividend, \divisor, lsr #1
cmp \dividend, \divisor, lsr #2
subhs \dividend, \dividend, \divisor, lsr #2
cmp \dividend, \divisor, lsr #3
subhs \dividend, \dividend, \divisor, lsr #3
cmp \dividend, #1
mov \divisor, \divisor, lsr #4
subges \order, \order, #4
bge 1b
tst \order, #3
teqne \dividend, #0
beq 5f
@ Either 1, 2 or 3 comparison/substractions are left.
2: cmn \order, #2
blt 4f
beq 3f
cmp \dividend, \divisor
subhs \dividend, \dividend, \divisor
mov \divisor, \divisor, lsr #1
3: cmp \dividend, \divisor
subhs \dividend, \dividend, \divisor
mov \divisor, \divisor, lsr #1
4: cmp \dividend, \divisor
subhs \dividend, \dividend, \divisor
5:
.endm
ENTRY(__aeabi_uidiv)
ENTRY(__udivsi3)
subs r2, r1, #1
moveq pc, lr
bcc Ldiv0
cmp r0, r1
bls 11f
tst r1, r2
beq 12f
ARM_DIV_BODY r0, r1, r2, r3
mov r0, r2
mov pc, lr
11: moveq r0, #1
movne r0, #0
mov pc, lr
12: ARM_DIV2_ORDER r1, r2
mov r0, r0, lsr r2
mov pc, lr
UNWIND(.fnend)
ENDPROC(__udivsi3)
ENDPROC(__aeabi_uidiv)
ENTRY(__umodsi3)
UNWIND(.fnstart)
subs r2, r1, #1 @ compare divisor with 1
bcc Ldiv0
cmpne r0, r1 @ compare dividend with divisor
moveq r0, #0
tsthi r1, r2 @ see if divisor is power of 2
andeq r0, r0, r2
movls pc, lr
ARM_MOD_BODY r0, r1, r2, r3
mov pc, lr
UNWIND(.fnend)
ENDPROC(__umodsi3)
ENTRY(__divsi3)
ENTRY(__aeabi_idiv)
UNWIND(.fnstart)
cmp r1, #0
eor ip, r0, r1 @ save the sign of the result.
beq Ldiv0
rsbmi r1, r1, #0 @ loops below use unsigned.
subs r2, r1, #1 @ division by 1 or -1 ?
beq 10f
movs r3, r0
rsbmi r3, r0, #0 @ positive dividend value
cmp r3, r1
bls 11f
tst r1, r2 @ divisor is power of 2 ?
beq 12f
ARM_DIV_BODY r3, r1, r0, r2
cmp ip, #0
rsbmi r0, r0, #0
mov pc, lr
10: teq ip, r0 @ same sign ?
rsbmi r0, r0, #0
mov pc, lr
11: movlo r0, #0
moveq r0, ip, asr #31
orreq r0, r0, #1
mov pc, lr
12: ARM_DIV2_ORDER r1, r2
cmp ip, #0
mov r0, r3, lsr r2
rsbmi r0, r0, #0
mov pc, lr
UNWIND(.fnend)
ENDPROC(__divsi3)
ENDPROC(__aeabi_idiv)
ENTRY(__modsi3)
UNWIND(.fnstart)
cmp r1, #0
beq Ldiv0
rsbmi r1, r1, #0 @ loops below use unsigned.
movs ip, r0 @ preserve sign of dividend
rsbmi r0, r0, #0 @ if negative make positive
subs r2, r1, #1 @ compare divisor with 1
cmpne r0, r1 @ compare dividend with divisor
moveq r0, #0
tsthi r1, r2 @ see if divisor is power of 2
andeq r0, r0, r2
bls 10f
ARM_MOD_BODY r0, r1, r2, r3
10: cmp ip, #0
rsbmi r0, r0, #0
mov pc, lr
UNWIND(.fnend)
ENDPROC(__modsi3)
ENTRY(__aeabi_uidivmod)
UNWIND(.fnstart)
UNWIND(.save {r0, r1, ip, lr} )
stmfd sp!, {r0, r1, ip, lr}
bl __aeabi_uidiv
ldmfd sp!, {r1, r2, ip, lr}
mul r3, r0, r2
sub r1, r1, r3
mov pc, lr
UNWIND(.fnend)
ENDPROC(__aeabi_uidivmod)
ENTRY(__aeabi_idivmod)
UNWIND(.fnstart)
UNWIND(.save {r0, r1, ip, lr} )
stmfd sp!, {r0, r1, ip, lr}
bl __aeabi_idiv
ldmfd sp!, {r1, r2, ip, lr}
mul r3, r0, r2
sub r1, r1, r3
mov pc, lr
UNWIND(.fnend)
ENDPROC(__aeabi_idivmod)
Ldiv0:
UNWIND(.fnstart)
UNWIND(.pad #4)
UNWIND(.save {lr})
str lr, [sp, #-8]!
bl __div0
mov r0, #0 @ About as wrong as it could be.
ldr pc, [sp], #8
UNWIND(.fnend)
ENDPROC(Ldiv0)

View File

@ -0,0 +1,44 @@
/*
* arm/math.c - math routines for ARM
*
* Copright (C) 2014 Linaro Ltd.
* Author: Ard Biesheuvel <ard.biesheuvel@linaro.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.
*/
#include "lib.h"
UINT64
LShiftU64 (
IN UINT64 Operand,
IN UINTN Count
)
// Left shift 64bit by 32bit and get a 64bit result
{
return Operand << Count;
}
UINT64
RShiftU64 (
IN UINT64 Operand,
IN UINTN Count
)
// Right shift 64bit by 32bit and get a 64bit result
{
return Operand >> Count;
}
UINT64
MultU64x32 (
IN UINT64 Multiplicand,
IN UINTN Multiplier
)
// Multiple 64bit by 32bit and get a 64bit result
{
return Multiplicand * Multiplier;
}