diff options
author | Michael Brown | 2015-10-19 21:01:19 +0200 |
---|---|---|
committer | Michael Brown | 2016-05-06 13:08:44 +0200 |
commit | 1a16f67a28c6e8b9875b07e15c7c379cfc147e69 (patch) | |
tree | 4f58c91b43ca8862d8219026c70e89717a514b89 | |
parent | [bitops] Fix typo in test case (diff) | |
download | ipxe-1a16f67a28c6e8b9875b07e15c7c379cfc147e69.tar.gz ipxe-1a16f67a28c6e8b9875b07e15c7c379cfc147e69.tar.xz ipxe-1a16f67a28c6e8b9875b07e15c7c379cfc147e69.zip |
[arm] Add support for 32-bit ARM
Signed-off-by: Michael Brown <mcb30@ipxe.org>
43 files changed, 1879 insertions, 49 deletions
diff --git a/src/arch/arm/Makefile b/src/arch/arm/Makefile new file mode 100644 index 00000000..f883a64d --- /dev/null +++ b/src/arch/arm/Makefile @@ -0,0 +1,25 @@ +# Assembler section type character +# +ASM_TCHAR := % +ASM_TCHAR_OPS := %% + +# ARM-specific directories containing source files +# +SRCDIRS += arch/arm/core +SRCDIRS += arch/arm/libgcc +SRCDIRS += arch/arm/interface/efi + +# ARM-specific flags +# +CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft +CFLAGS += -mword-relocations +ASFLAGS += -mthumb -mcpu=cortex-a15 + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# Include platform-specific Makefile +# +MAKEDEPS += arch/arm/Makefile.$(PLATFORM) +include arch/arm/Makefile.$(PLATFORM) diff --git a/src/arch/arm/Makefile.efi b/src/arch/arm/Makefile.efi new file mode 100644 index 00000000..909c8d21 --- /dev/null +++ b/src/arch/arm/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI32) + +# Specify EFI boot file +# +EFI_BOOT_FILE = bootarm.efi + +# Include generic EFI Makefile +# +MAKEDEPS += Makefile.efi +include Makefile.efi diff --git a/src/arch/arm/core/arm_bigint.c b/src/arch/arm/core/arm_bigint.c new file mode 100644 index 00000000..839bead1 --- /dev/null +++ b/src/arch/arm/core/arm_bigint.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <string.h> +#include <ipxe/bigint.h> + +/** @file + * + * Big integer support + */ + +/** + * Multiply big integers + * + * @v multiplicand0 Element 0 of big integer to be multiplied + * @v multiplier0 Element 0 of big integer to be multiplied + * @v result0 Element 0 of big integer to hold result + * @v size Number of elements + */ +void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *result0, unsigned int size ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand = + ( ( const void * ) multiplicand0 ); + const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier = + ( ( const void * ) multiplier0 ); + bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result = + ( ( void * ) result0 ); + unsigned int i; + unsigned int j; + uint32_t multiplicand_element; + uint32_t multiplier_element; + uint32_t *result_elements; + uint32_t discard_low; + uint32_t discard_high; + uint32_t discard_temp; + + /* Zero result */ + memset ( result, 0, sizeof ( *result ) ); + + /* Multiply integers one element at a time */ + for ( i = 0 ; i < size ; i++ ) { + multiplicand_element = multiplicand->element[i]; + for ( j = 0 ; j < size ; j++ ) { + multiplier_element = multiplier->element[j]; + result_elements = &result->element[ i + j ]; + /* Perform a single multiply, and add the + * resulting double-element into the result, + * carrying as necessary. The carry can + * never overflow beyond the end of the + * result, since: + * + * a < 2^{n}, b < 2^{n} => ab < 2^{2n} + */ + __asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t" + "ldr %3, [%0]\n\t" + "adds %3, %1\n\t" + "stmia %0!, {%3}\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, %2\n\t" + "stmia %0!, {%3}\n\t" + "bcc 2f\n\t" + "\n1:\n\t" + "ldr %3, [%0]\n\t" + "adcs %3, #0\n\t" + "stmia %0!, {%3}\n\t" + "bcs 1b\n\t" + "\n2:\n\t" + : "+l" ( result_elements ), + "=l" ( discard_low ), + "=l" ( discard_high ), + "=l" ( discard_temp ), + "+m" ( *result ) + : "l" ( multiplicand_element ), + "l" ( multiplier_element ) + : "cc" ); + } + } +} diff --git a/src/arch/arm/core/arm_io.c b/src/arch/arm/core/arm_io.c new file mode 100644 index 00000000..804014c9 --- /dev/null +++ b/src/arch/arm/core/arm_io.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/io.h> +#include <ipxe/arm_io.h> + +/** @file + * + * iPXE I/O API for ARM + * + */ + +/** An ARM I/O qword */ +union arm_io_qword { + uint64_t qword; + uint32_t dword[2]; +}; + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + * + * This is not atomic for ARM. + */ +static uint64_t arm_readq ( volatile uint64_t *io_addr ) { + volatile union arm_io_qword *ptr = + container_of ( io_addr, union arm_io_qword, qword ); + union arm_io_qword tmp; + + tmp.dword[0] = readl ( &ptr->dword[0] ); + tmp.dword[1] = readl ( &ptr->dword[1] ); + return tmp.qword; +} + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + * + * This is not atomic for ARM. + */ +static void arm_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + volatile union arm_io_qword *ptr = + container_of ( io_addr, union arm_io_qword, qword ); + union arm_io_qword tmp; + + tmp.qword = data; + writel ( tmp.dword[0], &ptr->dword[0] ); + writel ( tmp.dword[1], &ptr->dword[1] ); +} + +PROVIDE_IOAPI_INLINE ( arm, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( arm, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( arm, readb ); +PROVIDE_IOAPI_INLINE ( arm, readw ); +PROVIDE_IOAPI_INLINE ( arm, readl ); +PROVIDE_IOAPI_INLINE ( arm, writeb ); +PROVIDE_IOAPI_INLINE ( arm, writew ); +PROVIDE_IOAPI_INLINE ( arm, writel ); +PROVIDE_IOAPI_INLINE ( arm, iodelay ); +PROVIDE_IOAPI_INLINE ( arm, mb ); +PROVIDE_IOAPI ( arm, readq, arm_readq ); +PROVIDE_IOAPI ( arm, writeq, arm_writeq ); diff --git a/src/arch/arm/core/setjmp.S b/src/arch/arm/core/setjmp.S new file mode 100644 index 00000000..7e7b0fe5 --- /dev/null +++ b/src/arch/arm/core/setjmp.S @@ -0,0 +1,32 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/* + * Save stack context for non-local goto + */ + .globl setjmp + .type setjmp, %function +setjmp: + /* Store registers */ + stmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Return 0 when returning as setjmp() */ + mov r0, #0 + bx lr + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp + .type longjmp, %function +longjmp: + /* Restore registers */ + ldmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr } + /* Force result to non-zero */ + movs r0, r1 + moveq r0, #1 + /* Return to setjmp() caller */ + bx lr + .size longjmp, . - longjmp diff --git a/src/arch/arm/include/bits/bigint.h b/src/arch/arm/include/bits/bigint.h new file mode 100644 index 00000000..103c6c48 --- /dev/null +++ b/src/arch/arm/include/bits/bigint.h @@ -0,0 +1,316 @@ +#ifndef _BITS_BIGINT_H +#define _BITS_BIGINT_H + +/** @file + * + * Big integer support + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <string.h> +#include <strings.h> + +/** Element of a big integer */ +typedef uint32_t bigint_element_t; + +/** + * Initialise big integer + * + * @v value0 Element 0 of big integer to initialise + * @v size Number of elements + * @v data Raw data + * @v len Length of raw data + */ +static inline __attribute__ (( always_inline )) void +bigint_init_raw ( uint32_t *value0, unsigned int size, + const void *data, size_t len ) { + size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + uint8_t *value_byte = ( ( void * ) value0 ); + const uint8_t *data_byte = ( data + len ); + + /* Copy raw data in reverse order, padding with zeros */ + while ( len-- ) + *(value_byte++) = *(--data_byte); + while ( pad_len-- ) + *(value_byte++) = 0; +} + +/** + * Add big integers + * + * @v addend0 Element 0 of big integer to add + * @v value0 Element 0 of big integer to be added to + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_addend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_addend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %2, %0, %8, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "adcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_addend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_addend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( addend0 ), "1" ( value0 ), "l" ( size ) + : "cc" ); +} + +/** + * Subtract big integers + * + * @v subtrahend0 Element 0 of big integer to subtract + * @v value0 Element 0 of big integer to be subtracted from + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, + unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_subtrahend; + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_subtrahend_i; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "add %2, %0, %8, lsl #2\n\t" + "cmp %2, %0\n\t" /* set CF */ + "\n1:\n\t" + "ldmia %0!, {%3}\n\t" + "ldr %4, [%1]\n\t" + "sbcs %4, %3\n\t" + "stmia %1!, {%4}\n\t" + "teq %0, %2\n\t" + "bne 1b\n\t" + : "=l" ( discard_subtrahend ), + "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_subtrahend_i ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( subtrahend0 ), "1" ( value0 ), + "l" ( size ) + : "cc" ); +} + +/** + * Rotate big integer left + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_rol_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldr %2, [%0]\n\t" + "adcs %2, %2\n\t" + "stmia %0!, {%2}\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Rotate big integer right + * + * @v value0 Element 0 of big integer + * @v size Number of elements + */ +static inline __attribute__ (( always_inline )) void +bigint_ror_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + uint32_t *discard_value; + uint32_t *discard_end; + uint32_t discard_value_i; + + __asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */ + "\n1:\n\t" + "ldmdb %1!, {%2}\n\t" + "rrxs %2, %2\n\t" + "str %2, [%1]\n\t" + "teq %0, %1\n\t" + "bne 1b\n\t" + : "=l" ( discard_value ), + "=l" ( discard_end ), + "=l" ( discard_value_i ), + "+m" ( *value ) + : "0" ( value0 ), "1" ( size ) + : "cc" ); +} + +/** + * Test if big integer is equal to zero + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret is_zero Big integer is equal to zero + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_zero_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = value0; + uint32_t value_i; + + do { + value_i = *(value++); + if ( value_i ) + break; + } while ( --size ); + + return ( value_i == 0 ); +} + +/** + * Compare big integers + * + * @v value0 Element 0 of big integer + * @v reference0 Element 0 of reference big integer + * @v size Number of elements + * @ret geq Big integer is greater than or equal to the reference + */ +static inline __attribute__ (( always_inline, pure )) int +bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0, + unsigned int size ) { + const uint32_t *value = ( value0 + size ); + const uint32_t *reference = ( reference0 + size ); + uint32_t value_i; + uint32_t reference_i; + + do { + value_i = *(--value); + reference_i = *(--reference); + if ( value_i != reference_i ) + break; + } while ( --size ); + + return ( value_i >= reference_i ); +} + +/** + * Test if bit is set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @v bit Bit to test + * @ret is_set Bit is set + */ +static inline __attribute__ (( always_inline )) int +bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size, + unsigned int bit ) { + const bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( const void * ) value0 ); + unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) ); + unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) ); + + return ( value->element[index] & ( 1 << subindex ) ); +} + +/** + * Find highest bit set in big integer + * + * @v value0 Element 0 of big integer + * @v size Number of elements + * @ret max_bit Highest bit set + 1 (or 0 if no bits set) + */ +static inline __attribute__ (( always_inline )) int +bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) { + const uint32_t *value = ( value0 + size ); + int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) ); + uint32_t value_i; + + do { + value_i = *(--value); + max_bit -= ( 32 - fls ( value_i ) ); + if ( value_i ) + break; + } while ( --size ); + + return max_bit; +} + +/** + * Grow big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, + uint32_t *dest0, unsigned int dest_size ) { + unsigned int pad_size = ( dest_size - source_size ); + + memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) ); + memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) ); +} + +/** + * Shrink big integer + * + * @v source0 Element 0 of source big integer + * @v source_size Number of elements in source big integer + * @v dest0 Element 0 of destination big integer + * @v dest_size Number of elements in destination big integer + */ +static inline __attribute__ (( always_inline )) void +bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, + uint32_t *dest0, unsigned int dest_size ) { + + memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) ); +} + +/** + * Finalise big integer + * + * @v value0 Element 0 of big integer to finalise + * @v size Number of elements + * @v out Output buffer + * @v len Length of output buffer + */ +static inline __attribute__ (( always_inline )) void +bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, + void *out, size_t len ) { + const uint8_t *value_byte = ( ( const void * ) value0 ); + uint8_t *out_byte = ( out + len ); + + /* Copy raw data in reverse order */ + while ( len-- ) + *(--out_byte) = *(value_byte++); +} + +extern void bigint_multiply_raw ( const uint32_t *multiplicand0, + const uint32_t *multiplier0, + uint32_t *value0, unsigned int size ); + +#endif /* _BITS_BIGINT_H */ diff --git a/src/arch/arm/include/bits/bitops.h b/src/arch/arm/include/bits/bitops.h new file mode 100644 index 00000000..9a5fe14c --- /dev/null +++ b/src/arch/arm/include/bits/bitops.h @@ -0,0 +1,100 @@ +#ifndef _BITS_BITOPS_H +#define _BITS_BITOPS_H + +/** @file + * + * ARM bit operations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +/** + * Test and set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_set_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "orr %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Test and clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + * @ret old Old value of bit (zero or non-zero) + */ +static inline __attribute__ (( always_inline )) int +test_and_clear_bit ( unsigned int bit, volatile void *bits ) { + unsigned int index = ( bit / 32 ); + unsigned int offset = ( bit % 32 ); + volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index ); + uint32_t mask = ( 1UL << offset ); + uint32_t old; + uint32_t new; + uint32_t flag; + + __asm__ __volatile__ ( "\n1:\n\t" + "ldrex %0, %3\n\t" + "bic %1, %0, %4\n\t" + "strex %2, %1, %3\n\t" + "tst %2, %2\n\t" + "bne 1b\n\t" + : "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ), + "+Q" ( *dword ) + : "r" ( mask ) + : "cc" ); + + return ( old & mask ); +} + +/** + * Set bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +set_bit ( unsigned int bit, volatile void *bits ) { + + test_and_set_bit ( bit, bits ); +} + +/** + * Clear bit atomically + * + * @v bit Bit to set + * @v bits Bit field + */ +static inline __attribute__ (( always_inline )) void +clear_bit ( unsigned int bit, volatile void *bits ) { + + test_and_clear_bit ( bit, bits ); +} + +#endif /* _BITS_BITOPS_H */ diff --git a/src/arch/arm/include/bits/byteswap.h b/src/arch/arm/include/bits/byteswap.h new file mode 100644 index 00000000..1fc884bd --- /dev/null +++ b/src/arch/arm/include/bits/byteswap.h @@ -0,0 +1,52 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +/** @file + * + * Byte-order swapping functions + * + */ + +#include <stdint.h> + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "rev16 %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_16s ( uint16_t *x ) { + *x = __bswap_variable_16 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "rev %0, %1" : "=l" ( x ) : "l" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline )) void +__bswap_32s ( uint32_t *x ) { + *x = __bswap_variable_32 ( *x ); +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + uint32_t in_high = ( x >> 32 ); + uint32_t in_low = ( x & 0xffffffffUL ); + uint32_t out_high = __bswap_variable_32 ( in_low ); + uint32_t out_low = __bswap_variable_32 ( in_high ); + + return ( ( ( ( uint64_t ) out_high ) << 32 ) | + ( ( uint64_t ) out_low ) ); +} + +static inline __attribute__ (( always_inline )) void +__bswap_64s ( uint64_t *x ) { + *x = __bswap_variable_64 ( *x ); +} + +#endif diff --git a/src/arch/arm/include/bits/compiler.h b/src/arch/arm/include/bits/compiler.h new file mode 100644 index 00000000..e420cf92 --- /dev/null +++ b/src/arch/arm/include/bits/compiler.h @@ -0,0 +1,16 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_ARM_NONE + +#ifndef ASSEMBLY + +#define __asmcall +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /*_BITS_COMPILER_H */ diff --git a/src/arch/arm/include/bits/endian.h b/src/arch/arm/include/bits/endian.h new file mode 100644 index 00000000..4506711a --- /dev/null +++ b/src/arch/arm/include/bits/endian.h @@ -0,0 +1,13 @@ +#ifndef _BITS_ENDIAN_H +#define _BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* ARM may be either little-endian or big-endian */ +#ifdef __ARM_BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#endif /* _BITS_ENDIAN_H */ diff --git a/src/arch/arm/include/bits/entropy.h b/src/arch/arm/include/bits/entropy.h new file mode 100644 index 00000000..75fdc90e --- /dev/null +++ b/src/arch/arm/include/bits/entropy.h @@ -0,0 +1,12 @@ +#ifndef _BITS_ENTROPY_H +#define _BITS_ENTROPY_H + +/** @file + * + * ARM-specific entropy API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_ENTROPY_H */ diff --git a/src/arch/arm/include/bits/errfile.h b/src/arch/arm/include/bits/errfile.h new file mode 100644 index 00000000..65f7f719 --- /dev/null +++ b/src/arch/arm/include/bits/errfile.h @@ -0,0 +1,19 @@ +#ifndef _BITS_ERRFILE_H +#define _BITS_ERRFILE_H + +/** @file + * + * ARM-specific error file identifiers + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @addtogroup errfile Error file identifiers + * @{ + */ + +/** @} */ + +#endif /* _BITS_ERRFILE_H */ diff --git a/src/arch/arm/include/bits/hyperv.h b/src/arch/arm/include/bits/hyperv.h new file mode 100644 index 00000000..f0e0c879 --- /dev/null +++ b/src/arch/arm/include/bits/hyperv.h @@ -0,0 +1,12 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_HYPERV_H */ diff --git a/src/arch/arm/include/bits/io.h b/src/arch/arm/include/bits/io.h new file mode 100644 index 00000000..90f6455e --- /dev/null +++ b/src/arch/arm/include/bits/io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * ARM-specific I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/arm_io.h> + +#endif /* _BITS_IO_H */ diff --git a/src/arch/arm/include/bits/iomap.h b/src/arch/arm/include/bits/iomap.h new file mode 100644 index 00000000..ae953c45 --- /dev/null +++ b/src/arch/arm/include/bits/iomap.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IOMAP_H +#define _BITS_IOMAP_H + +/** @file + * + * ARM-specific I/O mapping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_IOMAP_H */ diff --git a/src/arch/arm/include/bits/nap.h b/src/arch/arm/include/bits/nap.h new file mode 100644 index 00000000..e30a7146 --- /dev/null +++ b/src/arch/arm/include/bits/nap.h @@ -0,0 +1,14 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * ARM-specific CPU sleeping API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/efi/efiarm_nap.h> + +#endif /* _BITS_MAP_H */ diff --git a/src/arch/arm/include/bits/pci_io.h b/src/arch/arm/include/bits/pci_io.h new file mode 100644 index 00000000..fba0eb97 --- /dev/null +++ b/src/arch/arm/include/bits/pci_io.h @@ -0,0 +1,14 @@ +#ifndef _BITS_PCI_IO_H +#define _BITS_PCI_IO_H + +/** @file + * + * ARM PCI I/O API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/io.h> + +#endif /* _BITS_PCI_IO_H */ diff --git a/src/arch/arm/include/bits/profile.h b/src/arch/arm/include/bits/profile.h new file mode 100644 index 00000000..2b15d160 --- /dev/null +++ b/src/arch/arm/include/bits/profile.h @@ -0,0 +1,30 @@ +#ifndef _BITS_PROFILE_H +#define _BITS_PROFILE_H + +/** @file + * + * Profiling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) uint64_t +profile_timestamp ( void ) { + uint32_t cycles; + + /* Read cycle counter */ + __asm__ __volatile__ ( "mcr p15, 0, %1, c9, c12, 0\n\t" + "mrc p15, 0, %0, c9, c13, 0\n\t" + : "=r" ( cycles ) : "r" ( 1 ) ); + return cycles; +} + +#endif /* _BITS_PROFILE_H */ diff --git a/src/arch/arm/include/bits/reboot.h b/src/arch/arm/include/bits/reboot.h new file mode 100644 index 00000000..88c50250 --- /dev/null +++ b/src/arch/arm/include/bits/reboot.h @@ -0,0 +1,12 @@ +#ifndef _BITS_REBOOT_H +#define _BITS_REBOOT_H + +/** @file + * + * ARM-specific reboot API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_REBOOT_H */ diff --git a/src/arch/arm/include/bits/sanboot.h b/src/arch/arm/include/bits/sanboot.h new file mode 100644 index 00000000..abd4c79a --- /dev/null +++ b/src/arch/arm/include/bits/sanboot.h @@ -0,0 +1,12 @@ +#ifndef _BITS_SANBOOT_H +#define _BITS_SANBOOT_H + +/** @file + * + * ARM-specific sanboot API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_SANBOOT_H */ diff --git a/src/arch/arm/include/bits/smbios.h b/src/arch/arm/include/bits/smbios.h new file mode 100644 index 00000000..d9421811 --- /dev/null +++ b/src/arch/arm/include/bits/smbios.h @@ -0,0 +1,12 @@ +#ifndef _BITS_SMBIOS_H +#define _BITS_SMBIOS_H + +/** @file + * + * ARM-specific SMBIOS API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_SMBIOS_H */ diff --git a/src/arch/arm/include/bits/stdint.h b/src/arch/arm/include/bits/stdint.h new file mode 100644 index 00000000..fe1f9946 --- /dev/null +++ b/src/arch/arm/include/bits/stdint.h @@ -0,0 +1,23 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +typedef __SIZE_TYPE__ size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/src/arch/arm/include/bits/string.h b/src/arch/arm/include/bits/string.h new file mode 100644 index 00000000..5b1c1505 --- /dev/null +++ b/src/arch/arm/include/bits/string.h @@ -0,0 +1,60 @@ +#ifndef BITS_STRING_H +#define BITS_STRING_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * String functions + * + */ + +/** + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int character, size_t len ) { + + /* Not yet optimised */ + generic_memset ( dest, character, len ); + return dest; +} + +/** + * Copy memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memcpy ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memcpy ( dest, src, len ); + return dest; +} + +/** + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region + */ +static inline __attribute__ (( always_inline )) void * +memmove ( void *dest, const void *src, size_t len ) { + + /* Not yet optimised */ + generic_memmove ( dest, src, len ); + return dest; +} + +#endif /* BITS_STRING_H */ diff --git a/src/arch/arm/include/bits/strings.h b/src/arch/arm/include/bits/strings.h new file mode 100644 index 00000000..adbd5f4b --- /dev/null +++ b/src/arch/arm/include/bits/strings.h @@ -0,0 +1,85 @@ +#ifndef _BITS_STRINGS_H +#define _BITS_STRINGS_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + unsigned long bits = value; + unsigned long lsb; + unsigned int lz; + + /* Extract least significant set bit */ + lsb = ( bits & -bits ); + + /* Count number of leading zeroes before LSB */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) ); + + return ( 32 - lz ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( low ) { + return ( __ffsl ( low ) ); + } else if ( high ) { + return ( 32 + __ffsl ( high ) ); + } else { + return 0; + } +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsl ( long value ) { + unsigned int lz; + + /* Count number of leading zeroes */ + __asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) ); + + return ( 32 - lz ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v value Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( high ) { + return ( 32 + __flsl ( high ) ); + } else if ( low ) { + return ( __flsl ( low ) ); + } else { + return 0; + } +} + +#endif /* _BITS_STRINGS_H */ diff --git a/src/arch/arm/include/bits/tcpip.h b/src/arch/arm/include/bits/tcpip.h new file mode 100644 index 00000000..fc3c5b3f --- /dev/null +++ b/src/arch/arm/include/bits/tcpip.h @@ -0,0 +1,19 @@ +#ifndef _BITS_TCPIP_H +#define _BITS_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +static inline __attribute__ (( always_inline )) uint16_t +tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) { + + /* Not yet optimised */ + return generic_tcpip_continue_chksum ( partial, data, len ); +} + +#endif /* _BITS_TCPIP_H */ diff --git a/src/arch/arm/include/bits/time.h b/src/arch/arm/include/bits/time.h new file mode 100644 index 00000000..724d8b93 --- /dev/null +++ b/src/arch/arm/include/bits/time.h @@ -0,0 +1,12 @@ +#ifndef _BITS_TIME_H +#define _BITS_TIME_H + +/** @file + * + * ARM-specific time API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_TIME_H */ diff --git a/src/arch/arm/include/bits/timer.h b/src/arch/arm/include/bits/timer.h new file mode 100644 index 00000000..64e7d31d --- /dev/null +++ b/src/arch/arm/include/bits/timer.h @@ -0,0 +1,12 @@ +#ifndef _BITS_TIMER_H +#define _BITS_TIMER_H + +/** @file + * + * ARM-specific timer API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_TIMER_H */ diff --git a/src/arch/arm/include/bits/uaccess.h b/src/arch/arm/include/bits/uaccess.h new file mode 100644 index 00000000..87f11509 --- /dev/null +++ b/src/arch/arm/include/bits/uaccess.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UACCESS_H +#define _BITS_UACCESS_H + +/** @file + * + * ARM-specific user access API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UACCESS_H */ diff --git a/src/arch/arm/include/bits/uart.h b/src/arch/arm/include/bits/uart.h new file mode 100644 index 00000000..6f85975f --- /dev/null +++ b/src/arch/arm/include/bits/uart.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UART_H +#define _BITS_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UART_H */ diff --git a/src/arch/arm/include/bits/umalloc.h b/src/arch/arm/include/bits/umalloc.h new file mode 100644 index 00000000..27970d7b --- /dev/null +++ b/src/arch/arm/include/bits/umalloc.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * ARM-specific user memory allocation API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_UMALLOC_H */ diff --git a/src/arch/arm/include/bits/xen.h b/src/arch/arm/include/bits/xen.h new file mode 100644 index 00000000..c3808a73 --- /dev/null +++ b/src/arch/arm/include/bits/xen.h @@ -0,0 +1,149 @@ +#ifndef _BITS_XEN_H +#define _BITS_XEN_H + +/** @file + * + * Xen interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Hypercall registers */ +#define XEN_HC "r12" +#define XEN_REG1 "r0" +#define XEN_REG2 "r1" +#define XEN_REG3 "r2" +#define XEN_REG4 "r3" +#define XEN_REG5 "r4" + +/** + * Issue hypercall with one argument + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_1 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + + __asm__ __volatile__ ( "hvc %1" + : "+r" ( reg1 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with two arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_2 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + + __asm__ __volatile__ ( "hvc %2" + : "+r" ( reg1 ), "+r" ( reg2 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with three arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_3 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + + __asm__ __volatile__ ( "hvc %3" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with four arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_4 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + + __asm__ __volatile__ ( "hvc %4" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +/** + * Issue hypercall with five arguments + * + * @v xen Xen hypervisor + * @v hypercall Hypercall number + * @v arg1 First argument + * @v arg2 Second argument + * @v arg3 Third argument + * @v arg4 Fourth argument + * @v arg5 Fifth argument + * @ret retval Return value + */ +static inline __attribute__ (( always_inline )) unsigned long +xen_hypercall_5 ( struct xen_hypervisor *xen __unused, unsigned int hypercall, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5 ) { + register unsigned long hc asm ( XEN_HC ) = hypercall; + register unsigned long reg1 asm ( XEN_REG1 ) = arg1; + register unsigned long reg2 asm ( XEN_REG2 ) = arg2; + register unsigned long reg3 asm ( XEN_REG3 ) = arg3; + register unsigned long reg4 asm ( XEN_REG4 ) = arg4; + register unsigned long reg5 asm ( XEN_REG5 ) = arg5; + + __asm__ __volatile__ ( "hvc %5" + : "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ), + "+r" ( reg4 ), "+r" ( reg5 ) + : "i" ( XEN_HYPERCALL_TAG ), "r" ( hc ) + : "memory", "cc" ); + return reg1; +} + +#endif /* _BITS_XEN_H */ diff --git a/src/arch/arm/include/efi/ipxe/dhcp_arch.h b/src/arch/arm/include/efi/ipxe/dhcp_arch.h new file mode 100644 index 00000000..f403d4ce --- /dev/null +++ b/src/arch/arm/include/efi/ipxe/dhcp_arch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +#ifndef _DHCP_ARCH_H +#define _DHCP_ARCH_H + +/** @file + * + * Architecture-specific DHCP options + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/dhcp.h> + +#define DHCP_ARCH_VENDOR_CLASS_ID \ + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) + +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_EFI ) + +#define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) + +#endif diff --git a/src/arch/arm/include/gdbmach.h b/src/arch/arm/include/gdbmach.h new file mode 100644 index 00000000..cd152eed --- /dev/null +++ b/src/arch/arm/include/gdbmach.h @@ -0,0 +1,45 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include <stdint.h> + +typedef unsigned long gdbreg_t; + +/* Register snapshot */ +enum { + /* Not yet implemented */ + GDBMACH_NREGS, +}; + +#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) ) + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + /* Not yet implemented */ + ( void ) regs; + ( void ) step; +} + +static inline void gdbmach_breakpoint ( void ) { + /* Not yet implemented */ +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, + int enable ); +extern void gdbmach_init ( void ); + +#endif /* GDBMACH_H */ diff --git a/src/arch/arm/include/ipxe/arm_io.h b/src/arch/arm/include/ipxe/arm_io.h new file mode 100644 index 00000000..e13de15f --- /dev/null +++ b/src/arch/arm/include/ipxe/arm_io.h @@ -0,0 +1,89 @@ +#ifndef _IPXE_ARM_IO_H +#define _IPXE_ARM_IO_H + +/** @file + * + * iPXE I/O API for ARM + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef IOAPI_ARM +#define IOAPI_PREFIX_arm +#else +#define IOAPI_PREFIX_arm __arm_ +#endif + +/* + * Memory space mappings + * + */ + +/** Page shift */ +#define PAGE_SHIFT 12 + +/* + * Physical<->Bus address mappings + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +/* + * MMIO reads and writes up to native word size + * + */ + +#define ARM_READX( _api_func, _type, _insn_suffix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( arm, _api_func ) ( volatile _type *io_addr ) { \ + _type data; \ + __asm__ __volatile__ ( "ldr" _insn_suffix " %0, %1" \ + : "=r" ( data ) : "Qo" ( *io_addr ) ); \ + return data; \ +} +ARM_READX ( readb, uint8_t, "b" ); +ARM_READX ( readw, uint16_t, "h" ); +ARM_READX ( readl, uint32_t, "" ); + +#define ARM_WRITEX( _api_func, _type, _insn_suffix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( arm, _api_func ) ( _type data, \ + volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "str" _insn_suffix " %0, %1" \ + : : "r" ( data ), "Qo" ( *io_addr ) ); \ +} +ARM_WRITEX ( writeb, uint8_t, "b" ); +ARM_WRITEX ( writew, uint16_t, "h" ); +ARM_WRITEX ( writel, uint32_t, "" ); + +/* + * Slow down I/O + * + */ + +static inline __always_inline void +IOAPI_INLINE ( arm, iodelay ) ( void ) { + /* Nothing to do */ +} + +/* + * Memory barrier + * + */ + +static inline __always_inline void +IOAPI_INLINE ( arm, mb ) ( void ) { + __asm__ __volatile__ ( "dmb" ); +} + +#endif /* _IPXE_ARM_IO_H */ diff --git a/src/arch/arm/include/ipxe/efi/efiarm_nap.h b/src/arch/arm/include/ipxe/efi/efiarm_nap.h new file mode 100644 index 00000000..dcbdd3e2 --- /dev/null +++ b/src/arch/arm/include/ipxe/efi/efiarm_nap.h @@ -0,0 +1,18 @@ +#ifndef _IPXE_EFIARM_NAP_H +#define _IPXE_EFIARM_NAP_H + +/** @file + * + * EFI CPU sleeping + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef NAP_EFIARM +#define NAP_PREFIX_efiarm +#else +#define NAP_PREFIX_efiarm __efiarm_ +#endif + +#endif /* _IPXE_EFIARM_NAP_H */ diff --git a/src/arch/arm/include/limits.h b/src/arch/arm/include/limits.h new file mode 100644 index 00000000..bb48b75a --- /dev/null +++ b/src/arch/arm/include/limits.h @@ -0,0 +1,61 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/arm/include/setjmp.h b/src/arch/arm/include/setjmp.h new file mode 100644 index 00000000..4828b47a --- /dev/null +++ b/src/arch/arm/include/setjmp.h @@ -0,0 +1,38 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +/** A jump buffer */ +typedef struct { + /** Saved r4 */ + uint32_t r4; + /** Saved r5 */ + uint32_t r5; + /** Saved r6 */ + uint32_t r6; + /** Saved r7 */ + uint32_t r7; + /** Saved r8 */ + uint32_t r8; + /** Saved r9 */ + uint32_t r9; + /** Saved r10 */ + uint32_t r10; + /** Saved frame pointer (r11) */ + uint32_t fp; + /** Saved stack pointer (r13) */ + uint32_t sp; + /** Saved link register (r14) */ + uint32_t lr; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/src/arch/arm/interface/efi/efiarm_nap.c b/src/arch/arm/interface/efi/efiarm_nap.c new file mode 100644 index 00000000..9ed638e9 --- /dev/null +++ b/src/arch/arm/interface/efi/efiarm_nap.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/nap.h> +#include <ipxe/efi/efi.h> + +/** @file + * + * iPXE CPU sleeping API for EFI + * + */ + +/** + * Sleep until next interrupt + * + */ +static void efiarm_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + */ + __asm__ __volatile__ ( "wfi" ); +} + +PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap ); diff --git a/src/arch/arm/libgcc/lldivmod.S b/src/arch/arm/libgcc/lldivmod.S new file mode 100644 index 00000000..910be4b7 --- /dev/null +++ b/src/arch/arm/libgcc/lldivmod.S @@ -0,0 +1,50 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .thumb + +/** + * Unsigned long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_uldivmod", "ax", %progbits + .globl __aeabi_uldivmod + .type __aeabi_uldivmod, %function +__aeabi_uldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __udivmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __udivmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_uldivmod, . - __aeabi_uldivmod + +/** + * Signed long long division + * + * @v r1:r0 Dividend + * @v r3:r2 Divisor + * @ret r1:r0 Quotient + * @ret r3:r2 Remainder + */ + .section ".text.__aeabi_ldivmod", "ax", %progbits + .globl __aeabi_ldivmod + .type __aeabi_ldivmod, %function +__aeabi_ldivmod: + /* Allocate stack space for remainder and pointer to remainder */ + push {r0, r1, r2, r3, r4, lr} + /* Call __divmoddi4() */ + add r4, sp, #8 + str r4, [sp] + bl __divmoddi4 + /* Retrieve remainder and return */ + add sp, sp, #8 + pop {r2, r3, r4, pc} + .size __aeabi_ldivmod, . - __aeabi_ldivmod diff --git a/src/arch/arm/libgcc/llshift.S b/src/arch/arm/libgcc/llshift.S new file mode 100644 index 00000000..cc16e261 --- /dev/null +++ b/src/arch/arm/libgcc/llshift.S @@ -0,0 +1,88 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .arm + +/** + * Logical shift left + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsl", "ax", %progbits + .globl __aeabi_llsl + .type __aeabi_llsl, %function +__aeabi_llsl: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * high = ( low << ( shift - 32 ) ) + */ + movpl r1, r0, lsl r3 + /* If shift < 32, then + * high = ( ( high << shift ) | ( low >> ( 32 - shift ) ) ) + */ + movmi r1, r1, lsl r2 + rsbmi r3, r2, #32 + orrmi r1, r1, r0, lsr r3 + /* low = ( low << shift ) */ + mov r0, r0, lsl r2 + bx lr + .size __aeabi_llsl, . - __aeabi_llsl + +/** + * Logical shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_llsr", "ax", %progbits + .globl __aeabi_llsr + .type __aeabi_llsr, %function +__aeabi_llsr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, lsr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, lsr r2 + bx lr + .size __aeabi_llsr, . - __aeabi_llsr + +/** + * Arithmetic shift right + * + * @v r1:r0 Value to shift + * @v r2 Shift amount + * @ret r1:r0 Shifted value + */ + .section ".text.__aeabi_lasr", "ax", %progbits + .globl __aeabi_lasr + .type __aeabi_lasr, %function +__aeabi_lasr: + /* r3 = ( shift - 32 ) */ + subs r3, r2, #32 + /* If shift >= 32, then + * low = ( high >> ( shift - 32 ) ) + */ + movpl r0, r1, asr r3 + /* If shift < 32, then + * low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) ) + */ + movmi r0, r0, lsr r2 + rsbmi r3, r2, #32 + orrmi r0, r0, r1, lsl r3 + /* high = ( high >> shift ) */ + mov r1, r1, asr r2 + bx lr + .size __aeabi_lasr, . - __aeabi_lasr diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index ea9c31e2..8d3a8bf2 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -10,12 +10,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI -#define IOAPI_X86 #define IOMAP_VIRT #define PCIAPI_EFI #define CONSOLE_EFI #define TIMER_EFI -#define NAP_EFIX86 #define UMALLOC_EFI #define SMBIOS_EFI #define SANBOOT_NULL @@ -35,6 +33,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define USB_EFI /* Provide EFI_USB_IO_PROTOCOL interface */ #define REBOOT_CMD /* Reboot command */ + +#if defined ( __i386__ ) || defined ( __x86_64__ ) +#define IOAPI_X86 +#define NAP_EFIX86 #define CPUID_CMD /* x86 CPU feature detection command */ +#endif + +#if defined ( __arm__ ) +#define IOAPI_ARM +#define NAP_EFIARM +#endif #endif /* CONFIG_DEFAULTS_EFI_H */ diff --git a/src/util/efirom.c b/src/util/efirom.c index a982c19a..72829cbd 100644 --- a/src/util/efirom.c +++ b/src/util/efirom.c @@ -81,6 +81,7 @@ static void read_pe_info ( void *pe, uint16_t *machine, *machine = nt->nt32.FileHeader.Machine; switch ( *machine ) { case EFI_IMAGE_MACHINE_IA32: + case EFI_IMAGE_MACHINE_ARMTHUMB_MIXED: *subsystem = nt->nt32.OptionalHeader.Subsystem; break; case EFI_IMAGE_MACHINE_X64: diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 5e1050e3..01ddfd68 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -70,20 +70,14 @@ #endif -#define EFI_FILE_ALIGN 0x20 +#define ELF_MREL( mach, type ) ( (mach) | ( (type) << 16 ) ) -struct elf_machine { - unsigned int pe_machine; - unsigned int r_none; - unsigned int r_abs; - unsigned int r_pcrel; -}; +#define EFI_FILE_ALIGN 0x20 struct elf_file { void *data; size_t len; const Elf_Ehdr *ehdr; - struct elf_machine *machine; }; struct pe_section { @@ -136,20 +130,6 @@ static struct pe_header efi_pe_header = { }, }; -static struct elf_machine machine_i386 = { - .pe_machine = EFI_IMAGE_MACHINE_IA32, - .r_none = R_386_NONE, - .r_abs = R_386_32, - .r_pcrel = R_386_PC32, -}; - -static struct elf_machine machine_x86_64 = { - .pe_machine = EFI_IMAGE_MACHINE_X64, - .r_none = R_X86_64_NONE, - .r_abs = R_X86_64_64, - .r_pcrel = R_X86_64_PC32, -}; - /** Command-line options */ struct options { unsigned int subsystem; @@ -355,19 +335,6 @@ static void read_elf_file ( const char *name, struct elf_file *elf ) { exit ( 1 ); } } - - /* Identify architecture */ - switch ( ehdr->e_machine ) { - case EM_386: - elf->machine = &machine_i386; - break; - case EM_X86_64: - elf->machine = &machine_x86_64; - break; - default: - eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); - exit ( 1 ); - } } /** @@ -416,6 +383,36 @@ static const char * elf_string ( struct elf_file *elf, unsigned int section, } /** + * Set machine architecture + * + * @v elf ELF file + * @v pe_header PE file header + */ +static void set_machine ( struct elf_file *elf, struct pe_header *pe_header ) { + const Elf_Ehdr *ehdr = elf->ehdr; + uint16_t machine; + + /* Identify machine architecture */ + switch ( ehdr->e_machine ) { + case EM_386: + machine = EFI_IMAGE_MACHINE_IA32; + break; + case EM_X86_64: + machine = EFI_IMAGE_MACHINE_X64; + break; + case EM_ARM: + machine = EFI_IMAGE_MACHINE_ARMTHUMB_MIXED; + break; + default: + eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine ); + exit ( 1 ); + } + + /* Set machine architecture */ + pe_header->nt.FileHeader.Machine = machine; +} + +/** * Process section * * @v elf ELF file @@ -568,6 +565,7 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, const Elf_Rel *rel, struct pe_relocs **pe_reltab ) { unsigned int type = ELF_R_TYPE ( rel->r_info ); unsigned int sym = ELF_R_SYM ( rel->r_info ); + unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type ); size_t offset = ( shdr->sh_addr + rel->r_offset ); /* Look up symbol and process relocation */ @@ -579,18 +577,36 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr, /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ - } else if ( type == elf->machine->r_none ) { - /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ - } else if ( type == elf->machine->r_abs ) { - /* Generate an 8-byte or 4-byte PE relocation */ - generate_pe_reloc ( pe_reltab, offset, sizeof ( Elf_Addr ) ); - } else if ( type == elf->machine->r_pcrel ) { - /* Skip PC-relative relocations; all relative offsets - * remain unaltered when the object is loaded. - */ } else { - eprintf ( "Unrecognised relocation type %d\n", type ); - exit ( 1 ); + switch ( mrel ) { + case ELF_MREL ( EM_386, R_386_NONE ) : + case ELF_MREL ( EM_ARM, R_ARM_NONE ) : + case ELF_MREL ( EM_X86_64, R_X86_64_NONE ) : + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ + break; + case ELF_MREL ( EM_386, R_386_32 ) : + case ELF_MREL ( EM_ARM, R_ARM_ABS32 ) : + /* Generate a 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 4 ); + break; + case ELF_MREL ( EM_X86_64, R_X86_64_64 ) : + /* Generate an 8-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 8 ); + break; + case ELF_MREL ( EM_386, R_386_PC32 ) : + case ELF_MREL ( EM_ARM, R_ARM_CALL ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_PC22 ) : + case ELF_MREL ( EM_ARM, R_ARM_THM_JUMP24 ) : + case ELF_MREL ( EM_X86_64, R_X86_64_PC32 ) : + /* Skip PC-relative relocations; all relative + * offsets remain unaltered when the object is + * loaded. + */ + break; + default: + eprintf ( "Unrecognised relocation type %d\n", type ); + exit ( 1 ); + } } } @@ -835,7 +851,7 @@ static void elf2pe ( const char *elf_name, const char *pe_name, /* Initialise the PE header */ memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); - pe_header.nt.FileHeader.Machine = elf.machine->pe_machine; + set_machine ( &elf, &pe_header ); pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry; pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; |