Menu

Expand
Rate this page:

Microvisor Notifications

Microvisor provides a notification mechanism which the application can use to be informed about the outcome of operations and system events. Microvisor issues two broad classes of notification — action completion notifications and status notifications — but both work in the same way: they are dispatched to the application by one or more notification centers.

If Microvisor is not able to attempt a requested action — the request was malformed, for example, or was configured to use memory to which the application is not permitted to access — the system call will immediately return an error code, and no notification will be issued. Notifications are only issued when Microvisor can perform a requested action. The final outcome of that action may be success or failure, of course.

Action completion notifications

Many Microvisor system calls trigger asynchronous operations: the actions requested by the application take a finite but unknown length of time to complete. For this reason, the Microvisor notification system is used to inform the application when the requested operation has been completed. The notification will include the outcome of the action: whether it succeeded or failed.

Status notifications

Notifications are also used to inform the application when certain key system events take place. The application must register its interest in these events and will then receive notifications if and when they take place. Unlike action completion notifications, status notifications do not come in response to a specific operation requested by the application.

How notifications work

Microvisor dispatches notifications through one or more notification centers. You can establish as many centers as you need: you are limited only by the amount of non-secure memory available to hold the centers’ notification-storage buffers. Buffers are sized and allocated for this purpose by the application, and referenced as a pointer during center setup. Each notification center is identified by its own handle.

Before using a handle, you should always check that it is not zero. Microvisor defines an invalid handle as any handle with a value of zero. All extant handles are zeroed when the host microcontroller restarts, and specific handles are zeroed when the resource they refer to is relinquished or closed by the application. If you attempt to make use of an invalid handle, Microvisor will report it as an error.

The memory allocated to receive notifications comprises contiguous bytes but they used as backing for a circular buffer. Each notification center maintains a write pointer to the next entry in its buffer, and when this pointer reaches past the final allocated byte, it is automatically moved back to the start of the buffer, as shown below:

Microvisor's notification buffer

With a buffer capable of holding n notifications, Microvisor at some point in the application lifecycle writes a sequence of four 16-byte notifications (red squares 1—4). Because of the circularity of the buffer, notifications 3 and 4 are written at the start of the buffer. The write pointer is set to record the next record (first grey square)

After it has written a notification, the notification center raises an interrupt to signal to the application that a new notification has been posted. When it asks Microvisor to create a notification center, the application provides a non-secure interrupt line’s IRQ number. The application may process notifications in its interrupt service routine, or it can choose to defer notification processing to its main run loop by setting a flag or some other record.

Notification center setup

To begin receiving notifications, your code first creates a notification center. It calls mvSetupNotifications() and passes a reference to a notification setup structure which contains the information the notification center will need to dispatch notifications to the application:

struct MvNotificationSetup {
  uint32_t irq;
  struct MvNotification *buffer;
  uint32_t buffer_size;
}

The application can create as many notification centers as it requires, but each one must specify a unique, valid IRQ.

In addition to a NotificationSetup structure, your code also passes in a pointer to four bytes of memory into which Microvisor will write the new notification center’s unique handle. System calls that respond with notifications require you to supply the handle of the notification center that you would like to dispatch their notifications.

The interrupt

The value of irq in the NotificationSetup structure is the number of the non-secure interrupt line that will be triggered to signal that a new notification has been posted. The call to mvSetupNotifications() will return an error if this value indicates a secure interrupt, or has already been assigned to a notification center.

The buffer

To receive any kind of notification, the application must allocate memory into which the notification center will write the notifications it posts. All notifications are 16 bytes in size, and the application is expected to allocate space for at least two of them. The first byte of the notification buffer must also be aligned to an eight-byte boundary. The buffer must be valid for the lifetime of the notification center that uses it.

Microvisor imposes no other restrictions on a notification buffer. The application may use one buffer or many: each center targets one buffer, but that buffer can be shared among notification centers. Many applications will be able to make do with a single notification center targeting one buffer. However many buffers your application allocates, each one can be any size from 32 bytes upwards — though any notification buffer’s size must be a multiple of 16 bytes, of course.

Reading notifications

The application should maintain per buffer read pointers that references where the next notification to be read from a given buffer is located. Microvisor doesn’t provide read pointers; this is the responsibility of the application. Because the application may choose to defer reading notifications, Microvisor provides a protocol that allows the application to detect when it has reached the end of a sequence of notifications. Each notification has a four-byte event_type field, and Microvisor zeros this field in the space after the notification it has just written. When the application reads a notification of type zero, it knows it has now read all currently available notifications.

Buffer overruns

If the application doesn’t read a notification in its interrupt service routine, or the interrupt is ignored — i.e., it is masked, has too low a priority, or has been disabled — there is a risk that the notification center’s write pointer will overtakes the application’s read pointer: a buffer overrun. The application can detect this, if it wishes, by zeroing the event_type of notifications as they are read, and on each interrupt checking that the entry prior to the read pointer has been zeroed.

One write pointer per buffer

Microvisor maintains a single write pointer for each unique buffer reference supplied by the application during notification center setup. When it is passed a buffer pointer, it checks if that buffer is already in use. If it is, Microvisor uses the pre-existing write pointer to ensure that notifications from action A don’t overwrite those from action B.

So if the application has multiple notification centers sharing a single buffer, and therefore provides the same pointer and buffer size through multiple notification configurations, Microvisor will maintain a single write pointer to that buffer.

Notifications

Each notification has the following 16-byte structure:

struct MvNotification { 
  uint64_t microseconds;
  enum MvEventType event_type;
  uint32_t tag; 
}

The value of microseconds is the value of the system’s monotonic microsecond counter when the notification was written. Use this to measure and assess the duration of key operations.

The event_type field indicates what event or action caused the notification to be issued, useful especially if your application uses a single buffer for a range of event types. Your code can check if this value is zero when it is iterating over a number of notifications: an event_type of zero indicates it has now read all the currently available notifications, and its read pointer is placed ready for the next notification to be posted. It can also be used to detect buffer overruns. The available event types are listed under each system call that initiates notifications.

The value of tag will match the tag set during the call that triggered the dispatch of the notification.

Tags

To help applications keep track of which notification was the result of what specific action, an application can provide a tag whenever it calls an System function which will result in notifications being dispatched

A tag is an arbitrary 32-bit value, which can be a pointer, a reference to an object, an index in a table, or whatever the application chooses.

The tag is set when the application requests an operation that may issue notifications. Microvisor makes no use of tags itself: it stores them at registration, and the center includes it in any relevant notifications it dispatches subsequently.

Tags are tied to operations or events, so even if a notification center is dispatching notifications for a variety of operations, notifications will always include the correct tag.

Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.

Thank you for your feedback!

Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

Sending your feedback...
🎉 Thank you for your feedback!
Something went wrong. Please try again.

Thanks for your feedback!

Refer us and get $10 in 3 simple steps!

Step 1

Get link

Get a free personal referral link here

Step 2

Give $10

Your user signs up and upgrade using link

Step 3

Get $10

1,250 free SMSes
OR 1,000 free voice mins
OR 12,000 chats
OR more