Give special attributes to a shared memory object
#include <sys/mman.h> int shm_ctl( int fd, int flags, uint64_t paddr, uint64_t size ); int shm_ctl_special( int fd, int flags, uint64_t paddr, uint64_t size, unsigned special );
The primary flags are:
If you create anonymous shared memory objects (by calling mmap() with MAP_ANON | MAP_SHARED and a file descriptor of -1), a MAP_LAZY flag implicitly sets the SHMCTL_LAZY flag on the object.
You can specify the primary flags individually, or in the following combinations:
The secondary flags include:
shm_ctl( fd, SHMCTL_PHYS, baseaddr, vstride ); shm_ctl( fd, SHMCTL_REPEAT, pstride, count );
The value of this argument must be a multiple of the page size (sysconf(_SC_PAGESIZE)).
For SHMCTL_REPEAT, this argument is the length, which is used to increment the offset within the object. The value must be a multiple of the page size.
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
The shm_ctl() function modifies the attributes of the shared memory object identified by the handle, fd. This handle is the value returned by shm_open(). The shm_ctl_special() function is similar to shm_ctl(), but has an additional, processor-specific argument.
Your process might need some abilities enabled (see procmgr_ability()):
In order to use: | You need: |
---|---|
SHMCTL_GLOBAL | PROCMGR_AID_MEM_GLOBAL |
SHMCTL_PHYS | PROCMGR_AID_MEM_PHYS |
shm_ctl_special() | PROCMGR_AID_MEM_SPECIAL |
Typically, you can call shm_ctl() only once on a new object created with shm_open(). Reopening an existing object with the O_TRUNC flag doesn't make the object new again.
You can make subsequent calls to shm_ctl() only in order to extend an existing object, and only when using the same primary flags as in the original call. How you can extend an existing object depends on the primary flags:
If you first initialized an object with shm_ctl_special(), all subsequent calls that follow the rules above must also be done with shm_ctl_special(), using the same special attributes.
Bits in the flags argument for x86 processors
For x86 processors:
Bits in the special argument for ARM processors
For ARM platforms, the special argument specifies the memory attributes that will be used when the object is mapped. You can specify the memory attributes via one of the following:
All loads and stores must be aligned for the access size, otherwise a memory fault is triggered resulting in delivery of a SIGBUS signal.
All loads and stores must be aligned for the access size, otherwise a memory fault is triggered resulting in delivery of a SIGBUS signal.
Reads are performed using a single memory access with the size as specified by the load instruction.
Stores are performed using a single memory access with the size as specified by the load instruction.
An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.
For an example on using this flag, see Sharing device memory below.
Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.
Stores can be merged into a write buffer before being written to the memory system.
An explicit memory barrier may be required after store operations to ensure visibility of the store to the memory system.
Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.
Loads allocate a cache line and return data from the cache.
Stores don't cause allocation of a cache line. If a store hits in the cache, the cache is modified and the data is written to memory via a write buffer. If a store misses in the cache, the data is written to memory via a write buffer. Multiple stores may be merged in the write buffer before being written to the memory system.
Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.
Loads allocate a cache line and return data from the cache.
Stores don't cause allocation of a cache line. If a store hits in the cache, the cache is modified and the line is marked for write back to the memory system at a later time. If a store misses in the cache, the data is written to memory via a write buffer. Multiple stores may be merged in the write buffer before being written to the memory system.
Loads and stores may be performed to unaligned addresses, except for those instructions that always require correct alignment.
Loads allocate a cache line and return data from the cache.
Stores allocate a cache line and modify the cache, marking the line for write back to the memory system at a later time.
You can specify the shareability of the mapping with the following:
For ARM_SHMCTL_NC, accesses are coherent with respect to loads and stores, and the additional coherency applies to load/store exclusive operations.
For ARM_SHMCTL_WT, ARM_SHMCTL_WB, and ARM_SHMCTL_WBWA, this indicates that hardware cache coherency is required.
The level at which this coherency applies is system dependent, and it may still require explicit cache maintenance to enforce coherency for memory accesses by other bus masters (e.g., DMA operations).
You can find full details of the ARM memory attributes and ordering requirements in section A3.5, Memory types and attributes and the memory order model, in the ARM Architecture Reference Manual (ARM DDI 0406).
The following examples go together. Run sharephyscreator, followed by sharephysuser.
The sharephyscreator process maps in an area of physical memory and then overlays it with a shared memory object. The sharephysuser process opens that shared memory object in order to access the physical memory.
/* * sharephyscreator.c * * This maps in an area of physical memory and then * overlays it with a shared memory object. This way, another process * can open that shared memory object in order to access the physical * memory. The other process in this case is sharephysuser. * * Note that the size and address that you pass to shm_ctl() must be * multiples of the page size (sysconf(_SC_PAGESIZE)). * * For VGA color text mode video memory: * sharephyscreator /wally b8000 * Note that for VGA color text mode video memory, each character * is followed by an attribute byte. Here we just use a space. */ #include <errno.h> #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> #include <sys/neutrino.h> #include <sys/stat.h> char *progname = "sharephyscreator"; main( int argc, char *argv[] ) { char *text = "H e l l o w o r l d ! "; int fd, memsize; char *ptr, *name; uint64_t physaddr; if ( argc != 3 ) { printf( "Use: sharephyscreator shared_memory_object_name \ physical_address_in_hex\n" ); printf( "Example: sharephyscreator wally b8000\n" ); exit( EXIT_FAILURE ); } name = argv[1]; physaddr = strtoul(argv[2], NULL, 16); memsize = sysconf( _SC_PAGESIZE ); /* this should be enough for our string */ /* map in the physical memory */ ptr = mmap( 0, memsize, PROT_READ | PROT_WRITE, MAP_PHYS | MAP_SHARED, NOFD, physaddr ); if ( ptr == MAP_FAILED ) { printf( "%s: mmap for physical address %llx failed: %s\n", progname, physaddr, strerror(errno) ); exit( EXIT_FAILURE ); } /* open the shared memory object, create it if it doesn't exist */ fd = shm_open( name, O_RDWR | O_CREAT, 0 ); if ( fd == -1 ) { printf( "%s: error creating the shared memory object '%s': %s\n", progname, name, strerror(errno) ); exit( EXIT_FAILURE ); } /* overlay the shared memory object onto the physical memory */ if ( shm_ctl( fd, SHMCTL_PHYS, physaddr, memsize ) == -1 ) { printf( "%s: shm_ctl failed: %s\n", progname, strerror(errno) ); close( fd ); munmap( ptr, memsize ); shm_unlink( name ); exit( EXIT_FAILURE ); } strcpy( ptr, text ); /* write to the shared memory */ printf( "\n%s: Physical memory mapped in, shared memory overlaid onto it.\n" "%s: Wrote '%s' to physical memory.\n" "%s: Sleeping for 20 seconds. While this program is sleeping\n" "%s: run 'sharephysuser %s %d'.\n", progname, progname, ptr, progname, progname, name, strlen(text)+1 ); sleep( 20 ); printf( "%s: Woke up. Cleaning up and exiting ...\n", progname ); close( fd ); munmap( ptr, memsize ); shm_unlink( name ); }
The following is meant to be run with sharephyscreator.
/* * sharephysuser.c * * This one is meant to be run in tandem with sharephyscreator. * * Run it as: sharephysuser shared_memory_object_name length * Example: sharephysuser wally 49 * */ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> #include <sys/neutrino.h> #include <sys/stat.h> char *progname = "sharephysuser"; main( int argc, char *argv[] ) { int fd, len, i; char *ptr, *name; if ( argc != 3 ) { fprintf( stderr, "Use: sharephysuser shared_memory_object_name \ length\n" ); fprintf( stderr, "Example: sharephysuser wally 49\n" ); exit( EXIT_FAILURE ); } name = argv[1]; len = atoi( argv[2] ); /* open the shared memory object */ fd = shm_open( name, O_RDWR, 0 ); if ( fd == -1 ) { fprintf( stderr, "%s: error opening the shared memory object '%s': %s\n", progname, name, strerror(errno) ); exit( EXIT_FAILURE ); } /* get a pointer to a piece of the shared memory, note that we only map in the amount we need to */ ptr = mmap( 0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); if ( ptr == MAP_FAILED ) { fprintf( stderr, "%s: mmap failed: %s\n", progname, strerror(errno) ); exit( EXIT_FAILURE ); } printf( "%s: reading the text: ", progname ); for ( i = 0; i < len; i++ ) printf( "%c", ptr[i] ); printf( "\n" ); close( fd ); munmap( ptr, len ); }
/* * create_device_shm.c */ #include <string.h> #include <unistd.h> #include <sys/mman.h> #include <aarch64/mmu.h> #include <stdint.h> #include <assert.h> #include <fcntl.h> #define S1_SHM_NAME "shm_s1" #define TOTAL_PAGE_SIZE (4096) static char *src; static int src_fd; /** Remove the shared memory object and destroy the device memory */ void destroy_device_shm(int shm_fd, char *name) { close(shm_fd); shm_unlink(name); } /** * Create a shared memory object and configure it to simulate device memory. * Return the file descriptor of the new object if successful, -1 otherwise. */ int create_device_shm(char *name, uint64_t size) { int fd; int rc; fd = shm_open(name, O_RDWR|O_CREAT|O_TRUNC, 0666); if (fd < 0) { return -1; } rc = shm_ctl_special(fd, SHMCTL_ANON | SHMCTL_PHYS, 0, size, ARM_SHMCTL_DEV); if (rc != 0) { destroy_device_shm(fd, name); return -1; } return fd; } void cleanup() { munmap(src, TOTAL_PAGE_SIZE); destroy_device_shm(src_fd, S1_SHM_NAME); } int main(void) { // Create device memory region src_fd = create_device_shm(S1_SHM_NAME, TOTAL_PAGE_SIZE); assert(src_fd >= 0); // Map device memory into current address space src = mmap(NULL, TOTAL_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, src_fd, 0); assert(src != (void *)MAP_FAILED); // Your code goes here // Destroy device memory region cleanup(); return 0; }
Safety: | |
---|---|
Cancellation point | Yes |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |