aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md163
-rwxr-xr-xlibraries/libprof/profile-runner.py73
-rw-r--r--libraries/mbed/Makefile2
-rw-r--r--projects/board-test/fmc-perf.c4
-rw-r--r--projects/board-test/fmc-test.c4
-rw-r--r--projects/cli-test/test-fmc.c10
-rw-r--r--projects/hsm/hsm.c14
-rw-r--r--stm-fmc.c80
-rw-r--r--stm-fmc.h58
9 files changed, 254 insertions, 154 deletions
diff --git a/README.md b/README.md
index 6d813ef..e2c10a8 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,127 @@
-STM32 software for dev-bridge/Alpha board
-=========================================
+STM32 firmware for Cryptech Alpha board
+=======================================
-The dev-bridge board is a daughterboard for the Novena, which talks to the
-Novena's FPGA through the high-speed expansion connector.
+The Alpha board is our first full prototype for an open-source hardware
+security module (HSM). It is a custom board with an STM32 Cortex-M4
+microcontroller and an Artix-7 FPGA, flash-based keystore, separate memory
+for the Key Encryption Key, etc. See the `hardware` repository for
+schematics and production files. See the wiki for design documents.
-The Alpha board is a stand-alone board with an Artix-7 FPGA, a STM32 Cortex-M4
-microcontroller, two USB interfaces etc.
-
-See user/ft/stm32-dev-bridge/hardware/rev01 for schematics of the bridge
-board. There will be more information on the wiki shortly.
+The code in this repository builds the firmware that provides the HSM
+functionality on the Alpha board.
+There is some residual code here to support the "dev-bridge" board, a
+daughterboard for the Novena, which talks to the Novena's FPGA through the
+high-speed expansion connector. Only a few of these boards were ever made,
+and all development/testing ceased as soon as the Alpha became available,
+so the dev-bridge should be considered deprecated, and support may be
+removed in the future.
Copyrights
==========
The license for all work done on this in the CrypTech project is a
-3-clause BSD license (see LICENSE.txt for details). Some files have
-been generated using the STMicroelectronics initialization code
-generator STM32CubeMX and thus have additional copyright header(s).
+3-clause BSD license.
+
+Third-party components, as well as code generated using the
+STMicroelectronics initialization code generator STM32CubeMX, or adapted
+from STM example/support code, may have different licensing, detailed
+below.
+
+Components
+==========
+
+Libraries
+---------
+
+* `mbed` - A stripped down copy of the ARM CMSIS library, copied from the
+ mbed github (see `libraries/mbed/README.txt` for details). The bulk of
+ this library is covered under 3-clause BSD licenses from either ARM or
+ STMicroelectronics, but one file is covered under an Apache license from
+ ARM.
+
+* `libhal` - Build directory for our own Hardware Adaption Library
+ (hardware-independent Cryptech components). Source is expected to be in
+ `sw/libhal`.
+
+* `libtfm` - Build directory for "Tom's Fast Math", which is used heavily
+ for bignum math in the RSA and ECDSA code. This code is covered under an
+ unrestricted public domain license, and source is expected to be in
+ `sw/thirdparty/libtfm`.
+
+* `libcli` - Build directory for a third-party Command Line Interface
+ library. The source is not currently under `sw/thirdparty` because the
+ license is LGPLv2.1; we are negotiating to see if we can get a
+ BSD-compatible license for it.
+
+* `libprof` - A port of the `gmon` profiling package, to be used in
+ development only, not in production code (obviously). The licensing is a
+ mix of BSD and "Cygwin license", which now seems to be LGPLv3.
+
+Projects
+--------
-The "Noise generator" and "Amplifier" parts of the circuit diagram are
-copied from Benedikt Stockebrand's ARRGH project. ARRGH copyright
-statement is included in LICENSE.txt.
+These directories build different firmware images for the Alpha board.
-A stripped down copy of the ARM CMSIS library version 3.20 is included
-in the Drivers/CMSIS/ directory. Unused parts (and documentation etc.)
-have been removed, but every attempt have been made to keep any
-licensing information intact. See in particular the file
-Drivers/CMSIS/CMSIS END USER LICENCE AGREEMENT.pdf.
+* `hsm` - Firmware providing HSM functionality. Clients communicate via
+ RPC requests on the USER USB port, or interactively on the MGMT USB
+ port.
-A full copy of the STM32F4xx HAL Drivers is included in the
-Drivers/STM32F4xx_HAL_Driver/ directory.
+* `bootloader` - The first thing that runs on the device. It either starts
+ the primary firmware, or installs new firmware.
+* `board-test` - Tests of hardware components.
+
+* `cli-test` - Test of the CLI itself, plus some interactive tests of
+ hardware components. Duplicates way too much of the HSM CLI.
+
+* `libhal-test` - A framework for running the libhal component
+ tests. Hasn't been run in a while, probably still works.
Building
========
-The following packages need to be installed (on Ubuntu 14.04):
+Our primary build environments are Debian and Ubuntu, but this should work
+on any system with Gnu tools installed.
- apt-get install gcc-arm-none-eabi gdb-arm-none-eabi openocd
+The following packages need to be installed:
-To build the source code, issue "make" from the top level directory
-(where this file is). The first time, this will build the complete STM
-CMSIS library. A subsequent "make clean" will *not* clean away the CMSIS
-library, but a "make distclean" will.
+ $ apt-get install gcc-arm-none-eabi gdb-arm-none-eabi openocd
+The Makefile assumes that all Cryptech repositories have been fetched into
+a canonical directory structure, e.g. `libhal` and `thirdparty` are
+siblings to this directory, under `sw`.
+
+To build the source code, issue `make` from the top level directory
+(where this file is). The first time, this will build the complete STM
+CMSIS library. A subsequent `make clean` will *not* clean away the CMSIS
+library, but a `make distclean` will.
Installing
==========
-Do "bin/flash-target" from the top level directory (where this file is)
+Do `bin/flash-target` from the top level directory (where this file is)
to flash a built image into the microcontroller. See the section ST-LINK
below for information about the actual hardware programming device needed.
-Example loading the bootloader and the led-test firmware to get some LEDs
-flashing:
+Example loading the HSM firmware:
- $ make bootloader board-test
- $ ./bin/flash-target projects/board-test/led-test
- $ ./bin/flash-target projects/bootloader/bootloader
+ $ make hsm
+ $ ./bin/flash-target projects/hsm/hsm
At this point, the STM32 will reset into the bootloader which flashes the
-blue LED five times in one second, and then execution of the LED test
-firmware will begin. The LED test firmware will flash the green, yellow,
-red and blue LEDs in order until the end of time.
+blue LED five times in one second, and then jumps to the primary firmware.
Once the bootloader is installed, regular firmware can be loaded without
an ST-LINK cable like this:
- $ ./bin/dfu projects/board-test/led-test.bin
+ $ cryptech_upload --firmware -i projects/hsm/hsm.bin
Then reboot the Alpha board.
-
ST-LINK
-=======
+-------
+
To program the MCU, an ST-LINK adapter is used. The cheapest way to get
one is to buy an evaluation board with an ST-LINK integrated, and pinouts
to program external chips. This should work with any evaluation board from
@@ -86,7 +133,7 @@ printed on the circuit board. The pin-outs is shown on the circuit board
(follow the thin white line from J1 to the white box with STM32_SWD
written in it). From left to right, the pins are
- 3V3, CLK, GND, I/O, NRST and N/C
+ 3V3, CLK, GND, I/O, NRST and N/C
This matches the pin-out on the DISCO and NUCLEO boards we have tried.
@@ -94,34 +141,30 @@ First remove the pair of ST-LINK jumpers (CN4 on the DISCO, CN2 on the
NUCLEO). Then find the 6-pin SWD header on the left of the STM board (CN2
on the DISCO, CN4 on the NUCLEO), and connect them to the Alpha board:
- NUCLEO / DISCO CRYPTECH ALPHA
- -------------- --------------
-* 1 VDD_TARGET <-> 3V3
-* 2 SWCLK / T_JTCK <-> CLK
-* 3 GND <-> GND
-* 4 SWDIO / T_JTMS <-> IO
-* 5 NRST / T_NRST <-> NRST
-
-N/C (pin 6) means Not Connected.
+ NUCLEO / DISCO CRYPTECH ALPHA
+ -------------- --------------
+ * 1 VDD_TARGET <-> 3V3
+ * 2 SWCLK / T_JTCK <-> CLK
+ * 3 GND <-> GND
+ * 4 SWDIO / T_JTMS <-> IO
+ * 5 NRST / T_NRST <-> NRST
+ * 6 N/C
The Alpha board should be powered on before attempting to flash it.
-
Debugging the firmware
-======================
-
-This site shows several ways to use various debuggers to debug the
-firmware in an STM32:
+----------------------
- http://fun-tech.se/stm32/OpenOCD/gdb.php
+[This site](http://fun-tech.se/stm32/OpenOCD/gdb.php) shows several ways
+to use various debuggers to debug the firmware in an STM32.
There is a shell script called 'bin/debug' that starts an OpenOCD server
and GDB. Example:
- $ ./bin/debug projects/board-test/led-test
+ $ ./bin/debug projects/hsm/hsm
-Once in GDB, issue "monitor reset halt" to reset the STM32 before debugging.
+Once in GDB, issue `monitor reset halt` to reset the STM32 before debugging.
Remember that the first code to run will be the bootloader, but if you do
-e.g. "break main" and "continue" you will end up in led-test main() after
-the bootloader has jumped there.
+e.g. `break main` and `continue` you will end up in main() after the
+bootloader has jumped there.
diff --git a/libraries/libprof/profile-runner.py b/libraries/libprof/profile-runner.py
new file mode 100755
index 0000000..e7ec55f
--- /dev/null
+++ b/libraries/libprof/profile-runner.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+
+"""
+Tool to run some test code under the profiler on the Cryptech Alpha.
+
+This assumes that the HSM code was built with DO_PROFILING=1, and
+requires an ST-LINK programmer and the Python pexpect package.
+"""
+
+import subprocess
+import argparse
+import pexpect
+import atexit
+import time
+import sys
+import os
+
+parser = argparse.ArgumentParser(description = __doc__,
+ formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument("--hsm-elf",
+ default = os.path.expanduser("~/git.cryptech.is/sw/stm32/projects/hsm/hsm.elf"),
+ help = "where you keep the profiled hsm.elf binary")
+parser.add_argument("--openocd-config",
+ default = "/usr/share/openocd/scripts/board/st_nucleo_f401re.cfg",
+ help = "OpenOCD ST-LINK configuration file ")
+parser.add_argument("--gmon-output",
+ default = "profile-runner.gmon",
+ help = "where to leave raw profiler output")
+parser.add_argument("--gprof-output", type = argparse.FileType("w"),
+ default = "profile-runner.gprof",
+ help = "where to leave profiler output after processing with gprof")
+parser.add_argument("--user",
+ default = "wheel",
+ help = "user name for logging in on the HSM console")
+parser.add_argument("--pin",
+ default = "fnord",
+ help = "PIN for logging in on the HSM console")
+parser.add_argument("command", nargs = 1,
+ help = "test program to run with profiling")
+parser.add_argument("arguments", nargs = argparse.REMAINDER,
+ help = argparse.SUPPRESS)
+args = parser.parse_args()
+
+openocd = subprocess.Popen(("openocd", "-f", args.openocd_config))
+atexit.register(openocd.terminate)
+
+time.sleep(5)
+
+telnet = pexpect.spawn("telnet localhost 4444")
+telnet.expect(">")
+telnet.sendline("arm semihosting enable")
+telnet.expect(">")
+telnet.sendline("exit")
+
+console = pexpect.spawn("cryptech_console")
+console.sendline("")
+if console.expect(["cryptech>", "Username:"]):
+ console.sendline(args.user)
+ console.expect("Password:")
+ console.sendline(args.pin)
+ console.expect("cryptech>")
+console.sendline("profile start")
+console.expect("cryptech>")
+
+cmd = args.command + args.arguments
+sys.stderr.write("Running command: {}\n".format(" ".join(cmd)))
+subprocess.check_call(cmd)
+
+console.sendline("profile stop")
+console.expect("cryptech>", timeout = 900)
+os.rename("gmon.out", args.gmon_output)
+
+subprocess.check_call(("gprof", args.hsm_elf, args.gmon_output), stdout = args.gprof_output)
diff --git a/libraries/mbed/Makefile b/libraries/mbed/Makefile
index 873359d..eb2bd2b 100644
--- a/libraries/mbed/Makefile
+++ b/libraries/mbed/Makefile
@@ -5,7 +5,7 @@ CFLAGS += -Wno-unused-parameter
###########################################
-vpath %.c targets/cmsis/TARGET_STM/TARGET_STM32F4 targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_DEV_BRIDGE
+vpath %.c $(CMSIS_DIR) $(BOARD_DIR)
SRCS = stm32f4xx_hal.c \
stm32f4xx_hal_adc.c \
diff --git a/projects/board-test/fmc-perf.c b/projects/board-test/fmc-perf.c
index e87f282..71d0149 100644
--- a/projects/board-test/fmc-perf.c
+++ b/projects/board-test/fmc-perf.c
@@ -31,7 +31,7 @@ static void sanity(void)
uint32_t rnd, data;
rnd = random();
- if (fmc_write_32(0, &rnd) != 0) {
+ if (fmc_write_32(0, rnd) != 0) {
uart_send_string("fmc_write_32 failed\r\n");
Error_Handler();
}
@@ -88,7 +88,7 @@ static void test_write(void)
uint32_t i;
for (i = 0; i < TEST_NUM_ROUNDS; ++i) {
- if (fmc_write_32(0, &i) != 0) {
+ if (fmc_write_32(0, i) != 0) {
uart_send_string("fmc_write_32 failed\r\n");
Error_Handler();
}
diff --git a/projects/board-test/fmc-test.c b/projects/board-test/fmc-test.c
index 2002f57..1421db0 100644
--- a/projects/board-test/fmc-test.c
+++ b/projects/board-test/fmc-test.c
@@ -171,7 +171,7 @@ int test_fpga_data_bus(void)
if (hal_result != HAL_OK) break;
// write value to fpga at address 0
- ok = fmc_write_32(0, &rnd);
+ ok = fmc_write_32(0, rnd);
if (ok != 0) break;
// read value from fpga
@@ -239,7 +239,7 @@ int test_fpga_address_bus(void)
if (rnd == 0) continue;
// write dummy value to fpga at some non-zero address
- ok = fmc_write_32(rnd, &buf);
+ ok = fmc_write_32(rnd, buf);
if (ok != 0) break;
// read value from fpga
diff --git a/projects/cli-test/test-fmc.c b/projects/cli-test/test-fmc.c
index 87f80ce..6773cfc 100644
--- a/projects/cli-test/test-fmc.c
+++ b/projects/cli-test/test-fmc.c
@@ -76,7 +76,7 @@ volatile uint32_t data_diff = 0;
volatile uint32_t addr_diff = 0;
-static int _write_then_read(struct cli_def *cli, uint32_t addr, uint32_t *write_buf, uint32_t *read_buf)
+static int _write_then_read(struct cli_def *cli, uint32_t addr, uint32_t write_buf, uint32_t *read_buf)
{
int ok;
@@ -115,7 +115,7 @@ int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds)
}
/* write value to fpga at address 0 and then read it back from the test register */
- if (! _write_then_read(cli, 0, &rnd, &buf)) break;
+ if (! _write_then_read(cli, 0, rnd, &buf)) break;
/* compare (abort testing in case of error) */
data_diff = buf ^ rnd;
@@ -137,7 +137,7 @@ int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds)
for (i = 0; i < 31; i++) {
data = 1 << i;
- if (! _write_then_read(cli, 0, &data, &buf)) break;
+ if (! _write_then_read(cli, 0, data, &buf)) break;
if (data == buf) {
cli_print(cli, "Data 0x%08lx (FMC_D%02i) - OK", data, i + 1);
@@ -181,7 +181,7 @@ int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds)
/* write dummy value to fpga at some non-zero address and then read from the
test register to see what address the FPGA thought we wrote to
*/
- if (! _write_then_read(cli, addr, &dummy, &buf)) break;
+ if (! _write_then_read(cli, addr, dummy, &buf)) break;
/* fpga receives address of 32-bit word, while we need
byte address here to compare
@@ -210,7 +210,7 @@ int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds)
shifted_addr = addr << 2;
- if (! _write_then_read(cli, shifted_addr, &dummy, &buf)) break;
+ if (! _write_then_read(cli, shifted_addr, dummy, &buf)) break;
if (addr == buf) {
cli_print(cli, "Address 0x%08lx (FMC_A%02i) - OK", addr, i + 1);
diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c
index c9d8d28..29509e8 100644
--- a/projects/hsm/hsm.c
+++ b/projects/hsm/hsm.c
@@ -99,7 +99,7 @@ typedef struct rpc_buffer_s {
} rpc_buffer_t;
/* RPC input (requst) buffers */
-static rpc_buffer_t ibufs[NUM_RPC_TASK];
+static rpc_buffer_t *ibufs;
/* ibuf queue structure */
typedef struct {
@@ -439,6 +439,12 @@ task_mutex_t ks_mutex = { 0 };
void hal_ks_lock(void) { task_mutex_lock(&ks_mutex); }
void hal_ks_unlock(void) { task_mutex_unlock(&ks_mutex); }
+/* A mutex to arbitrary concurrent access to the RSA blinding factors cache.
+ */
+task_mutex_t rsa_bf_mutex = { 0 };
+void hal_rsa_bf_lock(void) { task_mutex_lock(&rsa_bf_mutex); }
+void hal_rsa_bf_unlock(void) { task_mutex_unlock(&rsa_bf_mutex); }
+
/* Sleep for specified number of seconds.
*/
void hal_sleep(const unsigned seconds) { task_delay(seconds * 1000); }
@@ -455,9 +461,13 @@ int main(void)
Error_Handler();
/* Initialize the ibuf queues. */
+ ibufs = (rpc_buffer_t *)sdram_malloc(NUM_RPC_TASK * sizeof(rpc_buffer_t));
+ if (ibufs == NULL)
+ Error_Handler();
+ memset(ibufs, 0, NUM_RPC_TASK * sizeof(rpc_buffer_t));
memset(&ibuf_waiting, 0, sizeof(ibuf_waiting));
memset(&ibuf_ready, 0, sizeof(ibuf_ready));
- for (size_t i = 0; i < sizeof(ibufs)/sizeof(ibufs[0]); ++i)
+ for (size_t i = 0; i < NUM_RPC_TASK; ++i)
ibuf_put(&ibuf_waiting, &ibufs[i]);
/* Create the rpc dispatch worker tasks. */
diff --git a/stm-fmc.c b/stm-fmc.c
index 1302564..c5086b4 100644
--- a/stm-fmc.c
+++ b/stm-fmc.c
@@ -164,83 +164,3 @@ void fmc_init(void)
// initialize fmc
HAL_SRAM_Init(&_fmc_fpga_inst, &fmc_timing, NULL);
}
-
-
-static HAL_StatusTypeDef _fmc_nwait_idle(void)
-{
- int cnt;
-
- // poll NWAIT (number of iterations is limited)
- for (cnt=0; cnt<FMC_FPGA_NWAIT_MAX_POLL_TICKS; cnt++)
- {
- // read pin state
- if (HAL_GPIO_ReadPin(FMC_GPIO_PORT_NWAIT, FMC_GPIO_PIN_NWAIT) == FMC_NWAIT_IDLE)
- return HAL_OK;
- }
-
- return HAL_ERROR;
-}
-
-HAL_StatusTypeDef fmc_write_32(uint32_t addr, uint32_t *data)
-{
- // calculate target fpga address
- uint32_t ptr = FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK);
-
- __disable_irq();
-
- HAL_StatusTypeDef status =
- // write data to fpga
- HAL_SRAM_Write_32b(&_fmc_fpga_inst, (uint32_t *)ptr, data, 1);
- if (status == HAL_OK)
- // wait for transaction to complete
- status = _fmc_nwait_idle();
-
- __enable_irq();
-
- return status;
-}
-
-static inline HAL_StatusTypeDef _fmc_read_32(uint32_t *ptr, uint32_t *data)
-{
- HAL_StatusTypeDef status =
- // read data from fpga
- HAL_SRAM_Read_32b(&_fmc_fpga_inst, (uint32_t *)ptr, data, 1);
- if (status == HAL_OK)
- // wait for transaction to complete
- status = _fmc_nwait_idle();
-
- return status;
-}
-
-HAL_StatusTypeDef fmc_read_32(uint32_t addr, uint32_t *data)
-{
- // calculate target fpga address
- uint32_t ptr = FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK);
-
- /* Pavel says:
- * The short story is like, on one hand STM32 has a dedicated FMC_NWAIT
- * pin, that can be used in variable-latency data transfer mode. On the
- * other hand STM32 also has a very nasty hardware bug associated with
- * FMC_WAIT, that causes processor to freeze under certain conditions.
- * Because of this FMC_NWAIT cannot be used and FPGA can't properly signal
- * to STM32, when data transfer is done. Because of that we have to read
- * two times.
- */
-
- /* Add some level of reentrancy protection. When running under a
- * preemptive multitasker, with two threads banging on the fpga, we appear
- * to sometimes read the wrong value. I think this is because the second
- * read counts on the first read to put the correct value on the address
- * bus.
- */
- __disable_irq();
-
- HAL_StatusTypeDef status =
- _fmc_read_32((uint32_t *)ptr, data);
- if (status == HAL_OK)
- status = _fmc_read_32((uint32_t *)ptr, data);
-
- __enable_irq();
-
- return status;
-}
diff --git a/stm-fmc.h b/stm-fmc.h
index d0f8bb4..eab053d 100644
--- a/stm-fmc.h
+++ b/stm-fmc.h
@@ -57,7 +57,61 @@
extern void fmc_init(void);
-extern HAL_StatusTypeDef fmc_write_32(uint32_t addr, uint32_t *data);
-extern HAL_StatusTypeDef fmc_read_32(uint32_t addr, uint32_t *data);
+
+static inline HAL_StatusTypeDef _fmc_nwait_idle(void)
+{
+ int cnt;
+
+ // poll NWAIT (number of iterations is limited)
+ for (cnt=0; cnt<FMC_FPGA_NWAIT_MAX_POLL_TICKS; cnt++)
+ {
+ // read pin state
+ if (HAL_GPIO_ReadPin(FMC_GPIO_PORT_NWAIT, FMC_GPIO_PIN_NWAIT) == FMC_NWAIT_IDLE)
+ return HAL_OK;
+ }
+
+ return HAL_ERROR;
+}
+
+static inline HAL_StatusTypeDef fmc_write_32(const uint32_t addr, const uint32_t data)
+{
+ // calculate target fpga address
+ uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK));
+
+ // write data to fpga
+ *ptr = data;
+
+ // wait for transaction to complete
+ return _fmc_nwait_idle();
+}
+
+static inline HAL_StatusTypeDef fmc_read_32(const uint32_t addr, uint32_t * const data)
+{
+ // calculate target fpga address
+ uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK));
+
+ /* Pavel says:
+ * The short story is like, on one hand STM32 has a dedicated FMC_NWAIT
+ * pin, that can be used in variable-latency data transfer mode. On the
+ * other hand STM32 also has a very nasty hardware bug associated with
+ * FMC_WAIT, that causes processor to freeze under certain conditions.
+ * Because of this FMC_NWAIT cannot be used and FPGA can't properly signal
+ * to STM32, when data transfer is done. Because of that we have to read
+ * two times.
+ */
+
+ HAL_StatusTypeDef status;
+
+ *data = *ptr;
+ status = _fmc_nwait_idle();
+
+ if (status != HAL_OK)
+ return status;
+
+ *data = *ptr;
+ status = _fmc_nwait_idle();
+
+ return status;
+}
#endif /* __STM_FMC_H */