[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:
Michael Brown
2024-09-19 16:23:32 +01:00
parent 59d123658b
commit 3def13265d
14 changed files with 355 additions and 612 deletions

View File

@@ -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" );
}
}
}

View File

@@ -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 */

View File

@@ -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" );
}
}
}

View File

@@ -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 */

View File

@@ -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" );
}
}
}

View File

@@ -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 */

View File

@@ -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 ) ) );
}
}
}

View File

@@ -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 */

View File

@@ -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 ) );
}
}
}

View File

@@ -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 */