diff options
author | Paul Selkirk <paul@psgd.org> | 2016-07-10 22:51:47 -0400 |
---|---|---|
committer | Paul Selkirk <paul@psgd.org> | 2016-07-10 22:51:47 -0400 |
commit | 26a343971eb6f0e6e055441df353e60e81cf3595 (patch) | |
tree | 65e05394ff9807a9e709ce55e63ab46416d3067a | |
parent | 708103998b7005c51fd78fc5563e46dd93fee283 (diff) |
Sign/verifiy installable imagesparade_of_half_baked_ideas
Receive the image into sdram, verify the signature before copying to flash.
It would be great if worked...
-rw-r--r-- | projects/hsm/Makefile | 1 | ||||
-rwxr-xr-x | projects/hsm/cryptech_upload | 51 | ||||
-rw-r--r-- | projects/hsm/hsm.c | 22 | ||||
-rw-r--r-- | projects/hsm/mgmt-bootloader.c | 31 | ||||
-rw-r--r-- | projects/hsm/mgmt-fpga.c | 32 | ||||
-rw-r--r-- | projects/hsm/mgmt-misc.c | 158 | ||||
-rw-r--r-- | projects/hsm/mgmt-misc.h | 7 | ||||
-rw-r--r-- | sdram-malloc.c | 88 | ||||
-rw-r--r-- | sdram-malloc.h | 36 |
9 files changed, 326 insertions, 100 deletions
diff --git a/projects/hsm/Makefile b/projects/hsm/Makefile index acb9962..5153a64 100644 --- a/projects/hsm/Makefile +++ b/projects/hsm/Makefile @@ -22,6 +22,7 @@ BOARD_OBJS = \ $(TOPLEVEL)/stm-keystore.o \ $(TOPLEVEL)/stm-sdram.o \ $(TOPLEVEL)/stm-flash.o \ + $(TOPLEVEL)/sdram-malloc.o \ $(BOARD_DIR)/TOOLCHAIN_GCC_ARM/startup_stm32f429xx_rtos.o \ $(BOARD_DIR)/system_stm32f4xx.o \ $(BOARD_DIR)/stm32f4xx_hal_msp.o \ diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 7590b38..9f401b8 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -79,16 +79,17 @@ def parse_args(): # positional argument(s) parser.add_argument('filename') + parser.add_argument('signature') return parser.parse_args() def _write(dst, data): dst.write(data) - #if len(data) == 4: - # print("Wrote 0x{!s}".format(data.encode('hex'))) - #else: - # print("Wrote {!r}".format(data)) + if len(data) == 4: + print("Wrote 0x{!s}".format(data.encode('hex'))) + else: + print("Wrote {!r}".format(data)) def _read(dst): @@ -99,7 +100,7 @@ def _read(dst): while x: res += x x = dst.read(1) - #print ("Read {!r}".format(res)) + print ("Read {!r}".format(res)) return res pin = None @@ -123,9 +124,16 @@ def _execute(dst, cmd): response = _read(dst) return response -def send_file(filename, args, dst): - s = os.stat(filename) - size = s.st_size +def send_file(filename, signature, args, dst): + def fsize(fn): + try: + s = os.stat(fn) + except OSError as e: + print e + exit(1) + return s.st_size + + size = fsize(filename) src = open(filename, 'rb') if args.fpga: chunk_size = FPGA_CHUNK_SIZE @@ -178,14 +186,29 @@ def send_file(filename, args, dst): crc = crc32(data, crc) & 0xffffffff - _read(dst) + src.close() - # 3. Write CRC-32 (4 bytes) - _write(dst, struct.pack('<I', crc)) response = _read(dst) - print response - src.close() + if response.startswith('Send CRC-32'): + + # 3a. Write CRC-32 (4 bytes) + _write(dst, struct.pack('<I', crc)) + response = _read(dst) + print response + + elif response.startswith('Send signature'): + + # 3b. Write ECDSA signature + # write signature size + _write(dst, struct.pack('<I', fsize(signature))) + response = _read(dst) + if not response.startswith('Send '): + print response + return False + _write(dst, open(signature, 'rb').read()) + response = _read(dst) + print response if args.fpga: # tell the fpga to read its new configuration @@ -201,7 +224,7 @@ def send_file(filename, args, dst): def main(args): dst = serial.Serial(args.device, 921600, timeout=2) - send_file(args.filename, args, dst) + send_file(args.filename, args.signature, args, dst) dst.close() return True diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index 3186bc5..d133c6f 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -206,28 +206,6 @@ void dispatch_thread(void const *args) } osThreadDef_t thread_def[NUM_RPC_TASK]; -/* Allocate memory from SDRAM1. There is only malloc, no free, so we don't - * worry about fragmentation. */ -static uint8_t *sdram_malloc(size_t size) -{ - /* end of variables declared with __attribute__((section(".sdram1"))) */ - extern uint8_t _esdram1 __asm ("_esdram1"); - /* end of SDRAM1 section */ - extern uint8_t __end_sdram1 __asm ("__end_sdram1"); - - static uint8_t *sdram_heap = &_esdram1; - uint8_t *p = sdram_heap; - -#define pad(n) (((n) + 3) & ~3) - size = pad(size); - - if (p + size > &__end_sdram1) - return NULL; - - sdram_heap += size; - return p; -} - /* The main thread. This does all the setup, and the worker threads handle * the rest. */ diff --git a/projects/hsm/mgmt-bootloader.c b/projects/hsm/mgmt-bootloader.c index d2d7ffe..68a0b38 100644 --- a/projects/hsm/mgmt-bootloader.c +++ b/projects/hsm/mgmt-bootloader.c @@ -38,6 +38,7 @@ #include "stm-uart.h" #include "stm-flash.h" #include "stm-fpgacfg.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-misc.h" @@ -50,15 +51,6 @@ extern hal_user_t user; -static uint32_t dfu_offset; - -static int _flash_write_callback(uint8_t *buf, size_t len) -{ - stm_flash_write32(dfu_offset, (uint32_t *)buf, sizeof(buf)/4); - dfu_offset += DFU_UPLOAD_CHUNK_SIZE; - return 1; -} - static int cmd_bootloader_upload(struct cli_def *cli, const char *command, char *argv[], int argc) { if (user < HAL_USER_SO) { @@ -78,13 +70,24 @@ static int cmd_bootloader_upload(struct cli_def *cli, const char *command, char return CLI_ERROR; } - uint8_t buf[DFU_UPLOAD_CHUNK_SIZE]; - dfu_offset = DFU_BOOTLOADER_ADDR; + uint8_t *filebuf; + size_t file_len; - cli_receive_data(cli, buf, sizeof(buf), _flash_write_callback); + if (cli_receive_data(cli, &filebuf, &file_len, DFU_UPLOAD_CHUNK_SIZE) == CLI_ERROR) + return CLI_ERROR; - cli_print(cli, "DFU offset now: %li (%li chunks)", dfu_offset, dfu_offset / DFU_UPLOAD_CHUNK_SIZE); - return CLI_OK; + cli_print(cli, "Writing flash"); + int res = stm_flash_write32(DFU_BOOTLOADER_ADDR, (uint32_t *)filebuf, file_len/4) == 1; + sdram_free(filebuf); + + if (res) { + cli_print(cli, "SUCCESS"); + return CLI_OK; + } + else { + cli_print(cli, "FAIL"); + return CLI_ERROR; + } } void configure_cli_bootloader(struct cli_def *cli) diff --git a/projects/hsm/mgmt-fpga.c b/projects/hsm/mgmt-fpga.c index 45bd33c..78978b6 100644 --- a/projects/hsm/mgmt-fpga.c +++ b/projects/hsm/mgmt-fpga.c @@ -37,6 +37,7 @@ #include "stm-init.h" #include "stm-uart.h" #include "stm-fpgacfg.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-fpga.h" @@ -52,15 +53,6 @@ extern hal_user_t user; -static volatile uint32_t dfu_offset = 0; - - -static int _flash_write_callback(uint8_t *buf, size_t len) { - int res = fpgacfg_write_data(dfu_offset, buf, BITSTREAM_UPLOAD_CHUNK_SIZE) == 1; - dfu_offset += BITSTREAM_UPLOAD_CHUNK_SIZE; - return res; -} - static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *argv[], int argc) { if (user < HAL_USER_SO) { @@ -68,9 +60,8 @@ static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, c return CLI_ERROR; } - uint8_t buf[BITSTREAM_UPLOAD_CHUNK_SIZE]; - - dfu_offset = 0; + uint8_t *filebuf; + size_t file_len; fpgacfg_access_control(ALLOW_ARM); @@ -80,12 +71,23 @@ static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, c return CLI_ERROR; } - cli_receive_data(cli, &buf[0], sizeof(buf), _flash_write_callback); + if (cli_receive_data(cli, &filebuf, &file_len, BITSTREAM_UPLOAD_CHUNK_SIZE) == CLI_ERROR) + return CLI_ERROR; + + cli_print(cli, "Writing flash"); + int res = fpgacfg_write_data(0, filebuf, file_len) == 1; fpgacfg_access_control(ALLOW_FPGA); + sdram_free(filebuf); - cli_print(cli, "DFU offset now: %li (%li chunks)", dfu_offset, dfu_offset / BITSTREAM_UPLOAD_CHUNK_SIZE); - return CLI_OK; + if (res) { + cli_print(cli, "SUCCESS"); + return CLI_OK; + } + else { + cli_print(cli, "FAIL"); + return CLI_ERROR; + } } static int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *argv[], int argc) diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c index 15b2d13..5eecdb3 100644 --- a/projects/hsm/mgmt-misc.c +++ b/projects/hsm/mgmt-misc.c @@ -32,74 +32,174 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "stm-init.h" #include "stm-uart.h" +#include "sdram-malloc.h" #include "mgmt-cli.h" #include "mgmt-misc.h" -#include <string.h> +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#include "cryptech_signing_key.h" +#undef HAL_OK +#include <string.h> -extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); +#define pad(n, sz) (((n) + (sz-1)) & ~(sz-1)) +static hal_error_t verify_signature(const uint8_t * const data, + const size_t data_len, + const uint8_t * const signature, + const size_t signature_len) +{ + hal_error_t err; + const hal_core_t *core = NULL; + const hal_hash_descriptor_t * const descriptor = hal_hash_sha256; + hal_hash_state_t *state = NULL; + uint8_t statebuf[descriptor->hash_state_length]; + uint8_t digest[signature_len]; + +#if 1 + /* HACK - find the second sha256 core, to avoid interfering with rpc. + * If there isn't a second one, this will set core to NULL, and + * hal_hash_initialize will find the first one. + */ + core = hal_core_find(descriptor->core_name, core); + core = hal_core_find(descriptor->core_name, core); +#endif + + if ((err = hal_hash_initialize(core, descriptor, &state, statebuf, sizeof(statebuf))) != LIBHAL_OK || + (err = hal_hash_update(state, data, data_len)) != LIBHAL_OK || + (err = hal_hash_finalize(state, digest, sizeof(digest))) != LIBHAL_OK) + return err; + + const uint8_t name[] = "Cryptech signing key"; + const size_t name_len = sizeof(name); + uint8_t der[65]; /* ?? */ + int ks_hint; + + if ((err = hal_ks_fetch(HAL_KEY_TYPE_EC_PUBLIC, name, name_len, NULL, NULL, der, NULL, sizeof(der), &ks_hint)) != LIBHAL_OK) + /* get the built-in signing key */ + memcpy(der, cryptech_signing_key, sizeof(der)); + + uint8_t keybuf[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key = NULL; + + if ((err = hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, sizeof(der))) != LIBHAL_OK || + (err = hal_ecdsa_verify(NULL, key, digest, sizeof(digest), signature, signature_len)) != LIBHAL_OK) + return err; + + return LIBHAL_OK; +} -int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback) +int cli_receive_data(struct cli_def *cli, uint8_t **filebuf, size_t *file_len, size_t chunksize) { - uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; - size_t n = len; + uint32_t filesize = 0, counter = 0; + uint8_t *writeptr; + size_t n = chunksize; if (! control_mgmt_uart_dma_rx(DMA_RX_STOP)) { cli_print(cli, "Failed stopping DMA"); return CLI_OK; } - cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks, CRC-32 (4 bytes)", (uint32_t) n); + cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks", (uint32_t) n); - if (uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000) != HAL_OK) { + if (uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); return CLI_ERROR; } + *file_len = (size_t)(pad(filesize, chunksize)); + if ((*filebuf = sdram_malloc(*file_len)) == NULL) { + cli_print(cli, "File buffer allocation failed"); + return CLI_ERROR; + } + + /* By initializing buf to the same value that erased flash has (0xff), we don't + * have to try and be smart when writing the last page of data to a flash memory. + */ + memset(*filebuf, 0xff, *file_len); + writeptr = *filebuf; + cli_print(cli, "Send %li bytes of data", filesize); while (filesize) { - /* By initializing buf to the same value that erased flash has (0xff), we don't - * have to try and be smart when writing the last page of data to a flash memory. - */ - memset(buf, 0xff, len); if (filesize < n) n = filesize; - if (uart_receive_bytes(STM_UART_MGMT, (void *) buf, n, 1000) != HAL_OK) { + if (uart_receive_bytes(STM_UART_MGMT, (void *) writeptr, n, 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); - return CLI_ERROR; + goto errout; } + writeptr += n; filesize -= n; - my_crc = update_crc(my_crc, buf, n); - - /* After reception of a chunk but before ACKing we have "all" the time in the world to - * calculate CRC and invoke the data_callback. - */ - if (data_callback != NULL && ! data_callback(buf, (size_t) n)) { - cli_print(cli, "Data processing failed"); - return CLI_OK; - } counter++; uart_send_bytes(STM_UART_MGMT, (void *) &counter, 4); } - cli_print(cli, "Send CRC-32"); - uart_receive_bytes(STM_UART_MGMT, (void *) &crc, 4, 1000); - cli_print(cli, "CRC-32 0x%x, calculated CRC 0x%x", (unsigned int) crc, (unsigned int) my_crc); - if (crc == my_crc) { - cli_print(cli, "CRC checksum MATCHED"); - } else { - cli_print(cli, "CRC checksum did NOT match"); + uint32_t sigsize; + cli_print(cli, "Send signature size (4 bytes)"); + if (uart_receive_bytes(STM_UART_MGMT, (void *) &sigsize, 4, 1000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto errout; } + if (sigsize < 70 || sigsize > 72) { + cli_print(cli, "Unexpected signature size %d, expected 70-72", (int)sigsize); + goto errout; + } + uint8_t sigbuf[72]; + cli_print(cli, "Send %li bytes of data", sigsize); + if (uart_receive_bytes(STM_UART_MGMT, (void *) sigbuf, sigsize, 1000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto errout; + } + /* Signature is in DER format: sequence of 2 integers. Decode into + * what ecdsa expects: an octet string consisting of concatenated + * values for r and s, each of which occupies half of the octet string. + */ + /* XXX should use the asn1 decoder, but just going to hack this for now */ + int i = 0; + #define ASN1_SEQUENCE 0x30 + #define ASN1_INTEGER 0x02 + if (sigbuf[i++] != ASN1_SEQUENCE) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + i++; + if (sigbuf[i++] != ASN1_INTEGER) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + if (sigbuf[i++] == 0x21) i++; + memmove(&sigbuf[0], &sigbuf[i], 32); + i += 32; + if (sigbuf[i++] != ASN1_INTEGER) { + cli_print(cli, "Error parsing signature"); + goto errout; + } + if (sigbuf[i++] == 0x21) i++; + memmove(&sigbuf[32], &sigbuf[i], 32); + + if (verify_signature(*filebuf, *file_len, sigbuf, 64) != LIBHAL_OK) { + cli_print(cli, "Signature verification FAILED"); + goto errout; + } + cli_print(cli, "Signature verification SUCCEEDED"); return CLI_OK; + +errout: + sdram_free(*filebuf); + *filebuf = NULL; + *file_len = 0; + return CLI_ERROR; } static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) diff --git a/projects/hsm/mgmt-misc.h b/projects/hsm/mgmt-misc.h index b7eb4f4..619b94e 100644 --- a/projects/hsm/mgmt-misc.h +++ b/projects/hsm/mgmt-misc.h @@ -38,12 +38,7 @@ #include "stm-init.h" #include <libcli.h> - -#define FILETRANSFER_UPLOAD_CHUNK_SIZE 256 - -typedef int (*cli_data_callback)(uint8_t *, size_t); - extern void configure_cli_misc(struct cli_def *cli); -extern int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback); +extern int cli_receive_data(struct cli_def *cli, uint8_t **filebuf, size_t *file_len, size_t chunksize); #endif /* __STM32_CLI_MGMT_MISC_H */ diff --git a/sdram-malloc.c b/sdram-malloc.c new file mode 100644 index 0000000..3c2eabe --- /dev/null +++ b/sdram-malloc.c @@ -0,0 +1,88 @@ +/* + * sdram_malloc.c + * -------------- + * Use SDRAM for a very limited sort of heap. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Allocate memory from SDRAM1. This is not a general allocator, and + * should only be used with the greatest of caution and care. + * + * This is intended for allocating memory that will either never be freed + * (e.g. thread stack buffers), or will be freed as soon as the requesting + * function is done with it (e.g. file upload buffers). + * + * Memory should be freed in reverse order of allocation (most + * recent first). + * + * There are no memory block control fields, so no protection to ensure + * that memory that is freed is actually the memory that was allocated. + */ + +#include <stddef.h> +#include <stdint.h> + +/* end of variables declared with __attribute__((section(".sdram1"))) */ +extern uint8_t _esdram1 __asm ("_esdram1"); +static uint8_t * const sdram_heap_base = &_esdram1; + +/* end of sdram1 section */ +extern uint8_t __end_sdram1 __asm ("__end_sdram1"); +static uint8_t * const sdram_heap_end = &__end_sdram1; + +/* current heap top */ +static uint8_t *sdram_heap = &_esdram1; + +/* Allocate some memory. Allocations are not padded, because file upload will + * call repeatedly to write blocks of data to a "file". OTOH, it's a good + * idea to allocate padding after the last write, so the next allocation + * will be aligned. + */ +uint8_t *sdram_malloc(size_t size) +{ + uint8_t *p = sdram_heap; + + if (p + size > sdram_heap_end) + return NULL; + + sdram_heap += size; + return p; +} + +/* Free some memory allocated from sdram. This does only the most basic of + * sanity checks. + */ +void sdram_free(uint8_t *s) +{ + if (s < sdram_heap_base || s >= sdram_heap_end) + return; + if (s < sdram_heap) + sdram_heap = s; +} diff --git a/sdram-malloc.h b/sdram-malloc.h new file mode 100644 index 0000000..5fc80fc --- /dev/null +++ b/sdram-malloc.h @@ -0,0 +1,36 @@ +/* + * sdram_malloc.h + * -------------- + * Use SDRAM for a very limited sort of heap. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern uint8_t *sdram_malloc(size_t size); +extern void sdram_free(uint8_t *s); |