vcio2 API Reference

▲ Top, ▼ vcio2 IOCTLs, ▼ Memory mapping, ▶ Programming guide

Access vcio2 device

open device

int vcio2 = open("/dev/vcio2", O_RDWR);

Open the vcio2 device for further usage.

Note that all resources acquired by this device are tied to this device handle. So do not close it unless you no longer need the resources.

Device handles of vcio2 cannot be reasonably inherited nor passed to forked process instances. The resources are always tied to the PID that opened the device.

All calls to vcio2 handles are thread-safe.

close device


Close the vcio2 device and release all resources.

vcio2 IOCTLs


Allocate GPU memory. The memory is continuous in physical address and taken from the reserved GPU memory pool.

typedef struct
{ union {
    { unsigned int size;
      unsigned int alignment;
      unsigned int flags;
} in; struct { unsigned int handle; } out; }; } vcio_mem_allocate;

vcio_mem_allocate buf;
int retval = ioctl(vcio2, IOCTL_MEM_ALLOCATE, &buf);
Number of bytes to allocate.
Note that with CMA enabled (dynamic GPU memory size) in /boot/config.txt allocations of more than 16 MiB seem to fail.
Alignment of the resulting buffer in physical memory.
0xC := cached; 0x4 := direct
Other flags unknown. The parameter is directly passed to the VCMSG_SET_ALLOCATE_MEM mailbox message.
return value
0 := success; any other value = error code. See errno.h.
Memory handle. To be used with IOCTL_MEM_LOCK.

vcio2 keeps track of the allocated memory chunks. As soon as the vcio2 device is closed or the application terminates. The memory is given back to the GPU memory pool. So remember to keep the device open!


int retval = ioctl(vcio2, IOCTL_MEM_RELEASE, handle);

Release GPU memory. This also unlocks the memory segment if still locked.

Memory handle from IOCTL_MEM_ALLOC.
return value
0 := success; any other value = error code. See errno.h.

IOCTL_MEM_LOCK (0xC0046403)

int addr = handle;
int retval = ioctl(vcio2, IOCTL_MEM_LOCK, &addr);

Lock the memory segment at a physical address.

Memory handle from IOCTL_MEM_ALLOC.
return value
0 := success; any other value = error code. See errno.h.
Physical memory address where the memory segment has been locked.


int retval = ioctl(vcio2, IOCTL_MEM_UNLOCK, handle);

Unlock memory segment and release the binding to a physical address.

Memory handle from IOCTL_MEM_ALLOC.
return value
0 := success; any other value = error code. See errno.h.


Execute QPU code.

typedef struct
{ unsigned int uniforms;
  unsigned int code;
} vcio_exec_qpu_entry;
typedef struct { struct { unsigned int num_qpus; unsigned int control; unsigned int noflush; unsigned int timeout; } in; } vcio_exec_qpu;

vcio_exec_qpu buf;
int retval = ioctl(vcio2, IOCTL_EXEC_QPU, &buf);
Number of QPUs that should be kicked off. Each QPU receives their own shader code and their own set of uniforms. So this is also the size of the control array.
Setup entries for each QPU containing the physical start address of the uniforms and the code. This is a physical pointer to an array of vcio_qpu_entry structures with exactly num_qpus elements.
Flag: do not flush the cache before starting the QPU code.
Timeout in milliseconds to wait for an host interrupt. If the timeout elapses the function returns with an error. Note that this will not stop the QPU code so far.
return value
0 in case of success; non-zero otherwise. You will get EACCES when one of the physical memory pointers do not belong to a locked memory segment allocated by the same device handle.

Although vcio2 does some basic checks to prevent accidental access to invalid memory it cannot check for memory access done by the QPU code. So you have to take care to execute only valid QPU code, otherwise the Raspberry might crash. However, in most cases the Raspi will recover from faults after the timeout and no resources will be lost. So GPU development is significantly relaxed.

While GPU code is executing the Raspi kernel can no longer access the the property channel used for several other purposes, e.g. power management or several firmware calls. Every attempt to do such a function is blocked until the GPU code raises an host interrupt or the timeout elapsed. This is a restriction of the firmware rather than vcio2.

Memory mapping

To be able to access the GPU memory from the ARM cortex you will need map the memory into you physical address space. Simply use mmap with the vcio2 device handle for this purpose.

unsigned offset = base & 0xFFF;
char *mem = mmap(0, size + offset, PROT_READ|PROT_WRITE, MAP_SHARED, vcio2, base - offset);
if (mem != MAP_ERROR)
mem += offset;
Physical address of the memory block from IOCTL_MEM_LOCK.
Note that mmap requires base to be aligned at a pages boundary (4092 bytes). Therefore the bit mask.
Number of bytes to map. This should be the same than the size passed to IOCTL_MEM_ALLOC.
return value mem
Virtual address of the mapped memory or MAP_FAILED on error.
mem + offset
Virtual address of the GPU memory to be used by the ARM core.

If the GPU memory is allocated with at least 4k alignment the offset calculations could be dropped since offset is always zero.

vcio2 validates the memory mappings. I.e. you can only map memory that has been previously allocated with the same device handle. Otherwise you get an EACCES error.