The direct_memory
interface is implemented by objects that
model memory, such as RAM and ROM objects. These are called direct-memory
objects. A user of the interface is called a memory user and is
typically a processor that wants to do fast accesses to memory. The
direct-memory object corresponding to a particular physical address
can be obtained using the lookup method of the
direct_memory_lookup
interface.
See the documentation for the
direct_memory_lookup
interface for more information.
A memory user using the direct_memory
interface
must implement the direct_memory_update
interface.
The get_handle method is used by a memory user to create or retrieve a handle to the memory region starting at offset offs with size size. The handle is typically used later on to request access permissions and to retrieve a direct pointer to the region. The handle returned by get_handle is private to the memory user specified in the requester parameter.
If get_handle is invoked multiple times for the same range, and with identical requester and subsystem arguments, then the same handle will be returned each time, assuming the original handle is still valid. Note that the original handle is only returned if the range matches exactly. A single memory user can obtain multiple distinct handles for the same memory range by using different values for the subsystem parameter.
For RAM and ROM, offs and size must specify a region which does not intersect a naturally aligned 8192 byte boundary, or the request will fail with a NULL return value. Other direct-memory objects might have different requirements.
The request method is used to request a host pointer to simulated memory. This pointer can be used to carry out fast memory operations without having to involve the Simics API. The handle argument is the handle obtained using get_handle.
Both the permission argument and the inhibit argument
are access_t
type bit fields. The permission
argument is used to specify what kind of memory operations the memory user
will perform. For example, if a memory user wants to read memory, the
permission argument must include the Sim_Access_Read value. The
inhibit argument specifies what other memory users are not
allowed to do. For example, if inhibit is set to Sim_Access_Write
other memory users are not allowed to write to the memory range. This
protection mechanism can be used to create caches of simulated memory,
request exclusive permissions to a memory range in order to carry out atomic
operations, and similar. When a memory user is requesting permission to a
memory range that another memory user has protected with conflicting inhibit
bits, the direct-memory object will inform the other memory user of
the lost permissions and protection through the
direct_memory_update
interface. A user can lose both the
permission and protection for a memory range in this way. When this happens,
a memory user may re-request permissions and inhibit protection.
Note: if a memory user has multiple handles which overlaps, then each handle is considered to be a distinct memory user. For example, if a memory user holds two handles, and requests write inhibit on one of them, then write permission will be revoked from the second handle (if such permission had been granted).
The request method returns a direct_memory_t
value
with information about the retrieved permissions and inhibit bits. These
bits can be a super set of the bits that actually were requested. The
returned data pointer can be used to access the memory range. Accesses are
valid from the data pointer and up to the end of the range, i.e., addresses
up to data pointer + size - 1, where size is the size valid for
the handle. A call to request always succeeds and the
corresponding memory range is valid until the permissions or the handle are
revoked by the direct_memory_update
interface. Note that the
data pointer may change each time request is called (with the
same handle) since Simics may move simulated memory. If the pointer
changes, then the old pointer must not be used.
With set_user_data, a memory user can associate a user-defined pointer with a specific handle. The pointer can be retrieved using the get_user_data method, which takes a handle as an argument.
A memory user can use the release function to notify the direct-memory object when it is no longer interested in the memory region corresponding to handle. The handle is invalid and must not be used for anything after being released.
The ack method is used by a memory user to inform the
direct-memory object that it has given up the corresponding permission and
inhibit rights for a memory range when called by a method in the
direct_memory_update
interface.
Permissions can be revoked from all memory users by invoking the
revoke method. The permission parameter
specifies the permissions which will be revoked from all memory users.
Similarly, inhibit specifies the inhibit privileges which
will be revoked. For instance, calling revoke with
permission
set to Sim_Access_Write
will ensure that nobody has
write permissions to the direct-memory object.
typedef granted_mem_t *direct_memory_handle_t;
typedef struct { #ifndef PYWRAP uint8 *data; #endif access_t permission; access_t inhibit; } direct_memory_t;
typedef uint64 direct_memory_ack_id_t;
SIM_INTERFACE(direct_memory) { direct_memory_handle_t (*get_handle)(conf_object_t *NOTNULL obj, conf_object_t *NOTNULL requester, uint64 subsystem, uint64 offs, unsigned size); direct_memory_t (*request)(conf_object_t *NOTNULL obj, direct_memory_handle_t handle, access_t permission, access_t inhibit); void (*revoke)(conf_object_t *NOTNULL obj, access_t access, access_t permission, access_t inhibit); #ifndef PYWRAP void *(*get_user_data)(conf_object_t *NOTNULL obj, direct_memory_handle_t handle); void (*set_user_data)(conf_object_t *NOTNULL obj, direct_memory_handle_t handle, void *user_data); #endif void (*release)(conf_object_t *NOTNULL obj, direct_memory_handle_t handle); void (*ack)(conf_object_t *NOTNULL obj, direct_memory_ack_id_t id); }; #define DIRECT_MEMORY_INTERFACE "direct_memory"