Compare commits

...

3 Commits

Author SHA1 Message Date
21940425c4 [riscv] Add support for reboot and power off via SBI
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-10-22 15:13:59 +01:00
b23204b383 [riscv] Add support for the SBI debug console
Add the ability to issue Supervisor Binary Interface (SBI) calls via
the ECALL instruction, and use the SBI DBCN extension to implement a
debug console.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-10-22 12:51:48 +01:00
fa1c24d14b [crypto] Add bigint_mod_invert() to calculate inverse modulo a power of two
Montgomery multiplication requires calculating the inverse of the
modulus modulo a larger power of two.

Add bigint_mod_invert() to calculate the inverse of any (odd) big
integer modulo an arbitrary power of two, using a lightly modified
version of the algorithm presented in "A New Algorithm for Inversion
mod p^k (Koç, 2017)".

The power of two is taken to be 2^k, where k is the number of bits
available in the big integer representation of the invertend.  The
inverse modulo any smaller power of two may be obtained simply by
masking off the relevant bits in the inverse.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-10-21 17:24:53 +01:00
12 changed files with 526 additions and 0 deletions

View File

@ -10,6 +10,7 @@ INCDIRS := arch/$(ARCH)/include arch/riscv/include $(INCDIRS)
# RISCV-specific directories containing source files
#
SRCDIRS += arch/riscv/core
SRCDIRS += arch/riscv/interface/sbi
# RISCV-specific flags
#

View File

@ -14,6 +14,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @{
*/
#define ERRFILE_sbi_reboot ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 )
/** @} */
#endif /* _BITS_ERRFILE_H */

View File

@ -0,0 +1,14 @@
#ifndef _BITS_REBOOT_H
#define _BITS_REBOOT_H
/** @file
*
* RISCV-specific reboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sbi_reboot.h>
#endif /* _BITS_REBOOT_H */

View File

@ -0,0 +1,167 @@
#ifndef _IPXE_SBI_H
#define _IPXE_SBI_H
/** @file
*
* Supervisor Binary Interface (SBI)
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** An SBI function return value */
struct sbi_return {
/** Error status (returned in a0) */
long error;
/** Data value (returned in a1) */
long value;
};
/**
* @defgroup sbierrors SBI errors
*
* *{
*/
#define SBI_SUCCESS 0 /**< Completed successfully */
#define SBI_ERR_FAILED -1 /**< Failed */
#define SBI_ERR_NOT_SUPPORTED -2 /**< Not supported */
#define SBI_ERR_INVALID_PARAM -3 /**< Invalid parameter(s) */
#define SBI_ERR_DENIED -4 /**< Denied or not allowed */
#define SBI_ERR_INVALID_ADDRESS -5 /**< Invalid address(es) */
#define SBI_ERR_ALREADY_AVAILABLE -6 /**< Already available */
#define SBI_ERR_ALREADY_STARTED -7 /**< Already started */
#define SBI_ERR_ALREADY_STOPPED -8 /**< Already stopped */
#define SBI_ERR_NO_SHMEM -9 /**< Shared memory not available */
#define SBI_ERR_INVALID_STATE -10 /**< Invalid state */
#define SBI_ERR_BAD_RANGE -11 /**< Bad (or invalid) range */
#define SBI_ERR_TIMEOUT -12 /**< Failed due to timeout */
#define SBI_ERR_IO -13 /**< Input/output error */
/** @} */
/** Construct SBI extension ID */
#define SBI_EID( c1, c2, c3, c4 ) \
( (int) ( ( (c1) << 24 ) | ( (c2) << 16 ) | ( (c3) << 8 ) | (c4) ) )
/**
* Call supervisor with no parameters
*
* @v eid Extension ID
* @v fid Function ID
* @ret ret Return value
*/
static inline __attribute__ (( always_inline )) struct sbi_return
sbi_ecall_0 ( int eid, int fid ) {
register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
register unsigned long a0 asm ( "a0" );
register unsigned long a1 asm ( "a1" );
struct sbi_return ret;
__asm__ __volatile__ ( "ecall"
: "=r" ( a0 ), "=r" ( a1 )
: "r" ( a6 ), "r" ( a7 )
: "memory" );
ret.error = a0;
ret.value = a1;
return ret;
}
/**
* Call supervisor with one parameter
*
* @v eid Extension ID
* @v fid Function ID
* @v param0 Parameter 0
* @ret ret Return value
*/
static inline __attribute__ (( always_inline )) struct sbi_return
sbi_ecall_1 ( int eid, int fid, unsigned long p0 ) {
register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
register unsigned long a0 asm ( "a0" ) = p0;
register unsigned long a1 asm ( "a1" );
struct sbi_return ret;
__asm__ __volatile__ ( "ecall"
: "+r" ( a0 ), "=r" ( a1 )
: "r" ( a6 ), "r" ( a7 )
: "memory" );
ret.error = a0;
ret.value = a1;
return ret;
}
/**
* Call supervisor with two parameters
*
* @v eid Extension ID
* @v fid Function ID
* @v param0 Parameter 0
* @v param1 Parameter 1
* @ret ret Return value
*/
static inline __attribute__ (( always_inline )) struct sbi_return
sbi_ecall_2 ( int eid, int fid, unsigned long p0, unsigned long p1 ) {
register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
register unsigned long a0 asm ( "a0" ) = p0;
register unsigned long a1 asm ( "a1" ) = p1;
struct sbi_return ret;
__asm__ __volatile__ ( "ecall"
: "+r" ( a0 ), "+r" ( a1 )
: "r" ( a6 ), "r" ( a7 )
: "memory" );
ret.error = a0;
ret.value = a1;
return ret;
}
/**
* Call supervisor with three parameters
*
* @v eid Extension ID
* @v fid Function ID
* @v param0 Parameter 0
* @v param1 Parameter 1
* @v param2 Parameter 2
* @ret ret Return value
*/
static inline __attribute__ (( always_inline )) struct sbi_return
sbi_ecall_3 ( int eid, int fid, unsigned long p0, unsigned long p1,
unsigned long p2 ) {
register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
register unsigned long a0 asm ( "a0" ) = p0;
register unsigned long a1 asm ( "a1" ) = p1;
register unsigned long a2 asm ( "a2" ) = p2;
struct sbi_return ret;
__asm__ __volatile__ ( "ecall"
: "+r" ( a0 ), "+r" ( a1 )
: "r" ( a2 ), "r" ( a6 ), "r" ( a7 )
: "memory" );
ret.error = a0;
ret.value = a1;
return ret;
}
/** Convert an SBI error code to an iPXE status code */
#define ESBI( error ) EPLATFORM ( EINFO_EPLATFORM, error )
/** System reset extension */
#define SBI_SRST SBI_EID ( 'S', 'R', 'S', 'T' )
#define SBI_SRST_SYSTEM_RESET 0x00 /**< Reset system */
#define SBI_RESET_SHUTDOWN 0x00000000 /**< Shutdown */
#define SBI_RESET_COLD 0x00000001 /**< Cold reboot */
#define SBI_RESET_WARM 0x00000002 /**< Warm reboot */
/** Debug console extension */
#define SBI_DBCN SBI_EID ( 'D', 'B', 'C', 'N' )
#define SBI_DBCN_WRITE 0x00 /**< Console Write */
#define SBI_DBCN_READ 0x01 /**< Console Read */
#define SBI_DBCN_WRITE_BYTE 0x02 /**< Console Write Byte */
#endif /* _IPXE_SBI_H */

View File

@ -0,0 +1,18 @@
#ifndef _IPXE_BIOS_REBOOT_H
#define _IPXE_BIOS_REBOOT_H
/** @file
*
* Supervisor Binary Interface (SBI) reboot mechanism
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef REBOOT_SBI
#define REBOOT_PREFIX_sbi
#else
#define REBOOT_PREFIX_sbi __sbi_
#endif
#endif /* _IPXE_BIOS_REBOOT_H */

View File

@ -0,0 +1,101 @@
/*
* 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
*
* SBI debug console
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/sbi.h>
#include <ipxe/io.h>
#include <ipxe/console.h>
#include <config/console.h>
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_SBI ) && CONSOLE_EXPLICIT ( CONSOLE_SBI ) )
#undef CONSOLE_SBI
#define CONSOLE_SBI ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
/** Buffered input character (if any) */
static unsigned char sbi_console_input;
/**
* Print a character to SBI console
*
* @v character Character to be printed
*/
static void sbi_putchar ( int character ) {
/* Write byte to console */
sbi_ecall_1 ( SBI_DBCN, SBI_DBCN_WRITE_BYTE, character );
}
/**
* Get character from SBI console
*
* @ret character Character read from console, if any
*/
static int sbi_getchar ( void ) {
int character;
/* Consume and return buffered character, if any */
character = sbi_console_input;
sbi_console_input = 0;
return character;
}
/**
* Check for character ready to read from SBI console
*
* @ret True Character available to read
* @ret False No character available to read
*/
static int sbi_iskey ( void ) {
struct sbi_return ret;
/* Do nothing if we already have a buffered character */
if ( sbi_console_input )
return sbi_console_input;
/* Read and buffer byte from console, if any */
ret = sbi_ecall_3 ( SBI_DBCN, SBI_DBCN_READ,
sizeof ( sbi_console_input ),
virt_to_phys ( &sbi_console_input ), 0 );
if ( ret.error )
return 0;
/* Return number of characters read and buffered */
return ret.value;
}
/** SBI console */
struct console_driver sbi_console_driver __console_driver = {
.putchar = sbi_putchar,
.getchar = sbi_getchar,
.iskey = sbi_iskey,
.usage = CONSOLE_SBI,
};

View File

@ -0,0 +1,76 @@
/*
* 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 );
/** @file
*
* Supervisor Binary Interface (SBI) reboot mechanism
*
*/
#include <errno.h>
#include <string.h>
#include <ipxe/sbi.h>
#include <ipxe/reboot.h>
/**
* Reboot system
*
* @v warm Perform a warm reboot
*/
static void sbi_reboot ( int warm ) {
struct sbi_return ret;
int rc;
/* Reboot system */
ret = sbi_ecall_2 ( SBI_SRST, SBI_SRST_SYSTEM_RESET,
( warm ? SBI_RESET_WARM : SBI_RESET_COLD ), 0 );
/* Any return is an error */
rc = -ESBI ( ret.error );
DBGC ( SBI_SRST, "SBI %s reset failed: %s\n",
( warm ? "warm" : "cold" ), strerror ( rc ) );
}
/**
* Power off system
*
* @ret rc Return status code
*/
static int sbi_poweroff ( void ) {
struct sbi_return ret;
int rc;
/* Shut down system */
ret = sbi_ecall_2 ( SBI_SRST, SBI_SRST_SYSTEM_RESET,
SBI_RESET_SHUTDOWN, 0 );
/* Any return is an error */
rc = -ESBI ( ret.error );
DBGC ( SBI_SRST, "SBI shutdown failed: %s\n", strerror ( rc ) );
return rc;
}
PROVIDE_REBOOT ( sbi, reboot, sbi_reboot );
PROVIDE_REBOOT ( sbi, poweroff, sbi_poweroff );

View File

@ -78,6 +78,9 @@ REQUIRE_OBJECT ( vmconsole );
#ifdef CONSOLE_DEBUGCON
REQUIRE_OBJECT ( debugcon );
#endif
#ifdef CONSOLE_SBI
REQUIRE_OBJECT ( sbi_console );
#endif
/*
* Drag in all requested network protocols

View File

@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
//#define CONSOLE_VMWARE /* VMware logfile console */
//#define CONSOLE_DEBUGCON /* Bochs/QEMU/KVM debug port console */
//#define CONSOLE_INT13 /* INT13 disk log console */
//#define CONSOLE_SBI /* RISC-V SBI debug console */
/*
* Very obscure console types

View File

@ -305,6 +305,60 @@ void bigint_reduce_raw ( const bigint_element_t *minuend0,
profile_stop ( &bigint_mod_profiler );
}
/**
* Compute inverse of odd big integer modulo any power of two
*
* @v invertend0 Element 0 of odd big integer to be inverted
* @v inverse0 Element 0 of big integer to hold result
* @v size Number of elements in invertend and result
* @v tmp Temporary working space
*/
void bigint_mod_invert_raw ( const bigint_element_t *invertend0,
bigint_element_t *inverse0,
unsigned int size, void *tmp ) {
const bigint_t ( size ) __attribute__ (( may_alias ))
*invertend = ( ( const void * ) invertend0 );
bigint_t ( size ) __attribute__ (( may_alias ))
*inverse = ( ( void * ) inverse0 );
struct {
bigint_t ( size ) residue;
} *temp = tmp;
const unsigned int width = ( 8 * sizeof ( bigint_element_t ) );
unsigned int i;
/* Sanity check */
assert ( invertend->element[0] & 1 );
/* Initialise temporary working space and output value */
memset ( &temp->residue, 0xff, sizeof ( temp->residue ) );
memset ( inverse, 0, sizeof ( *inverse ) );
/* Compute inverse modulo 2^(width)
*
* This method is a lightly modified version of the pseudocode
* presented in "A New Algorithm for Inversion mod p^k (Koç,
* 2017)".
*
* Each loop iteration calculates one bit of the inverse. The
* residue value is the two's complement negation of the value
* "b" as used by Koç, to allow for division by two using a
* logical right shift (since we have no arithmetic right
* shift operation for big integers).
*
* Due to the suffix property of inverses mod 2^k, the result
* represents the least significant bits of the inverse modulo
* an arbitrarily large 2^k.
*/
for ( i = 0 ; i < ( 8 * sizeof ( *inverse ) ) ; i++ ) {
if ( temp->residue.element[0] & 1 ) {
inverse->element[ i / width ] |=
( 1UL << ( i % width ) );
bigint_add ( invertend, &temp->residue );
}
bigint_shr ( &temp->residue );
}
}
/**
* Perform modular multiplication of big integers
*

View File

@ -246,6 +246,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
bigint_t ( size ) temp_modulus; \
} ); } )
/**
* Compute inverse of odd big integer modulo its own size
*
* @v invertend Odd big integer to be inverted
* @v inverse Big integer to hold result
* @v tmp Temporary working space
*/
#define bigint_mod_invert( invertend, inverse, tmp ) do { \
unsigned int size = bigint_size (invertend); \
bigint_mod_invert_raw ( (invertend)->element, \
(inverse)->element, size, tmp ); \
} while ( 0 )
/**
* Calculate temporary working space required for modular inversion
*
* @v invertend Odd big integer to be inverted
* @ret len Length of temporary working space
*/
#define bigint_mod_invert_tmp_len( invertend ) ( { \
unsigned int size = bigint_size (invertend); \
sizeof ( struct { \
bigint_t ( size ) temp_residue; \
} ); } )
/**
* Perform modular multiplication of big integers
*
@ -373,6 +398,9 @@ void bigint_reduce_raw ( const bigint_element_t *minuend0,
const bigint_element_t *modulus0,
unsigned int modulus_size,
bigint_element_t *result0, void *tmp );
void bigint_mod_invert_raw ( const bigint_element_t *invertend0,
bigint_element_t *inverse0,
unsigned int size, void *tmp );
void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0,
const bigint_element_t *multiplier0,
const bigint_element_t *modulus0,

View File

@ -200,6 +200,17 @@ void bigint_reduce_sample ( const bigint_element_t *minuend0,
bigint_reduce ( minuend, modulus, result, tmp );
}
void bigint_mod_invert_sample ( const bigint_element_t *invertend0,
bigint_element_t *inverse0,
unsigned int size, void *tmp ) {
const bigint_t ( size ) __attribute__ (( may_alias ))
*invertend = ( ( const void * ) invertend0 );
bigint_t ( size ) __attribute__ (( may_alias ))
*inverse = ( ( void * ) inverse0 );
bigint_mod_invert ( invertend, inverse, tmp );
}
void bigint_mod_multiply_sample ( const bigint_element_t *multiplicand0,
const bigint_element_t *multiplier0,
const bigint_element_t *modulus0,
@ -573,6 +584,39 @@ void bigint_mod_exp_sample ( const bigint_element_t *base0,
sizeof ( result_raw ) ) == 0 ); \
} while ( 0 )
/**
* Report result of big integer modular inversion test
*
* @v invertend Big integer to be inverted
* @v expected Big integer expected result
*/
#define bigint_mod_invert_ok( invertend, expected ) do { \
static const uint8_t invertend_raw[] = invertend; \
static const uint8_t expected_raw[] = expected; \
uint8_t inverse_raw[ sizeof ( expected_raw ) ]; \
unsigned int size = \
bigint_required_size ( sizeof ( invertend_raw ) ); \
bigint_t ( size ) invertend_temp; \
bigint_t ( size ) inverse_temp; \
size_t tmp_len = bigint_mod_invert_tmp_len ( &invertend_temp ); \
uint8_t tmp[tmp_len]; \
{} /* Fix emacs alignment */ \
\
assert ( bigint_size ( &invertend_temp ) == \
bigint_size ( &inverse_temp ) ); \
bigint_init ( &invertend_temp, invertend_raw, \
sizeof ( invertend_raw ) ); \
DBG ( "Modular invert:\n" ); \
DBG_HDA ( 0, &invertend_temp, sizeof ( invertend_temp ) ); \
bigint_mod_invert ( &invertend_temp, &inverse_temp, tmp ); \
DBG_HDA ( 0, &inverse_temp, sizeof ( inverse_temp ) ); \
bigint_done ( &inverse_temp, inverse_raw, \
sizeof ( inverse_raw ) ); \
\
ok ( memcmp ( inverse_raw, expected_raw, \
sizeof ( inverse_raw ) ) == 0 ); \
} while ( 0 )
/**
* Report result of big integer modular multiplication test
*
@ -1760,6 +1804,23 @@ static void bigint_test_exec ( void ) {
0xfb, 0x5d, 0x55 ),
BIGINT ( 0x27, 0x31, 0x49, 0xc3, 0xf5, 0x06, 0x1f,
0x3c, 0x7c, 0xd5 ) );
bigint_mod_invert_ok ( BIGINT ( 0x01 ), BIGINT ( 0x01 ) );
bigint_mod_invert_ok ( BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff ),
BIGINT ( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff ) );
bigint_mod_invert_ok ( BIGINT ( 0x95, 0x6a, 0xc5, 0xe7, 0x2e, 0x5b,
0x44, 0xed, 0xbf, 0x7e, 0xfe, 0x8d,
0xf4, 0x5a, 0x48, 0xc1 ),
BIGINT ( 0xad, 0xb8, 0x3d, 0x85, 0x10, 0xdf,
0xea, 0x70, 0x71, 0x2c, 0x80, 0xf4,
0x6e, 0x66, 0x47, 0x41 ) );
bigint_mod_invert_ok ( BIGINT ( 0x35, 0xe4, 0x80, 0x48, 0xdd, 0xa1,
0x46, 0xc0, 0x84, 0x63, 0xc1, 0xe4,
0xf7, 0xbf, 0xb3, 0x05 ),
BIGINT ( 0xf2, 0x9c, 0x63, 0x29, 0xfa, 0xe4,
0xbf, 0x90, 0xa6, 0x9a, 0xec, 0xcf,
0x5f, 0xe2, 0x21, 0xcd ) );
bigint_mod_multiply_ok ( BIGINT ( 0x37 ),
BIGINT ( 0x67 ),
BIGINT ( 0x3f ),