This patch adds compatibility wrappers for the ioctls exposed by vchiq/vc04_services. The compat ioctls are completely implemented on top of the native ioctls. No existing lines are modified. While the ideal approach would be to cleanup the existing code, this path is simplier and easier to review. While it does have a small runtime performance penality vs seperating the existing code into wrapper+worker functions, the penality is small since only the metadata is copied back onto the 32 bit user mode stack. The on top of approach is the approach used by several existing performance critical subsystems of Linux such as the DRM 3D graphics subsystem. Testing: 1. A 32 bit chroot was created on a RPI 3 and vchiq_test was built for armhf. The usual tests were run such as vchiq_test -f 10 and vchiq_test -p. 2. This patch was applied to the shipping version of the Linux kernel used for the RPI and that kernel was built for arm64. That kernel was used to boot Raspbian. Many of the builtin features are now functional such as the "hello_pi" examples, and minecraft_pi. Changes: V1 - Complete rewrite of the ioctl code. V2 - Rewrite of only ioctls that change between 32 bit and 64 bit. V3 - Minor changes. V4(This Version) - Abandon cleaning up the exising code and completely write the wrappers on top of the native ioctls. No existing lines are changed. Signed-off-by: Michael Zoran <mzoran@xxxxxxxxxxxx> --- .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 446 +++++++++++++++++++++ 1 file changed, 446 insertions(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 19bd4ac6e855..90dfa79089d3 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -47,6 +47,7 @@ #include <linux/list.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/compat.h> #include <soc/bcm2835/raspberrypi-firmware.h> #include "vchiq_core.h" @@ -1227,6 +1228,448 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } +#if defined(CONFIG_COMPAT) + +struct vchiq_service_params32 { + int fourcc; + compat_uptr_t callback; + compat_uptr_t userdata; + short version; /* Increment for non-trivial changes */ + short version_min; /* Update for incompatible changes */ +}; + +struct vchiq_create_service32 { + struct vchiq_service_params32 params; + int is_open; + int is_vchi; + unsigned int handle; /* OUT */ +}; + +#define VCHIQ_IOC_CREATE_SERVICE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 2, struct vchiq_create_service32) + +static long +vchiq_compat_ioctl_create_service( + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_CREATE_SERVICE_T __user *args; + struct vchiq_create_service32 __user *ptrargs32 = + (struct vchiq_create_service32 __user *)arg; + struct vchiq_create_service32 args32; + long ret; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_create_service32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.params.fourcc, &args->params.fourcc) || + put_user(compat_ptr(args32.params.callback), + &args->params.callback) || + put_user(compat_ptr(args32.params.userdata), + &args->params.userdata) || + put_user(args32.params.version, &args->params.version) || + put_user(args32.params.version_min, + &args->params.version_min) || + put_user(args32.is_open, &args->is_open) || + put_user(args32.is_vchi, &args->is_vchi) || + put_user(args32.handle, &args->handle)) + return -EFAULT; + + ret = vchiq_ioctl(file, VCHIQ_IOC_CREATE_SERVICE, (unsigned long)args); + + if (ret < 0) + return ret; + + if (get_user(args32.handle, &args->handle)) + return -EFAULT; + + if (copy_to_user(&ptrargs32->handle, + &args32.handle, + sizeof(args32.handle))) + return -EFAULT; + + return ret; +} + +struct vchiq_element32 { + compat_uptr_t data; + unsigned int size; +}; + +struct vchiq_queue_message32 { + unsigned int handle; + unsigned int count; + compat_uptr_t elements; +}; + +#define VCHIQ_IOC_QUEUE_MESSAGE32 \ + _IOW(VCHIQ_IOC_MAGIC, 4, struct vchiq_queue_message32) + +static long +vchiq_compat_ioctl_queue_message(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_QUEUE_MESSAGE_T *args; + VCHIQ_ELEMENT_T *elements; + struct vchiq_queue_message32 args32; + unsigned int count; + + if (copy_from_user(&args32, + (struct vchiq_queue_message32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + args = compat_alloc_user_space(sizeof(*args) + + (sizeof(*elements) * MAX_ELEMENTS)); + + if (!args) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(args32.count, &args->count) || + put_user(compat_ptr(args32.elements), &args->elements)) + return -EFAULT; + + if (args32.elements && args32.count && !(args32.count > MAX_ELEMENTS)) { + struct vchiq_element32 tempelement32[MAX_ELEMENTS]; + + elements = (VCHIQ_ELEMENT_T __user *)(args + 1); + + if (copy_from_user(&tempelement32, + compat_ptr(args32.elements), + sizeof(tempelement32))) + return -EFAULT; + + for (count = 0; count < args32.count; count++) { + if (put_user(compat_ptr(tempelement32[count].data), + &elements[count].data) || + put_user(tempelement32[count].size, + &elements[count].size)) + return -EFAULT; + } + + if (put_user(elements, &args->elements)) + return -EFAULT; + } + + return vchiq_ioctl(file, VCHIQ_IOC_QUEUE_MESSAGE, (unsigned long)args); +} + +struct vchiq_queue_bulk_transfer32 { + unsigned int handle; + compat_uptr_t data; + unsigned int size; + compat_uptr_t userdata; + VCHIQ_BULK_MODE_T mode; +}; + +#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 \ + _IOWR(VCHIQ_IOC_MAGIC, 5, struct vchiq_queue_bulk_transfer32) +#define VCHIQ_IOC_QUEUE_BULK_RECEIVE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 6, struct vchiq_queue_bulk_transfer32) + +static long +vchiq_compat_ioctl_queue_bulk(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_QUEUE_BULK_TRANSFER_T *args; + struct vchiq_queue_bulk_transfer32 args32; + struct vchiq_queue_bulk_transfer32 *ptrargs32 = + (struct vchiq_queue_bulk_transfer32 *)arg; + long ret; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_queue_bulk_transfer32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(compat_ptr(args32.data), &args->data) || + put_user(args32.size, &args->size) || + put_user(compat_ptr(args32.userdata), &args->userdata) || + put_user(args32.mode, &args->mode)) + return -EFAULT; + + if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32) + cmd = VCHIQ_IOC_QUEUE_BULK_TRANSMIT; + else + cmd = VCHIQ_IOC_QUEUE_BULK_RECEIVE; + + ret = vchiq_ioctl(file, cmd, (unsigned long)args); + + if (ret < 0) + return ret; + + if (get_user(args32.mode, &args->mode)) + return -EFAULT; + + if (copy_to_user(&ptrargs32->mode, + &args32.mode, + sizeof(args32.mode))) + return -EFAULT; + + return ret; +} + +struct vchiq_completion_data32 { + VCHIQ_REASON_T reason; + compat_uptr_t header; + compat_uptr_t service_userdata; + compat_uptr_t bulk_userdata; +}; + +struct vchiq_await_completion32 { + unsigned int count; + compat_uptr_t buf; + unsigned int msgbufsize; + unsigned int msgbufcount; /* IN/OUT */ + compat_uptr_t msgbufs; +}; + +#define VCHIQ_IOC_AWAIT_COMPLETION32 \ + _IOWR(VCHIQ_IOC_MAGIC, 7, struct vchiq_await_completion32) + +static long +vchiq_compat_ioctl_await_completion(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_AWAIT_COMPLETION_T *args; + VCHIQ_COMPLETION_DATA_T *completion; + VCHIQ_COMPLETION_DATA_T completiontemp; + struct vchiq_await_completion32 args32; + struct vchiq_completion_data32 completion32; + unsigned int *msgbufcount32; + compat_uptr_t msgbuf32; + void *msgbuf; + void **msgbufptr; + long ret; + + args = compat_alloc_user_space(sizeof(*args) + + sizeof(*completion) + + sizeof(*msgbufptr)); + if (!args) + return -EFAULT; + + completion = (VCHIQ_COMPLETION_DATA_T *)(args + 1); + msgbufptr = (void __user **)(completion + 1); + + if (copy_from_user(&args32, + (struct vchiq_completion_data32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.count, &args->count) || + put_user(compat_ptr(args32.buf), &args->buf) || + put_user(args32.msgbufsize, &args->msgbufsize) || + put_user(args32.msgbufcount, &args->msgbufcount) || + put_user(compat_ptr(args32.msgbufs), &args->msgbufs)) + return -EFAULT; + + if (!args32.count || !args32.buf || !args32.msgbufcount) + return vchiq_ioctl(file, + VCHIQ_IOC_AWAIT_COMPLETION, + (unsigned long)args); + + if (copy_from_user(&msgbuf32, + compat_ptr(args32.msgbufs) + + (sizeof(compat_uptr_t) * + (args32.msgbufcount - 1)), + sizeof(msgbuf32))) + return -EFAULT; + + msgbuf = compat_ptr(msgbuf32); + + if (copy_to_user(msgbufptr, + &msgbuf, + sizeof(msgbuf))) + return -EFAULT; + + if (copy_to_user(&args->msgbufs, + &msgbufptr, + sizeof(msgbufptr))) + return -EFAULT; + + if (put_user(1U, &args->count) || + put_user(completion, &args->buf) || + put_user(1U, &args->msgbufcount)) + return -EFAULT; + + ret = vchiq_ioctl(file, + VCHIQ_IOC_AWAIT_COMPLETION, + (unsigned long)args); + + if (ret <= 0) + return ret; + + if (copy_from_user(&completiontemp, completion, sizeof(*completion))) + return -EFAULT; + + completion32.reason = completiontemp.reason; + completion32.header = ptr_to_compat(completiontemp.header); + completion32.service_userdata = + ptr_to_compat(completiontemp.service_userdata); + completion32.bulk_userdata = + ptr_to_compat(completiontemp.bulk_userdata); + + if (copy_to_user(compat_ptr(args32.buf), + &completion32, + sizeof(completion32))) + return -EFAULT; + + args32.msgbufcount--; + + msgbufcount32 = + &((struct vchiq_await_completion32 __user *)arg)->msgbufcount; + + if (copy_to_user(msgbufcount32, + &args32.msgbufcount, + sizeof(args32.msgbufcount))) + return -EFAULT; + + return ret; +} + +struct vchiq_dequeue_message32 { + unsigned int handle; + int blocking; + unsigned int bufsize; + compat_uptr_t buf; +}; + +#define VCHIQ_IOC_DEQUEUE_MESSAGE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 8, struct vchiq_dequeue_message32) + +static long +vchiq_compat_ioctl_dequeue_message(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_DEQUEUE_MESSAGE_T *args; + struct vchiq_dequeue_message32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_dequeue_message32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(args32.blocking, &args->blocking) || + put_user(args32.bufsize, &args->bufsize) || + put_user(compat_ptr(args32.buf), &args->buf)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_DEQUEUE_MESSAGE, + (unsigned long)args); +} + +struct vchiq_get_config32 { + unsigned int config_size; + compat_uptr_t pconfig; +}; + +#define VCHIQ_IOC_GET_CONFIG32 \ + _IOWR(VCHIQ_IOC_MAGIC, 10, struct vchiq_get_config32) + +static long +vchiq_compat_ioctl_get_config(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_GET_CONFIG_T *args; + struct vchiq_get_config32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_get_config32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.config_size, &args->config_size) || + put_user(compat_ptr(args32.pconfig), &args->pconfig)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_GET_CONFIG, (unsigned long)args); +} + +struct vchiq_dump_mem32 { + compat_uptr_t virt_addr; + u32 num_bytes; +}; + +#define VCHIQ_IOC_DUMP_PHYS_MEM32 \ + _IOW(VCHIQ_IOC_MAGIC, 15, struct vchiq_dump_mem32) + +static long +vchiq_compat_ioctl_dump_phys_mem(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_DUMP_MEM_T *args; + struct vchiq_dump_mem32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_dump_mem32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(compat_ptr(args32.virt_addr), &args->virt_addr) || + put_user(args32.num_bytes, &args->num_bytes)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_DUMP_PHYS_MEM, (unsigned long)args); +} + +static long +vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case VCHIQ_IOC_CREATE_SERVICE32: + return vchiq_compat_ioctl_create_service(file, cmd, arg); + case VCHIQ_IOC_QUEUE_MESSAGE32: + return vchiq_compat_ioctl_queue_message(file, cmd, arg); + case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32: + case VCHIQ_IOC_QUEUE_BULK_RECEIVE32: + return vchiq_compat_ioctl_queue_bulk(file, cmd, arg); + case VCHIQ_IOC_AWAIT_COMPLETION32: + return vchiq_compat_ioctl_await_completion(file, cmd, arg); + case VCHIQ_IOC_DEQUEUE_MESSAGE32: + return vchiq_compat_ioctl_dequeue_message(file, cmd, arg); + case VCHIQ_IOC_GET_CONFIG32: + return vchiq_compat_ioctl_get_config(file, cmd, arg); + case VCHIQ_IOC_DUMP_PHYS_MEM32: + return vchiq_compat_ioctl_dump_phys_mem(file, cmd, arg); + default: + return vchiq_ioctl(file, cmd, arg); + } +} + +#endif + /**************************************************************************** * * vchiq_open @@ -1687,6 +2130,9 @@ static const struct file_operations vchiq_fops = { .owner = THIS_MODULE, .unlocked_ioctl = vchiq_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = vchiq_compat_ioctl, +#endif .open = vchiq_open, .release = vchiq_release, .read = vchiq_read -- 2.11.0 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel