Monday, December 5, 2011

RTEMS Memory Protection API

I am working on a solution to introduce real-time memory protection to RTEMS. I started from the work of two GSOC projects related to MMU support in RTEMS, and you can see most of the raw code that I have at http://code.google.com/p/gsoc2011-rtems-mmu-support-project/. This post describes the current design of the (high-level) API layer for setting attributes on regions of memory. I plan to post in the future about the middle and low-level interfaces. Any suggestions about design/naming/implementation etc. are welcomed.

The high-level interface comprises a region and attributes to create a memory protection entry (MPE), a set of which constitute a memory protection domain (MPD). Three structures are visible at the API layer, although only regions should ever be directly accessed; MPEs and MPDs are only meant to be used as handles in userland.
/**
 * A region of contiguous memory
 */
typedef struct
{
 char  *name;
 void  *base;
 size_t bounds;
} rtems_memory_protection_region_descriptor;

typedef uint8_t rtems_memory_protection_permission;

/**
 * A memory protection entry (MPE) is a region of contiguous memory
 * (rtems_memory_protection_region_descriptor) with a set of permissions.
 * If it is currently being enforced then the installed field is true.
 */
typedef struct
{
 rtems_chain_node                          node;
 rtems_memory_protection_region_descriptor region;
 bool                                      installed; 
 rtems_memory_protection_permission        permissions;
} rtems_memory_protection_entry;

/**
 * A memory protection domain comprises a chain of active MPEs and 
 * a freelist of idle MPEs. MPE storage is allocated from the Workspace
 * and managed in the MPD.
 */
typedef struct
{
  rtems_memory_protection_entry  *mpe_array;
  size_t                          mpe_array_size;
  rtems_chain_control             active_mpe_chain;
  rtems_chain_control             idle_mpe_chain;    /* free list */
} rtems_memory_protection_domain;

Application developers can create regions, for example
rtems_memory_protection_region_descriptor text_region = {
    .name = "text",
    .base = TEXT_BEGIN,
    .bounds = TEXT_SIZE
  };
Where TEXT_BEGIN and TEXT_SIZE are specified somewhere. The advantage of using a name to identify a region is that eventually we could layer a filesystem over the MPD structure and open memory files with specified attributes, for example: int fd = open("/memory/text", O_RDWR);. I have not given thought to how best to create regions, but that interface should be orthogonal to the protection API.

Regions must be encapsulated within MPEs and added to a MPD to have permissions enforced.  The requisite functions are
/**
 *  Allocates an array of max_number_of_mpes 
 *  memory protection entries from the workspace.
 */
rtems_status_code rtems_memory_protection_initialize_domain(
  rtems_memory_protection_domain *domain,
  size_t max_number_of_mpes
);

/**
 * Create a new memory protection entry for the region with
 * permissions in perms. This function adds the newly created mpe
 * to the active chain of the_domain but does not install it. 
 */
rtems_status_code rtems_memory_protection_create_entry(
  rtems_memory_protection_domain* the_domain,
  rtems_memory_protection_region_descriptor * const region,
  const rtems_memory_protection_permission perms,
  rtems_memory_protection_entry** mpe_ret
);

/**
 * Install all mpes on the active list of the_domain.
 */
rtems_status_code rtems_memory_protection_install_domain(
  rtems_memory_protection_domain* the_domain
);
So to enforce the text_region from above:
rtems_memory_protection_domain *protection_domain;
rtems_memory_protection_entry *mp_entry;
rtems_memory_protection_permission permission;

rtems_memory_protection_initialize_domain( protection_domain, 8 );

permission = RTEMS_MEMORY_PROTECTION_READ_PERMISSION | 
             RTEMS_MEMORY_PROTECTION_EXECUTE_PERMISSION;

rtems_memory_protection_create_entry(
    protection_domain,
    &text_region,
    permission,
    &mp_entry
);

rtems_memory_protection_install_domain(protection_domain);
One aspect I'm not happy with is the permissions field, an 8-bit field with preprocessor macros defining masks. A better solution would improve usability. The remainder of the API layer are a handful of functions to manage MPEs (find mpe by address/MPD, delete mpe from mpd, set permission) and MPDs (uninstall all MPEs and finalize to free resources).

Update: continued here

No comments:

Post a Comment