Multi Zone API v1.1.1
The RISC-V multi zone API definition is free as in “free speech” and free as in “free lunch”. The API definition is a free and open standard completely open sourced under ISC permissive license for “any use”. It is maintained by Hex Five Security, Inc. and we welcome everyone’s direct contributions in the form of GitHub pull requests. Everyone can write their own implementation of the free and open API. We are strong supporter of the commercial open source movement and we actively promote the development of a global vibrant community!
The MultiZone Security implementation provided by Hex Five includes MultiZone™ Configurator, MultiZone™ nanoKernel, and MultiZone™ Interzone Communications. These components are distributed as part of the MultiZone™ Security SDK, free of charge for evaluation only. Commercial use is restricted and requires Hex Five commercial license, sometime included with the hardware itself, but never ever royalties – as we hate royalties as much as you do! Hex Five’s core components are suitable for formal verification and everyone is welcome to perform formal verification and to publish models and results. In addition, Hex Five’s source code is always available to commercial licensees for inspection and detailed code review in a clean room environment.
For more information see http://hex-five.com/faq
Function | Syntax and Function | Example |
---|---|---|
ECALL_YIELD | void ECALL_YIELD(); Indicates to the nanoKernel scheduler that the Zone has nothing pressing to do and causes the nanoKernel to immediately move to the next Zone in context. |
ECALL_YIELD(); In the case of a three zone implementation with a tick time of 10ms, the maximum time to come back to context is 20ms, faster if the other zones Yield as well. |
ECALL_WFI | void ECALL_WFI(); Unprivileged implementation of the Wait for Interrupt instruction WFI. The Wait for Interrupt instruction provides a hint to scheduler that the current zone can be paused until an interrupt might need servicing. If all zones are waiting for interrupt, the scheduler puts the core in a suspended low power state. Note that ECALL_WFI() is just a hint: a legal implementation is to implement ECALL_WFI() as an ECALL_YIELD(), in which case the core will never enter the low power state. |
int main (void){ ... setup interrupt handlers ... while(1) { ...... do something ...... ECALL_WFI(); ... } } Typical implementation of the main loop of an event-driven zone. |
ECALL_SEND | int ECALL_SEND([Zone #], [0-3][Int]); Send transmits a message from the current zone to the [Zone #]; the message size is an array of [4] integers and the nanoKernel manages transmission with no shared memory. The value returned is 1 if the receiving mailbox is empty and transmission successful or 0 if the receiving mailbox is full and transmission was blocked. |
int state = ECALL_SEND(1, {201, 0, 0 ,0}); Sends an array to Zone 1 of {201, 0, 0, 0}; state = 1 if successful transmission. |
ECALL_RECV | int ECALL_RECV[Zone #], [0-3][int]); Checks the mailbox of the current Zone for a message from the listed Zone #, if there is a new message the it returns 1, if no new messaage it returns 0. The value of the message is copied into the the array structure provided. |
int msg[4]={0,0,0,0}; int state = ECALL_RECV(1, msg); If a newmessage exists in the mailbox from zone 1, state = 1 and it copies it to msg, otherwise state = 0. |
ECALL_CSRS_MIE | void ECALL_CSRS_MIE(); Secure user-mode emulation of the Machine Status Register (mstatus) MIE bit. Enables all interrupts (PLIC + CLINT) mapped to the zone including the soft timer (trap 0x3). The operation is atomic with respect to the context of the zone. |
ECALL_CSRS_MIE(); |
ECALL_CSRC_MIE | void ECALL_CSRC_MIE(); Secure user-mode emulation of the Machine Status Register (mstatus) MIE bit. Disables all interrupts (PLIC + CLINT) mapped to the zone including the soft timer (trap 0x3). The operation is atomic with respect to the context of the zone. |
ECALL_CSRC_MIE(); |
ECALL_TRP_VECT | void ECALL_TRP_VECT([Exception Code], [Trap Handler]) Registers a handler against a trap generated by anauthorized instructions; the TRAP #s are defined in the RISC-V Privileged Architectures definition V1.1, Table 3.6 Interrupt 0 types. https://riscv.org/specifications/privileged-isa/ |
ECALL_TRP_VECT(0x0, trap_0x0_handler); Where trap_0x0_handler is registered at the User level of privilege with: Void trap_0x0_handler(void)__attribute__((interrupt("user"))); void trap_0x0_handler(void){ // Your handler code here } |
ECALL_IRQ_VECT | void ECALL_IRQ_VECT([Interrupt #], [Trap Handler]) Registers a handler for an interrupt that has been assigned to a Zone in the multizone.cfg file. When an interrupt occurs, the nanoKernel will immediately pull the zone assigned to that interrupt into context and execute the registered interrupt handler. |
ECALL_IRQ_VECT(11, button_0_handler); Where button_0_handler is a registered at the user level of privilege with: void button_1_handler(void)__attribute__((interrupt("user"))); void button_1_handler(void){ // interrupt handler here } |
ECALL_CSRW_MTIMECMP | void ECALL_CSRW_MTIMECMP(uint64_t) Secure user-mode emulation of the machine-mode timer compare register (mtimecmp). Causes a trap 0x3 exception when the mtime register contains a value greater than or equal to the value assigned. Each zone has its own secure instance of timer and trap handler. Per RISC-V specs this is a one-shot timer: once set it will execute its callback function only once. Note that mtime and mtimecmp size is 64-bit even on rv32 architecture. Registering the trap 0x3 handler sets the value of mtimecmp to zero to prevent spurious interrupts. If the timer is set but no handler is registered the exception is ignored. |
#include <libhexfive.h> ... void trap_0x3_handler(void)__attribute__((interrupt("user"))); void trap_0x3_handler(void){ // do something // restart the timer uint64_t T = 10; // ms uint64_t T0 = ECALL_CSRR_MTIME(); uint64_t T1 = T0 + T*32768/1000; ECALL_CSRR_MTIMECMP(T1); } ... main () { ECALL_TRP_VECT(0x3, trap_0x3_handler); // register 0x3 Soft timer while(1){ // do many things } } |
ECALL_CSRR_MTIME | Int64 ECALL_CSRR_MTIME() Returns MTIME to a variable in a zone, MTIME is a privileged registered normally only available in M mode. |
Int64 mtime = ECALL_CSRR_MTIME(); |
ECALL_CSRR_MCYCLE | Int64 ECALL_CSRR_MCYCLE() Returns MCYCLE to a variable in a zone, MCYCLE is a privileged registered normally only available in M mode. |
Int64 mcycle = ECALL_CSRR_MCYCLE(); |
ECALL_CSRR_MINSTR | Int64 ECALL_CSRR_MINSTR() Returns MINSTR to a variable in a zone, MINSTR is a privileged registered normally only available in M mode. |
Int64 minstr = ECALL_CSRR_MINSTR(); |
ECALL_CSRR_MHPMC3 | Int64 ECALL_CSRR_MHPMC3() Returns MHPMC3 to a variable in a zone, MHPMC3 is a privileged registered normally only available in M mode. |
Int64 mhpmc3 = ECALL_CSRR_MHPMC3(); |
ECALL_CSRR_MHPMC4 | Int64 ECALL_CSRR_MHPMC4() Returns MHPMC4 to a variable in a zone, MHPMC4 is a privileged registered normally only available in M mode. |
Int64 mhpmc3 = ECALL_CSRR_MHPMC4(); |
ECALL_CSRR_MISA | Int64 ECALL_CSRR_MISA() Returns MISA to a variable in a zone, MISA is a privileged registered normally only available in M mode. |
Int64 misa = ECALL_CSRR_MISA(); |
ECALL_CSRR_MVENDID | Int64 ECALL_CSRR_MVENDID() Returns MVENDID to a variable in a zone, MVENDID is a privileged registered normally only available in M mode. |
Int64 misa = ECALL_CSRR_MVENDID(); |
ECALL_CSRR_MARCHID | Int64 ECALL_CSRR_MARCHID() Returns MARCHID to a variable in a zone, MARCHID is a privileged registered normally only available in M mode. |
Int64 marchid = ECALL_CSRR_MARCHID(); |
ECALL_CSRR_MIMPID | Int64 ECALL_CSRR_MIMPID() Returns MIMPID to a variable in a zone, MIMPID is a privileged registered normally only available in M mode. |
Int64 mimpid = ECALL_CSRR_ MIMPID (); |
ECALL_CSRR_MHARTID | Int64 ECALL_CSRR_MHARTID() Returns MHARTID to a variable in a zone, MHARTID is a privileged registered normally only available in M mode. |
Int64 mhardid = ECALL_CSRR_ MHARTID (); |