mirror of
https://github.com/ipxe/ipxe
synced 2026-02-14 02:31:26 +03:00
[crypto] Use constant-time big integer multiplication
Big integer multiplication currently performs immediate carry propagation from each step of the long multiplication, relying on the fact that the overall result has a known maximum value to minimise the number of carries performed without ever needing to explicitly check against the result buffer size. This is not a constant-time algorithm, since the number of carries performed will be a function of the input values. We could make it constant-time by always continuing to propagate the carry until reaching the end of the result buffer, but this would introduce a large number of redundant zero carries. Require callers of bigint_multiply() to provide a temporary carry storage buffer, of the same size as the result buffer. This allows the carry-out from the accumulation of each double-element product to be accumulated in the temporary carry space, and then added in via a single call to bigint_add() after the multiplication is complete. Since the structure of big integer multiplication is identical across all current CPU architectures, provide a single shared implementation of bigint_multiply(). The architecture-specific operation then becomes the multiplication of two big integer elements and the accumulation of the double-element product. Note that any intermediate carry arising from accumulating the lower half of the double-element product may be added to the upper half of the double-element product without risk of overflow, since the result of multiplying two n-bit integers can never have all n bits set in its upper half. This simplifies the carry calculations for architectures such as RISC-V and LoongArch64 that do not have a carry flag. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* 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 multiplicand_size Number of elements in multiplicand
|
||||
* @v multiplier0 Element 0 of big integer to be multiplied
|
||||
* @v multiplier_size Number of elements in multiplier
|
||||
* @v result0 Element 0 of big integer to hold result
|
||||
*/
|
||||
void bigint_multiply_raw ( const uint32_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint32_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint32_t *result0 ) {
|
||||
unsigned int result_size = ( multiplicand_size + multiplier_size );
|
||||
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
|
||||
*multiplicand = ( ( const void * ) multiplicand0 );
|
||||
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
|
||||
*multiplier = ( ( const void * ) multiplier0 );
|
||||
bigint_t ( result_size ) __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 < multiplicand_size ; i++ ) {
|
||||
multiplicand_element = multiplicand->element[i];
|
||||
for ( j = 0 ; j < multiplier_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^{m} => ab < 2^{n+m}
|
||||
*/
|
||||
__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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,10 +309,34 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
|
||||
*(--out_byte) = *(value_byte++);
|
||||
}
|
||||
|
||||
extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint32_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint32_t *value0 );
|
||||
/**
|
||||
* Multiply big integer elements
|
||||
*
|
||||
* @v multiplicand Multiplicand element
|
||||
* @v multiplier Multiplier element
|
||||
* @v result Result element pair
|
||||
* @v carry Carry element
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
bigint_multiply_one ( const uint32_t multiplicand, const uint32_t multiplier,
|
||||
uint32_t *result, uint32_t *carry ) {
|
||||
uint32_t discard_low;
|
||||
uint32_t discard_high;
|
||||
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"umull %0, %1, %5, %6\n\t"
|
||||
/* Accumulate result */
|
||||
"adds %2, %0\n\t"
|
||||
"adcs %3, %1\n\t"
|
||||
/* Accumulate carry (cannot overflow) */
|
||||
"adc %4, #0\n\t"
|
||||
: "=r" ( discard_low ),
|
||||
"=r" ( discard_high ),
|
||||
"+r" ( result[0] ),
|
||||
"+r" ( result[1] ),
|
||||
"+r" ( *carry )
|
||||
: "r" ( multiplicand ),
|
||||
"r" ( multiplier ) );
|
||||
}
|
||||
|
||||
#endif /* _BITS_BIGINT_H */
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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 multiplicand_size Number of elements in multiplicand
|
||||
* @v multiplier0 Element 0 of big integer to be multiplied
|
||||
* @v multiplier_size Number of elements in multiplier
|
||||
* @v result0 Element 0 of big integer to hold result
|
||||
*/
|
||||
void bigint_multiply_raw ( const uint64_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint64_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint64_t *result0 ) {
|
||||
unsigned int result_size = ( multiplicand_size + multiplier_size );
|
||||
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
|
||||
*multiplicand = ( ( const void * ) multiplicand0 );
|
||||
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
|
||||
*multiplier = ( ( const void * ) multiplier0 );
|
||||
bigint_t ( result_size ) __attribute__ (( may_alias ))
|
||||
*result = ( ( void * ) result0 );
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
uint64_t multiplicand_element;
|
||||
uint64_t multiplier_element;
|
||||
uint64_t *result_elements;
|
||||
uint64_t discard_low;
|
||||
uint64_t discard_high;
|
||||
uint64_t discard_temp_low;
|
||||
uint64_t discard_temp_high;
|
||||
|
||||
/* Zero result */
|
||||
memset ( result, 0, sizeof ( *result ) );
|
||||
|
||||
/* Multiply integers one element at a time */
|
||||
for ( i = 0 ; i < multiplicand_size ; i++ ) {
|
||||
multiplicand_element = multiplicand->element[i];
|
||||
for ( j = 0 ; j < multiplier_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^{m} => ab < 2^{n+m}
|
||||
*/
|
||||
__asm__ __volatile__ ( "mul %1, %6, %7\n\t"
|
||||
"umulh %2, %6, %7\n\t"
|
||||
"ldp %3, %4, [%0]\n\t"
|
||||
"adds %3, %3, %1\n\t"
|
||||
"adcs %4, %4, %2\n\t"
|
||||
"stp %3, %4, [%0], #16\n\t"
|
||||
"bcc 2f\n\t"
|
||||
"\n1:\n\t"
|
||||
"ldr %3, [%0]\n\t"
|
||||
"adcs %3, %3, xzr\n\t"
|
||||
"str %3, [%0], #8\n\t"
|
||||
"bcs 1b\n\t"
|
||||
"\n2:\n\t"
|
||||
: "+r" ( result_elements ),
|
||||
"=&r" ( discard_low ),
|
||||
"=&r" ( discard_high ),
|
||||
"=r" ( discard_temp_low ),
|
||||
"=r" ( discard_temp_high ),
|
||||
"+m" ( *result )
|
||||
: "r" ( multiplicand_element ),
|
||||
"r" ( multiplier_element )
|
||||
: "cc" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,10 +310,35 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
|
||||
*(--out_byte) = *(value_byte++);
|
||||
}
|
||||
|
||||
extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint64_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint64_t *value0 );
|
||||
/**
|
||||
* Multiply big integer elements
|
||||
*
|
||||
* @v multiplicand Multiplicand element
|
||||
* @v multiplier Multiplier element
|
||||
* @v result Result element pair
|
||||
* @v carry Carry element
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
bigint_multiply_one ( const uint64_t multiplicand, const uint64_t multiplier,
|
||||
uint64_t *result, uint64_t *carry ) {
|
||||
uint64_t discard_low;
|
||||
uint64_t discard_high;
|
||||
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"mul %0, %5, %6\n\t"
|
||||
"umulh %1, %5, %6\n\t"
|
||||
/* Accumulate result */
|
||||
"adds %2, %2, %0\n\t"
|
||||
"adcs %3, %3, %1\n\t"
|
||||
/* Accumulate carry (cannot overflow) */
|
||||
"adc %4, %4, xzr\n\t"
|
||||
: "=&r" ( discard_low ),
|
||||
"=r" ( discard_high ),
|
||||
"+r" ( result[0] ),
|
||||
"+r" ( result[1] ),
|
||||
"+r" ( *carry )
|
||||
: "r" ( multiplicand ),
|
||||
"r" ( multiplier ) );
|
||||
}
|
||||
|
||||
#endif /* _BITS_BIGINT_H */
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
* Copyright (c) 2023, Xiaotian Wu <wuxiaotian@loongson.cn>
|
||||
*
|
||||
* 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 multiplicand_size Number of elements in multiplicand
|
||||
* @v multiplier0 Element 0 of big integer to be multiplied
|
||||
* @v multiplier_size Number of elements in multiplier
|
||||
* @v result0 Element 0 of big integer to hold result
|
||||
*/
|
||||
void bigint_multiply_raw ( const uint64_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint64_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint64_t *result0 ) {
|
||||
unsigned int result_size = ( multiplicand_size + multiplier_size );
|
||||
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
|
||||
*multiplicand = ( ( const void * ) multiplicand0 );
|
||||
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
|
||||
*multiplier = ( ( const void * ) multiplier0 );
|
||||
bigint_t ( result_size ) __attribute__ (( may_alias ))
|
||||
*result = ( ( void * ) result0 );
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
uint64_t multiplicand_element;
|
||||
uint64_t multiplier_element;
|
||||
uint64_t *result_elements;
|
||||
uint64_t discard_low;
|
||||
uint64_t discard_high;
|
||||
uint64_t discard_temp_low;
|
||||
uint64_t discard_temp_high;
|
||||
|
||||
/* Zero result */
|
||||
memset ( result, 0, sizeof ( *result ) );
|
||||
|
||||
/* Multiply integers one element at a time */
|
||||
for ( i = 0 ; i < multiplicand_size ; i++ ) {
|
||||
multiplicand_element = multiplicand->element[i];
|
||||
for ( j = 0 ; j < multiplier_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^{m} => ab < 2^{n+m}
|
||||
*/
|
||||
__asm__ __volatile__ ( "mul.d %1, %6, %7\n\t"
|
||||
"mulh.du %2, %6, %7\n\t"
|
||||
|
||||
"ld.d %3, %0, 0\n\t"
|
||||
"ld.d %4, %0, 8\n\t"
|
||||
|
||||
"add.d %3, %3, %1\n\t"
|
||||
"sltu $t0, %3, %1\n\t"
|
||||
|
||||
"add.d %4, %4, %2\n\t"
|
||||
"sltu $t1, %4, %2\n\t"
|
||||
|
||||
"add.d %4, %4, $t0\n\t"
|
||||
"sltu $t0, %4, $t0\n\t"
|
||||
"or $t0, $t0, $t1\n\t"
|
||||
|
||||
"st.d %3, %0, 0\n\t"
|
||||
"st.d %4, %0, 8\n\t"
|
||||
|
||||
"addi.d %0, %0, 16\n\t"
|
||||
"beqz $t0, 2f\n"
|
||||
"1:\n\t"
|
||||
"ld.d %3, %0, 0\n\t"
|
||||
"add.d %3, %3, $t0\n\t"
|
||||
"sltu $t0, %3, $t0\n\t"
|
||||
"st.d %3, %0, 0\n\t"
|
||||
"addi.d %0, %0, 8\n\t"
|
||||
"bnez $t0, 1b\n"
|
||||
"2:"
|
||||
: "+r" ( result_elements ),
|
||||
"=&r" ( discard_low ),
|
||||
"=&r" ( discard_high ),
|
||||
"=r" ( discard_temp_low ),
|
||||
"=r" ( discard_temp_high ),
|
||||
"+m" ( *result )
|
||||
: "r" ( multiplicand_element ),
|
||||
"r" ( multiplier_element )
|
||||
: "t0", "t1" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,10 +357,42 @@ bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
|
||||
*(--out_byte) = *(value_byte++);
|
||||
}
|
||||
|
||||
extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint64_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint64_t *value0 );
|
||||
/**
|
||||
* Multiply big integer elements
|
||||
*
|
||||
* @v multiplicand Multiplicand element
|
||||
* @v multiplier Multiplier element
|
||||
* @v result Result element pair
|
||||
* @v carry Carry element
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
bigint_multiply_one ( const uint64_t multiplicand, const uint64_t multiplier,
|
||||
uint64_t *result, uint64_t *carry ) {
|
||||
uint64_t discard_low;
|
||||
uint64_t discard_high;
|
||||
uint64_t discard_carry;
|
||||
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"mul.d %0, %6, %7\n\t"
|
||||
"mulh.du %1, %6, %7\n\t"
|
||||
/* Accumulate low half */
|
||||
"add.d %3, %3, %0\n\t"
|
||||
"sltu %2, %3, %0\n\t"
|
||||
/* Add carry to high half (cannot overflow) */
|
||||
"add.d %1, %1, %2\n\t"
|
||||
/* Accumulate high half */
|
||||
"add.d %4, %4, %1\n\t"
|
||||
"sltu %2, %4, %1\n\t"
|
||||
/* Accumulate carry (cannot overflow) */
|
||||
"add.d %5, %5, %2\n\t"
|
||||
: "=&r" ( discard_low ),
|
||||
"=r" ( discard_high ),
|
||||
"=r" ( discard_carry ),
|
||||
"+r" ( result[0] ),
|
||||
"+r" ( result[1] ),
|
||||
"+r" ( *carry )
|
||||
: "r" ( multiplicand ),
|
||||
"r" ( multiplier ) );
|
||||
}
|
||||
|
||||
#endif /* _BITS_BIGINT_H */
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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 multiplicand_size Number of elements in multiplicand
|
||||
* @v multiplier0 Element 0 of big integer to be multiplied
|
||||
* @v multiplier_size Number of elements in multiplier
|
||||
* @v result0 Element 0 of big integer to hold result
|
||||
*/
|
||||
void bigint_multiply_raw ( const unsigned long *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const unsigned long *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
unsigned long *result0 ) {
|
||||
unsigned int result_size = ( multiplicand_size + multiplier_size );
|
||||
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
|
||||
*multiplicand = ( ( const void * ) multiplicand0 );
|
||||
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
|
||||
*multiplier = ( ( const void * ) multiplier0 );
|
||||
bigint_t ( result_size ) __attribute__ (( may_alias ))
|
||||
*result = ( ( void * ) result0 );
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
unsigned long multiplicand_element;
|
||||
unsigned long multiplier_element;
|
||||
unsigned long *result_elements;
|
||||
unsigned long discard_low;
|
||||
unsigned long discard_high;
|
||||
unsigned long discard_temp;
|
||||
unsigned long discard_carry;
|
||||
|
||||
/* Zero result */
|
||||
memset ( result, 0, sizeof ( *result ) );
|
||||
|
||||
/* Multiply integers one element at a time */
|
||||
for ( i = 0 ; i < multiplicand_size ; i++ ) {
|
||||
multiplicand_element = multiplicand->element[i];
|
||||
for ( j = 0 ; j < multiplier_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^{m} => ab < 2^{n+m}
|
||||
*/
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"mulhu %2, %6, %7\n\t"
|
||||
"mul %1, %6, %7\n\t"
|
||||
/* Accumulate low half */
|
||||
LOADN " %3, (%0)\n\t"
|
||||
"add %3, %3, %1\n\t"
|
||||
"sltu %4, %3, %1\n\t"
|
||||
STOREN " %3, 0(%0)\n\t"
|
||||
/* Carry into high half */
|
||||
"add %4, %4, %2\n\t"
|
||||
/* Propagate as necessary */
|
||||
"\n1:\n\t"
|
||||
"addi %0, %0, %8\n\t"
|
||||
LOADN " %3, 0(%0)\n\t"
|
||||
"add %3, %3, %4\n\t"
|
||||
"sltu %4, %3, %4\n\t"
|
||||
STOREN " %3, 0(%0)\n\t"
|
||||
"bnez %4, 1b\n\t"
|
||||
: "+r" ( result_elements ),
|
||||
"=r" ( discard_low ),
|
||||
"=r" ( discard_high ),
|
||||
"=r" ( discard_temp ),
|
||||
"=r" ( discard_carry ),
|
||||
"+m" ( *result )
|
||||
: "r" ( multiplicand_element ),
|
||||
"r" ( multiplier_element ),
|
||||
"i" ( sizeof ( *result0 ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,10 +353,43 @@ bigint_done_raw ( const unsigned long *value0, unsigned int size __unused,
|
||||
*(--out_byte) = *(value_byte++);
|
||||
}
|
||||
|
||||
extern void bigint_multiply_raw ( const unsigned long *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const unsigned long *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
unsigned long *value0 );
|
||||
/**
|
||||
* Multiply big integer elements
|
||||
*
|
||||
* @v multiplicand Multiplicand element
|
||||
* @v multiplier Multiplier element
|
||||
* @v result Result element pair
|
||||
* @v carry Carry element
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
bigint_multiply_one ( const unsigned long multiplicand,
|
||||
const unsigned long multiplier,
|
||||
unsigned long *result, unsigned long *carry ) {
|
||||
unsigned long discard_low;
|
||||
unsigned long discard_high;
|
||||
unsigned long discard_carry;
|
||||
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"mulhu %1, %6, %7\n\t"
|
||||
"mul %0, %6, %7\n\t"
|
||||
/* Accumulate low half */
|
||||
"add %3, %3, %0\n\t"
|
||||
"sltu %2, %3, %0\n\t"
|
||||
/* Add carry to high half (cannot overflow) */
|
||||
"add %1, %1, %2\n\t"
|
||||
/* Accumulate high half */
|
||||
"add %4, %4, %1\n\t"
|
||||
"sltu %2, %4, %1\n\t"
|
||||
/* Accumulate carry (cannot overflow) */
|
||||
"add %5, %5, %2\n\t"
|
||||
: "=r" ( discard_low ),
|
||||
"=&r" ( discard_high ),
|
||||
"=r" ( discard_carry ),
|
||||
"+r" ( result[0] ),
|
||||
"+r" ( result[1] ),
|
||||
"+r" ( *carry )
|
||||
: "r" ( multiplicand ),
|
||||
"r" ( multiplier ) );
|
||||
}
|
||||
|
||||
#endif /* _BITS_BIGINT_H */
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 multiplicand_size Number of elements in multiplicand
|
||||
* @v multiplier0 Element 0 of big integer to be multiplied
|
||||
* @v multiplier_size Number of elements in multiplier
|
||||
* @v result0 Element 0 of big integer to hold result
|
||||
*/
|
||||
void bigint_multiply_raw ( const uint32_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint32_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint32_t *result0 ) {
|
||||
unsigned int result_size = ( multiplicand_size + multiplier_size );
|
||||
const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
|
||||
*multiplicand = ( ( const void * ) multiplicand0 );
|
||||
const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
|
||||
*multiplier = ( ( const void * ) multiplier0 );
|
||||
bigint_t ( result_size ) __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_a;
|
||||
uint32_t discard_d;
|
||||
long index;
|
||||
|
||||
/* Zero result */
|
||||
memset ( result, 0, sizeof ( *result ) );
|
||||
|
||||
/* Multiply integers one element at a time */
|
||||
for ( i = 0 ; i < multiplicand_size ; i++ ) {
|
||||
multiplicand_element = multiplicand->element[i];
|
||||
for ( j = 0 ; j < multiplier_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^{m} => ab < 2^{n+m}
|
||||
*/
|
||||
__asm__ __volatile__ ( "mull %5\n\t"
|
||||
"addl %%eax, (%6,%2,4)\n\t"
|
||||
"adcl %%edx, 4(%6,%2,4)\n\t"
|
||||
"\n1:\n\t"
|
||||
"adcl $0, 8(%6,%2,4)\n\t"
|
||||
"inc %2\n\t"
|
||||
/* Does not affect CF */
|
||||
"jc 1b\n\t"
|
||||
: "=&a" ( discard_a ),
|
||||
"=&d" ( discard_d ),
|
||||
"=&r" ( index ),
|
||||
"+m" ( *result )
|
||||
: "0" ( multiplicand_element ),
|
||||
"g" ( multiplier_element ),
|
||||
"r" ( result_elements ),
|
||||
"2" ( 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,10 +322,34 @@ bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
|
||||
: "eax" );
|
||||
}
|
||||
|
||||
extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
|
||||
unsigned int multiplicand_size,
|
||||
const uint32_t *multiplier0,
|
||||
unsigned int multiplier_size,
|
||||
uint32_t *value0 );
|
||||
/**
|
||||
* Multiply big integer elements
|
||||
*
|
||||
* @v multiplicand Multiplicand element
|
||||
* @v multiplier Multiplier element
|
||||
* @v result Result element pair
|
||||
* @v carry Carry element
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
bigint_multiply_one ( const uint32_t multiplicand, const uint32_t multiplier,
|
||||
uint32_t *result, uint32_t *carry ) {
|
||||
uint32_t discard_a;
|
||||
uint32_t discard_d;
|
||||
|
||||
__asm__ __volatile__ ( /* Perform multiplication */
|
||||
"mull %6\n\t"
|
||||
/* Accumulate result */
|
||||
"addl %0, %2\n\t"
|
||||
"adcl %1, %3\n\t"
|
||||
/* Accumulate carry (cannot overflow) */
|
||||
"adcl $0, %4\n\t"
|
||||
: "=a" ( discard_a ),
|
||||
"=d" ( discard_d ),
|
||||
"+m" ( result[0] ),
|
||||
"+m" ( result[1] ),
|
||||
"+m" ( *carry )
|
||||
: "0" ( multiplicand ),
|
||||
"g" ( multiplier ) );
|
||||
}
|
||||
|
||||
#endif /* _BITS_BIGINT_H */
|
||||
|
||||
Reference in New Issue
Block a user