From a57cc2c988482010061b9e68344fdf1969889763 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 12 Jan 2016 14:06:54 -0800 Subject: initial commit, FreeRTOS_BSP_1.0.0_iMX7D --- middleware/multicore/open-amp/common/hil/hil.c | 406 +++++++++++ middleware/multicore/open-amp/common/hil/hil.h | 494 +++++++++++++ middleware/multicore/open-amp/common/llist/llist.c | 100 +++ middleware/multicore/open-amp/common/llist/llist.h | 59 ++ middleware/multicore/open-amp/common/shm/sh_mem.c | 230 ++++++ middleware/multicore/open-amp/common/shm/sh_mem.h | 89 +++ middleware/multicore/open-amp/docs/openamp_gs.pdf | Bin 0 -> 194581 bytes middleware/multicore/open-amp/docs/openamp_ref.pdf | Bin 0 -> 1128759 bytes .../multicore/open-amp/porting/config/config.c | 48 ++ .../multicore/open-amp/porting/config/config.h | 51 ++ middleware/multicore/open-amp/porting/env/env.h | 431 +++++++++++ .../multicore/open-amp/porting/env/freertos_env.c | 461 ++++++++++++ .../open-amp/porting/imx7d_m4/plat_porting.h | 50 ++ .../multicore/open-amp/porting/imx7d_m4/platform.c | 115 +++ .../multicore/open-amp/porting/imx7d_m4/platform.h | 41 ++ .../open-amp/porting/imx7d_m4/platform_info.c | 245 +++++++ .../multicore/open-amp/rpmsg/remote_device.c | 534 ++++++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg.c | 424 +++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg.h | 411 +++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg_core.c | 796 +++++++++++++++++++++ middleware/multicore/open-amp/rpmsg/rpmsg_core.h | 190 +++++ middleware/multicore/open-amp/virtio/virtio.c | 96 +++ middleware/multicore/open-amp/virtio/virtio.h | 151 ++++ middleware/multicore/open-amp/virtio/virtio_ring.h | 165 +++++ middleware/multicore/open-amp/virtio/virtqueue.c | 693 ++++++++++++++++++ middleware/multicore/open-amp/virtio/virtqueue.h | 227 ++++++ 26 files changed, 6507 insertions(+) create mode 100644 middleware/multicore/open-amp/common/hil/hil.c create mode 100644 middleware/multicore/open-amp/common/hil/hil.h create mode 100644 middleware/multicore/open-amp/common/llist/llist.c create mode 100644 middleware/multicore/open-amp/common/llist/llist.h create mode 100644 middleware/multicore/open-amp/common/shm/sh_mem.c create mode 100644 middleware/multicore/open-amp/common/shm/sh_mem.h create mode 100644 middleware/multicore/open-amp/docs/openamp_gs.pdf create mode 100644 middleware/multicore/open-amp/docs/openamp_ref.pdf create mode 100644 middleware/multicore/open-amp/porting/config/config.c create mode 100644 middleware/multicore/open-amp/porting/config/config.h create mode 100644 middleware/multicore/open-amp/porting/env/env.h create mode 100644 middleware/multicore/open-amp/porting/env/freertos_env.c create mode 100644 middleware/multicore/open-amp/porting/imx7d_m4/plat_porting.h create mode 100644 middleware/multicore/open-amp/porting/imx7d_m4/platform.c create mode 100644 middleware/multicore/open-amp/porting/imx7d_m4/platform.h create mode 100644 middleware/multicore/open-amp/porting/imx7d_m4/platform_info.c create mode 100644 middleware/multicore/open-amp/rpmsg/remote_device.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg.h create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg_core.c create mode 100644 middleware/multicore/open-amp/rpmsg/rpmsg_core.h create mode 100644 middleware/multicore/open-amp/virtio/virtio.c create mode 100644 middleware/multicore/open-amp/virtio/virtio.h create mode 100644 middleware/multicore/open-amp/virtio/virtio_ring.h create mode 100644 middleware/multicore/open-amp/virtio/virtqueue.c create mode 100644 middleware/multicore/open-amp/virtio/virtqueue.h (limited to 'middleware/multicore/open-amp') diff --git a/middleware/multicore/open-amp/common/hil/hil.c b/middleware/multicore/open-amp/common/hil/hil.c new file mode 100644 index 0000000..e4b0bf3 --- /dev/null +++ b/middleware/multicore/open-amp/common/hil/hil.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * hil.c + * + * COMPONENT + * + * OpenAMP Stack. + * + * DESCRIPTION + * + * This file is implementation of generic part of HIL. + * + * + * + **************************************************************************/ + +#include "hil.h" + +/*--------------------------- Globals ---------------------------------- */ +struct hil_proc_list procs; + +#if defined (OPENAMP_BENCHMARK_ENABLE) + +unsigned long long boot_time_stamp; +unsigned long long shutdown_time_stamp; + +#endif + +extern int platform_get_processor_info(struct hil_proc *proc, int cpu_id); +extern int platform_get_processor_for_fw(char *fw_name); + +/** + * hil_create_proc + * + * This function creates a HIL proc instance for given CPU id and populates + * it with platform info. + * + * @param cpu_id - cpu id + * + * @return - pointer to proc instance + * + */ +struct hil_proc *hil_create_proc(int cpu_id) { + struct hil_proc *proc = NULL; + struct llist *node = NULL; + struct llist *proc_hd = procs.proc_list; + int status; + + /* If proc already exists then return it */ + while (proc_hd != NULL) { + proc = (struct hil_proc *) proc_hd->data; + if (proc->cpu_id == cpu_id) { + return proc; + } + proc_hd = proc_hd->next; + } + + /* Allocate memory for proc instance */ + proc = env_allocate_memory(sizeof(struct hil_proc)); + if (!proc) { + return NULL; + } + + /* Get HW specfic info */ + status = platform_get_processor_info(proc, cpu_id); + if (status) { + env_free_memory(proc); + return NULL; + } + + /* Enable mapping for the shared memory region */ + env_map_memory((unsigned int) proc->sh_buff.start_addr, + (unsigned int) proc->sh_buff.start_addr, proc->sh_buff.size, + (SHARED_MEM | UNCACHED)); + + /* Put the new proc in the procs list */ + node = env_allocate_memory(sizeof(struct llist)); + + if (!node) { + env_free_memory(proc); + return NULL; + } + + node->data = proc; + add_to_list(&procs.proc_list, node); + + return proc; +} + +/** + * hil_get_cpuforfw + * + * This function provides the CPU ID for the given firmware. + * + * @param fw_name - name of firmware + * + * @return - cpu id + * + */ +int hil_get_cpuforfw(char *fw_name) { + return (platform_get_processor_for_fw(fw_name)); +} + +/** + * hil_delete_proc + * + * This function deletes the given proc instance and frees the + * associated resources. + * + * @param proc - pointer to hil remote_proc instance + * + */ +void hil_delete_proc(struct hil_proc *proc) { + struct llist *proc_hd = NULL; + + if (!proc) + return; + + proc_hd = procs.proc_list; + + while (proc_hd != NULL) { + if (proc_hd->data == proc) { + remove_from_list(&procs.proc_list, proc_hd); + env_free_memory(proc_hd); + break; + } + proc_hd = proc_hd->next; + } + + env_free_memory(proc); +} + + +/** + * hil_isr() + * + * This function is called when interrupt is received for the vring. + * This function gets the corresponding virtqueue and generates + * call back for it. + * + * @param vring_hw - pointer to vring control block + * + */ +void hil_isr(struct proc_vring *vring_hw){ + virtqueue_notification(vring_hw->vq); +} + +/** + * hil_get_proc + * + * This function finds the proc instance based on the given ID + * from the proc list and returns it to user. + * + * @param cpu_id - cpu id + * + * @return - pointer to hil proc instance + * + */ +struct hil_proc *hil_get_proc(int cpu_id) { + struct llist *proc_hd = procs.proc_list; + + if (!proc_hd) + return NULL; + + while (proc_hd != NULL) { + struct hil_proc *proc = (struct hil_proc *) proc_hd->data; + if (proc->cpu_id == cpu_id) { + return proc; + } + proc_hd = proc_hd->next; + } + + return NULL; +} + +/** + * hil_get_chnl_info + * + * This function returns channels info for given proc. + * + * @param proc - pointer to proc info struct + * @param num_chnls - pointer to integer variable to hold + * number of available channels + * + * @return - pointer to channel info control block + * + */ +struct proc_chnl *hil_get_chnl_info(struct hil_proc *proc, int *num_chnls) { + *num_chnls = proc->num_chnls; + return (proc->chnls); +} + +/** + * hil_get_vdev_info + * + * This function return virtio device for remote core. + * + * @param proc - pointer to remote proc + * + * @return - pointer to virtio HW device. + * + */ + +struct proc_vdev *hil_get_vdev_info(struct hil_proc *proc) { + return (&proc->vdev); + +} + +/** + * hil_get_vring_info + * + * This function returns vring_info_table. The caller will use + * this table to get the vring HW info which will be subsequently + * used to create virtqueues. + * + * @param vdev - pointer to virtio HW device + * @param num_vrings - pointer to hold number of vrings + * + * @return - pointer to vring hardware info table + */ +struct proc_vring *hil_get_vring_info(struct proc_vdev *vdev, int *num_vrings) { + + *num_vrings = vdev->num_vrings; + return (vdev->vring_info); + +} + +/** + * hil_get_shm_info + * + * This function returns shared memory info control block. The caller + * will use this information to create and manage memory buffers for + * vring descriptor table. + * + * @param proc - pointer to proc instance + * + * @return - pointer to shared memory region used for buffers + * + */ +struct proc_shm *hil_get_shm_info(struct hil_proc *proc) { + return (&proc->sh_buff); +} + +/** + * hil_enable_vring_notifications() + * + * This function is called after successful creation of virtqueues. + * This function saves queue handle in the vring_info_table which + * will be used during interrupt handling .This function setups + * interrupt handlers. + * + * @param vring_index - index to vring HW table + * @param vq - pointer to virtqueue to save in vring HW table + * + * @return - execution status + */ +int hil_enable_vring_notifications(int vring_index, struct virtqueue *vq) { + struct hil_proc *proc_hw = (struct hil_proc *) vq->vq_dev->device; + struct proc_vring *vring_hw = &proc_hw->vdev.vring_info[vring_index]; + /* Save virtqueue pointer for later reference */ + vring_hw->vq = vq; + + if (proc_hw->ops->enable_interrupt) { + proc_hw->ops->enable_interrupt(vring_hw); /*_enable_interrupt*/ + } + + return 0; +} + +/** + * hil_vring_notify() + * + * This function generates IPI to let the other side know that there is + * job available for it. The required information to achieve this, like interrupt + * vector, CPU id etc is be obtained from the proc_vring table. + * + * @param vq - pointer to virtqueue + * + */ +void hil_vring_notify(struct virtqueue *vq) { + struct hil_proc *proc_hw = (struct hil_proc *) vq->vq_dev->device; + struct proc_vring *vring_hw = &proc_hw->vdev.vring_info[vq->vq_queue_index]; + + if (proc_hw->ops->notify) { + proc_hw->ops->notify(proc_hw->cpu_id, &vring_hw->intr_info); /*_notify*/ + } +} + +/** + * hil_get_status + * + * This function is used to check if the given core is up and running. + * This call will return after it is confirmed that remote core has + * started. + * + * @param proc - pointer to proc instance + * + * @return - execution status + */ +int hil_get_status(struct hil_proc *proc) { + /* For future use only.*/ + return 0; +} + +/** + * hil_set_status + * + * This function is used to update the status + * of the given core i.e it is ready for IPC. + * + * @param proc - pointer to remote proc + * + * @return - execution status + */ +int hil_set_status(struct hil_proc *proc) { + /* For future use only.*/ + return 0; +} + +/** + * hil_boot_cpu + * + * This function boots the remote processor. + * + * @param proc - pointer to remote proc + * @param start_addr - start address of remote cpu + * + * @return - execution status + */ +int hil_boot_cpu(struct hil_proc *proc, unsigned int start_addr) { + + if (proc->ops->boot_cpu) { + proc->ops->boot_cpu(proc->cpu_id, start_addr); + } + +#if defined (OPENAMP_BENCHMARK_ENABLE) + boot_time_stamp = env_get_timestamp(); +#endif + + return 0; +} + +/** + * hil_shutdown_cpu + * + * This function shutdowns the remote processor + * + * @param proc - pointer to remote proc + * + */ +void hil_shutdown_cpu(struct hil_proc *proc) { + if (proc->ops->shutdown_cpu) { + proc->ops->shutdown_cpu(proc->cpu_id); + } + +#if defined (OPENAMP_BENCHMARK_ENABLE) + shutdown_time_stamp = env_get_timestamp(); +#endif +} + +/** + * hil_get_firmware + * + * This function returns address and size of given firmware name passed as + * parameter. + * + * @param fw_name - name of the firmware + * @param start_addr - pointer t hold start address of firmware + * @param size - pointer to hold size of firmware + * + * returns - status of function execution + * + */ +int hil_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size){ + return (config_get_firmware(fw_name , start_addr, size)); +} diff --git a/middleware/multicore/open-amp/common/hil/hil.h b/middleware/multicore/open-amp/common/hil/hil.h new file mode 100644 index 0000000..0f5ce2d --- /dev/null +++ b/middleware/multicore/open-amp/common/hil/hil.h @@ -0,0 +1,494 @@ +#ifndef _HIL_H_ +#define _HIL_H_ + +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * hil.h + * + * DESCRIPTION + * + * This file defines interface layer to access hardware features. This + * interface is used by both RPMSG and remoteproc components. + * + ***************************************************************************/ + +#include "../../virtio/virtio.h" +#include "../../porting/config/config.h" + +/* Configurable parameters */ +#define HIL_MAX_CORES 2 +#define HIL_MAX_NUM_VRINGS 2 +#define HIL_MAX_NUM_CHANNELS 1 +/* Reserved CPU id */ +#define HIL_RSVD_CPU_ID 0xffffffff + +/** + * struct proc_shm + * + * This structure is maintained by hardware interface layer for + * shared memory information. The shared memory provides buffers + * for use by the vring to exchange messages between the cores. + * + */ +struct proc_shm +{ + /* Start address of shared memory used for buffers. */ + void *start_addr; + /* Size of shared memory. */ + unsigned long size; + /* Attributes for shared memory - cached or uncached. */ + unsigned long flags; +}; + +/** +* struct proc_intr +* +* This structure is maintained by hardware interface layer for +* notification(interrupts) mechanism. The most common notification mechanism +* is Inter-Processor Interrupt(IPI). There can be other mechanism depending +* on SoC architecture. +* +*/ +struct proc_intr +{ + /* Interrupt number for vring - use for IPI */ + unsigned int vect_id; + /* Interrupt priority */ + unsigned int priority; + /* Interrupt trigger type */ + unsigned int trigger_type; + /* Private data */ + void *data; +}; + +/** +* struct proc_vring +* +* This structure is maintained by hardware interface layer to keep +* vring physical memory and notification info. +* +*/ +struct proc_vring +{ + /* Pointer to virtqueue encapsulating the vring */ + struct virtqueue *vq; + /* Vring physical address */ + void *phy_addr; + /* Number of vring descriptors */ + unsigned short num_descs; + /* Vring alignment*/ + unsigned long align; + /* Vring interrupt control block */ + struct proc_intr intr_info; +}; + +/** + * struct proc_vdev + * + * This structure represents a virtio HW device for remote processor. + * Currently only one virtio device per processor is supported. + * + */ +struct proc_vdev +{ + /* Number of vrings*/ + unsigned int num_vrings; + /* Virtio device features */ + unsigned int dfeatures; + /* Virtio gen features */ + unsigned int gfeatures; + /* Vring info control blocks */ + struct proc_vring vring_info[HIL_MAX_NUM_VRINGS]; +}; + +/** + * struct proc_chnl + * + * This structure represents channel IDs that would be used by + * the remote in the name service message. This will be extended + * further to support static channel creation. + * + */ +struct proc_chnl +{ + /* Channel ID */ + char name[32]; +}; + +/** +* struct hil_proc +* +* This structure represents a remote processor and encapsulates shared +* memory and notification info required for IPC. +* +*/ +struct hil_proc +{ + /* CPU ID as defined by the platform */ + unsigned long cpu_id; + /* Shared memory info */ + struct proc_shm sh_buff; + /* Virtio device hardware info */ + struct proc_vdev vdev; + /* Number of RPMSG channels */ + unsigned long num_chnls; + /* RPMsg channels array */ + struct proc_chnl chnls[HIL_MAX_NUM_CHANNELS]; + /* HIL platform ops table */ + struct hil_platform_ops *ops; + /* Attrbites to represent processor role, master or remote . This field is for + * future use. */ + unsigned long attr; + /* + * CPU bitmask - shared variable updated by each core + * after it has been initialized. This field is for future use. + */ + unsigned long cpu_bitmask; + /* Spin lock - This field is for future use. */ + volatile unsigned int *slock; +}; + +/** + * struct hil_proc_list + * + * This structure serves as lists for cores present in the system. + * It provides entry point to access remote core parameters. + * + */ +struct hil_proc_list { + struct llist *proc_list; +}; + +/** + * hil_create_proc + * + * This function creates a HIL proc instance for given CPU id and populates + * it with platform info. + * + * @param cpu_id - cpu id + * + * @return - pointer to proc instance + * + */ +struct hil_proc *hil_create_proc(int cpu_id); + +/** + * hil_delete_proc + * + * This function deletes the given proc instance and frees the + * associated resources. + * + * @param proc - pointer to HIL proc instance + * + */ +void hil_delete_proc(struct hil_proc *proc); + +/** + * hil_get_proc + * + * This function finds the proc instance based on the given ID + * from the proc list and returns it to user. + * + * @param cpu_id - cpu id + * + * @return - pointer to proc instance + * + */ +struct hil_proc *hil_get_proc(int cpu_id); + +/** + * hil_isr() + * + * This function is called when interrupt is received for the vring. + * This function gets the corresponding virtqueue and generates + * call back for it. + * + * @param vring_hw - pointer to vring control block + * + */ +void hil_isr(struct proc_vring *vring_hw); + +/** + * hil_get_cpuforfw + * + * This function provides the CPU ID for the given firmware. + * + * @param fw_name - name of firmware + * + * @return - cpu id + * + */ +int hil_get_cpuforfw(char *fw_name); + +/** + * hil_get_vdev_info + * + * This function return virtio device for remote core. + * + * @param proc - pointer to remote proc + * + * @return - pointer to virtio HW device. + * + */ +struct proc_vdev *hil_get_vdev_info(struct hil_proc *proc); + +/** + * hil_get_chnl_info + * + * This function returns channels info for given proc. + * + * @param proc - pointer to proc info struct + * @param num_chnls - pointer to integer variable to hold + * number of available channels + * + * @return - pointer to channel info control block + * + */ +struct proc_chnl *hil_get_chnl_info(struct hil_proc *proc , int *num_chnls); + +/** + * hil_get_vring_info + * + * This function returns vring_info_table. The caller will use + * this table to get the vring HW info which will be subsequently + * used to create virtqueues. + * + * @param vdev - pointer to virtio HW device + * @param num_vrings - pointer to hold number of vrings + * + * @return - pointer to vring hardware info table + */ +struct proc_vring *hil_get_vring_info(struct proc_vdev *vdev, int *num_vrings); + +/** + * hil_get_shm_info + * + * This function returns shared memory info control block. The caller + * will use this information to create and manage memory buffers for + * vring descriptor table. + * + * @param proc - pointer to proc instance + * + * @return - pointer to shared memory region used for buffers + * + */ +struct proc_shm *hil_get_shm_info(struct hil_proc *proc); + +/** + * hil_enable_vring_notifications() + * + * This function is called after successful creation of virtqueues. + * This function saves queue handle in the vring_info_table which + * will be used during interrupt handling .This function setups + * interrupt handlers. + * + * @param vring_index - index to vring HW table + * @param vq - pointer to virtqueue to save in vring HW table + * + * @return - execution status + */ +int hil_enable_vring_notifications(int vring_index, struct virtqueue *vq); + +/** + * hil_vring_notify() + * + * This function generates IPI to let the other side know that there is + * job available for it. The required information to achieve this, like interrupt + * vector, CPU id etc is be obtained from the proc_vring table. + * + * @param vq - pointer to virtqueue + * + */ +void hil_vring_notify(struct virtqueue *vq); + +/** + * hil_get_status + * + * This function is used to check if the given core is up and running. + * This call will return after it is confirmed that remote core has + * started. + * + * @param proc - pointer to proc instance + * + * @return - execution status + */ +int hil_get_status(struct hil_proc *proc); + +/** + * hil_set_status + * + * This function is used to update the status + * of the given core i.e it is ready for IPC. + * + * @param proc - pointer to remote proc + * + * @return - execution status + */ + +int hil_set_status(struct hil_proc *proc); + +/** + * hil_boot_cpu + * + * This function starts remote processor at given address. + * + * @param proc - pointer to remote proc + * @param load_addr - load address of remote firmware + * + * @return - execution status + */ +int hil_boot_cpu(struct hil_proc *proc , unsigned int load_addr); + +/** + * hil_shutdown_cpu + * + * This function shutdowns the remote processor + * + * @param proc - pointer to remote proc + * + */ +void hil_shutdown_cpu(struct hil_proc *proc); + +/** + * hil_get_firmware + * + * This function returns address and size of given firmware name passed as + * parameter. + * + * @param fw_name - name of the firmware + * @param start_addr - pointer t hold start address of firmware + * @param size - pointer to hold size of firmware + * + * returns - status of function execution + * + */ +int hil_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size); + +/** + * + * This structure is an interface between HIL and platform porting + * component. It is required for the user to provide definitions of + * these functions when framework is ported to new hardware platform. + * + */ +struct hil_platform_ops +{ + /** + * enable_interrupt() + * + * This function enables interrupt(IPI) for given vring. + * + * @param vring_hw - pointer to vring control block + * + * @return - execution status + */ + int (*enable_interrupt)(struct proc_vring *vring_hw); + + /** + * reg_ipi_after_deinit() + * This function register interrupt(IPI) after openamp resource . + * + * @param vring_hw - pointer to vring control block + */ + void (*reg_ipi_after_deinit)(struct proc_vring *vring_hw); + + /** + * notify() + * + * This function generates IPI to let the other side know that there is + * job available for it. + * + * @param cpu_id - ID of CPU which is to be notified + * @param intr_info - pointer to interrupt info control block + */ + void (*notify)(int cpu_id , struct proc_intr *intr_info); + + /** + * get_status + * + * This function is used to check if the given core is + * up and running. This call will return after it is confirmed + * that remote core is initialized. + * + * @param cpu_id - ID of CPU for which status is requested. + * + * @return - execution status + */ + int (*get_status)(int cpu_id); + + /** + * set_status + * + * This function is used to update the status + * of the given core i.e it is ready for IPC. + * + * @param cpu_id - ID of CPU for which status is to be set + * + * @return - execution status + */ + + int (*set_status)(int cpu_id); + + /** + * boot_cpu + * + * This function boots the remote processor. + * + * @param cpu_id - ID of CPU to boot + * @param start_addr - start address of remote cpu + * + * @return - execution status + */ + int (*boot_cpu)(int cpu_id , unsigned int start_addr); + + /** + * shutdown_cpu + * + * This function shutdowns the remote processor. + * + * @param cpu_id - ID of CPU to shutdown + * + */ + void (*shutdown_cpu)(int cpu_id); + +}; + +/* Utility macros for register read/write */ +#define HIL_MEM_READ8(addr) *(volatile unsigned char *)(addr) +#define HIL_MEM_READ16(addr) *(volatile unsigned short *)(addr) +#define HIL_MEM_READ32(addr) *(volatile unsigned long *)(addr) +#define HIL_MEM_WRITE8(addr,data) *(volatile unsigned char *)(addr) = (unsigned char)(data) +#define HIL_MEM_WRITE16(addr,data) *(volatile unsigned short *)(addr) = (unsigned short)(data) +#define HIL_MEM_WRITE32(addr,data) *(volatile unsigned long *)(addr) = (unsigned long)(data) + +#endif /* _HIL_H_ */ diff --git a/middleware/multicore/open-amp/common/llist/llist.c b/middleware/multicore/open-amp/common/llist/llist.c new file mode 100644 index 0000000..37e888c --- /dev/null +++ b/middleware/multicore/open-amp/common/llist/llist.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * llist.c + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * Source file for basic linked list service. + * + **************************************************************************/ +#include "llist.h" + +#define LIST_NULL ((void *)0) +/** + * add_to_list + * + * Places new element at the start of the list. + * + * @param head - list head + * @param node - new element to add + * + */ +void add_to_list(struct llist **head, struct llist *node) { + + if (!node) + return; + + if (*head) { + /* Place the new element at the start of list. */ + node->next = *head; + node->prev = LIST_NULL; + (*head)->prev = node; + *head = node; + } else { + /* List is empty - assign new element to list head. */ + *head = node; + (*head)->next = LIST_NULL; + (*head)->prev = LIST_NULL; + } +} + +/** + * remove_from_list + * + * Removes the given element from the list. + * + * @param head - list head + * @param element - element to remove from list + * + */ +void remove_from_list(struct llist **head, struct llist *node) { + + if (!(*head) || !(node)) + return; + + if (node == *head) { + /* First element has to be removed. */ + *head = (*head)->next; + } else if (node->next == LIST_NULL) { + /* Last element has to be removed. */ + node->prev->next = node->next; + } else { + /* Intermediate element has to be removed. */ + node->prev->next = node->next; + node->next->prev = node->prev; + } +} diff --git a/middleware/multicore/open-amp/common/llist/llist.h b/middleware/multicore/open-amp/common/llist/llist.h new file mode 100644 index 0000000..004f4e0 --- /dev/null +++ b/middleware/multicore/open-amp/common/llist/llist.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/************************************************************************** + * FILE NAME + * + * llist.h + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * Header file for linked list service. + * + **************************************************************************/ + +#ifndef LLIST_H_ +#define LLIST_H_ + +struct llist { + void *data; + unsigned int attr; + struct llist *next; + struct llist *prev; +}; + +void add_to_list(struct llist **head, struct llist *node); +void remove_from_list(struct llist **head, struct llist *node); + +#endif /* LLIST_H_ */ diff --git a/middleware/multicore/open-amp/common/shm/sh_mem.c b/middleware/multicore/open-amp/common/shm/sh_mem.c new file mode 100644 index 0000000..6a290a4 --- /dev/null +++ b/middleware/multicore/open-amp/common/shm/sh_mem.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/************************************************************************** + * FILE NAME + * + * sh_mem.c + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * Source file for fixed buffer size memory management service. Currently + * it is only being used to manage shared memory. + * + **************************************************************************/ +#include "sh_mem.h" + +/** + * sh_mem_create_pool + * + * Creates new memory pool with the given parameters. + * + * @param start_addr - start address of the memory region + * @param size - size of the memory + * @param buff_size - fixed buffer size + * + * @return - pointer to memory pool + * + */ +struct sh_mem_pool * sh_mem_create_pool(void *start_addr, unsigned int size, + unsigned int buff_size) { + struct sh_mem_pool *mem_pool; + int status, pool_size; + int num_buffs, bmp_size; + + if (!start_addr || !size || !buff_size) + return NULL; + + /* Word align the buffer size */ + buff_size = WORD_ALIGN(buff_size); + + /* Get number of buffers. */ + num_buffs = (size / buff_size) + ((size % buff_size) == 0 ? 0 : 1); + + /* + * Size of the bitmap required to maintain buffers info. One word(32 bit) can + * keep track of 32 buffers. + */ + bmp_size = (num_buffs / BITMAP_WORD_SIZE) + + ((num_buffs % BITMAP_WORD_SIZE) == 0 ? 0 : 1); + + /* Total size required for pool control block. */ + pool_size = sizeof(struct sh_mem_pool) + WORD_SIZE * bmp_size; + + /* Create pool control block. */ + mem_pool = env_allocate_memory(pool_size); + + if (mem_pool) { + /* Initialize pool parameters */ + env_memset(mem_pool, 0x00, pool_size); + status = env_create_mutex(&mem_pool->lock , 1); + if (status){ + env_free_memory(mem_pool); + return NULL; + } + mem_pool->start_addr = start_addr; + mem_pool->buff_size = buff_size; + mem_pool->bmp_size = bmp_size; + mem_pool->total_buffs = num_buffs; + } + + return mem_pool; +} + +/** + * sh_mem_get_buffer + * + * Allocates fixed size buffer from the given memory pool. + * + * @param pool - pointer to memory pool + * + * @return - pointer to allocated buffer + * + */ +void * sh_mem_get_buffer(struct sh_mem_pool *pool) { + void *buff = NULL; + int idx, bit_idx; + + if (!pool) + return NULL; + + env_lock_mutex(pool->lock); + + if (pool->used_buffs >= pool->total_buffs) { + env_unlock_mutex(pool->lock); + return NULL; + } + + for (idx = 0; idx < pool->bmp_size; idx++) { + /* + * Find the first 0 bit in the buffers bitmap. The 0th bit + * represents a free buffer. + */ + bit_idx = get_first_zero_bit(pool->bitmap[idx]); + if (bit_idx < 32) { + /* Set bit to mark it as consumed. */ + pool->bitmap[idx] |= (1 << bit_idx); + buff = (char *) pool->start_addr + + pool->buff_size * (idx * BITMAP_WORD_SIZE + bit_idx); + pool->used_buffs++; + break; + } + } + + env_unlock_mutex(pool->lock); + + return buff; +} + +/** + * sh_mem_free_buffer + * + * Frees the given buffer. + * + * @param pool - pointer to memory pool + * @param buff - pointer to buffer + * + * @return - none + */ +void sh_mem_free_buffer(void *buff, struct sh_mem_pool *pool) { + unsigned long *bitmask; + int bmp_idx, bit_idx, buff_idx; + + if (!pool || !buff) + return; + + /* Acquire the pool lock */ + env_lock_mutex(pool->lock); + + /* Map the buffer address to its index. */ + buff_idx = ((char *) buff - (char*) pool->start_addr) / pool->buff_size; + + /* Translate the buffer index to bitmap index. */ + bmp_idx = buff_idx / BITMAP_WORD_SIZE; + bit_idx = buff_idx % BITMAP_WORD_SIZE; + bitmask = &pool->bitmap[bmp_idx]; + + /* Mark the buffer as free */ + *bitmask ^= (1 << bit_idx); + + pool->used_buffs--; + + /* Release the pool lock. */ + env_unlock_mutex(pool->lock); + +} + +/** + * sh_mem_delete_pool + * + * Deletes the given memory pool. + * + * @param pool - pointer to memory pool + * + * @return - none + */ +void sh_mem_delete_pool(struct sh_mem_pool *pool) { + + if (pool) { + env_delete_mutex(pool->lock); + env_free_memory(pool); + } +} + +/** + * get_first_zero_bit + * + * Provides position of first 0 bit in a 32 bit value + * + * @param value - given value + * + * @return - 0th bit position + */ +unsigned int get_first_zero_bit(unsigned long value) { + unsigned int idx; + unsigned int tmp32; + + /* Invert value */ + value = ~value; + + /* (~value) & (2's complement of value) */ + value = (value & (-value)) - 1; + + /* log2(value) */ + + tmp32 = value - ((value >> 1) & 033333333333) + - ((value >> 2) & 011111111111); + + idx = ((tmp32 + (tmp32 >> 3)) & 030707070707) % 63; + + return idx; +} diff --git a/middleware/multicore/open-amp/common/shm/sh_mem.h b/middleware/multicore/open-amp/common/shm/sh_mem.h new file mode 100644 index 0000000..4ba830b --- /dev/null +++ b/middleware/multicore/open-amp/common/shm/sh_mem.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * sh_mem.c + * + * COMPONENT + * + * IPC Stack for uAMP systems. + * + * DESCRIPTION + * + * Header file for fixed buffer size memory management service. Currently + * it is being used to manage shared memory. + * + **************************************************************************/ +#ifndef SH_MEM_H_ +#define SH_MEM_H_ + +#include "../../porting/env/env.h" + + +/* Macros */ +#define BITMAP_WORD_SIZE 32 +#define WORD_SIZE sizeof(unsigned long) +#define WORD_ALIGN(a) (((a) & (WORD_SIZE-1)) != 0)? \ + (((a) & (~(WORD_SIZE-1))) + 4):(a) +/* + * This structure represents a shared memory pool. + * + * @start_addr - start address of shared memory region + * @lock - lock to ensure exclusive access + * @size - size of shared memory* + * @buff_size - size of each buffer + * @total_buffs - total number of buffers in shared memory region + * @used_buffs - number of used buffers + * @bmp_size - size of bitmap array + * @bitmap - array to keep record of free and used blocks + * + */ + +struct sh_mem_pool { + void *start_addr; + LOCK *lock; + int size; + int buff_size; + int total_buffs; + int used_buffs; + int bmp_size; + unsigned long bitmap[0]; +}; + +/* APIs */ +struct sh_mem_pool *sh_mem_create_pool(void *start_addr, unsigned int size, + unsigned int buff_size); +void sh_mem_delete_pool(struct sh_mem_pool *pool); +void *sh_mem_get_buffer(struct sh_mem_pool *pool); +void sh_mem_free_buffer(void *ptr, struct sh_mem_pool *pool); +unsigned int get_first_zero_bit(unsigned long value); + +#endif /* SH_MEM_H_ */ diff --git a/middleware/multicore/open-amp/docs/openamp_gs.pdf b/middleware/multicore/open-amp/docs/openamp_gs.pdf new file mode 100644 index 0000000..704e566 Binary files /dev/null and b/middleware/multicore/open-amp/docs/openamp_gs.pdf differ diff --git a/middleware/multicore/open-amp/docs/openamp_ref.pdf b/middleware/multicore/open-amp/docs/openamp_ref.pdf new file mode 100644 index 0000000..eb94849 Binary files /dev/null and b/middleware/multicore/open-amp/docs/openamp_ref.pdf differ diff --git a/middleware/multicore/open-amp/porting/config/config.c b/middleware/multicore/open-amp/porting/config/config.c new file mode 100644 index 0000000..fe29d72 --- /dev/null +++ b/middleware/multicore/open-amp/porting/config/config.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * config.c + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * + **************************************************************************/ + +#include "config.h" + +int config_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size) { + return -1; +} diff --git a/middleware/multicore/open-amp/porting/config/config.h b/middleware/multicore/open-amp/porting/config/config.h new file mode 100644 index 0000000..23bc36f --- /dev/null +++ b/middleware/multicore/open-amp/porting/config/config.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include "../env/env.h" + +/* Max supprted ISR counts */ +#define ISR_COUNT 2 + +/** + * Structure to keep track of registered ISR's. + */ +struct isr_info { + int vector; + int priority; + int type; + void *data; + void (*isr)(int vector, void *data); +}; + +int config_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size); + +#endif diff --git a/middleware/multicore/open-amp/porting/env/env.h b/middleware/multicore/open-amp/porting/env/env.h new file mode 100644 index 0000000..05b9e62 --- /dev/null +++ b/middleware/multicore/open-amp/porting/env/env.h @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /************************************************************************** + * FILE NAME + * + * env.h + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * This file defines abstraction layer for OpenAMP stack. The implementor + * must provide definition of all the functions. + * + * DATA STRUCTURES + * + * none + * + * FUNCTIONS + * + * env_allocate_memory + * env_free_memory + * env_memset + * env_memcpy + * env_strlen + * env_strcpy + * env_strncpy + * env_print + * env_map_vatopa + * env_map_patova + * env_mb + * env_rmb + * env_wmb + * env_create_mutex + * env_delete_mutex + * env_lock_mutex + * env_unlock_mutex + * env_sleep_msec + * env_disable_interrupts + * env_restore_interrupts + * + **************************************************************************/ +#ifndef _ENV_H_ +#define _ENV_H_ + +#include +#include +#include "device_imx.h" +#include "debug_console_imx.h" +/** + * env_init + * + * Initializes OS/BM environment. + * + * @returns - execution status + */ + +int env_init(void); + +/** + * env_deinit + * + * Uninitializes OS/BM environment. + * + * @returns - execution status + */ + +int env_deinit(void); +/** + * ------------------------------------------------------------------------- + * + * Dynamic memory management functions. The parameters + * are similar to standard c functions. + * + *------------------------------------------------------------------------- + **/ + +/** + * env_allocate_memory + * + * Allocates memory with the given size. + * + * @param size - size of memory to allocate + * + * @return - pointer to allocated memory + */ +void *env_allocate_memory(unsigned int size); + +/** + * env_free_memory + * + * Frees memory pointed by the given parameter. + * + * @param ptr - pointer to memory to free + */ +void env_free_memory(void *ptr); + +/** + * ------------------------------------------------------------------------- + * + * RTL Functions + * + *------------------------------------------------------------------------- + */ + +void env_memset(void *, int, unsigned long); +void env_memcpy(void *, void const *, unsigned long); +size_t env_strlen(const char *); +void env_strcpy(char *, const char *); +int env_strcmp(const char *, const char *); +void env_strncpy(char *, const char *, unsigned long); +int env_strncmp(char *, const char *, unsigned long); +#define env_print(...) PRINTF(__VA_ARGS__) + +/** + *----------------------------------------------------------------------------- + * + * Functions to convert physical address to virtual address and vice versa. + * + *----------------------------------------------------------------------------- + */ + +/** + * env_map_vatopa + * + * Converts logical address to physical address + * + * @param address - pointer to logical address + * + * @return - physical address + */ +unsigned long env_map_vatopa(void *address); + +/** + * env_map_patova + * + * Converts physical address to logical address + * + * @param address - pointer to physical address + * + * @return - logical address + * + */ +void *env_map_patova(unsigned long address); + +/** + *----------------------------------------------------------------------------- + * + * Abstractions for memory barrier instructions. + * + *----------------------------------------------------------------------------- + */ + +/** + * env_mb + * + * Inserts memory barrier. + */ + +void env_mb(void); + +/** + * env_rmb + * + * Inserts read memory barrier + */ + +void env_rmb(void); + +/** + * env_wmb + * + * Inserts write memory barrier + */ + +void env_wmb(void); + +/** + *----------------------------------------------------------------------------- + * + * Abstractions for OS lock primitives. + * + *----------------------------------------------------------------------------- + */ + +/** + * env_create_mutex + * + * Creates a mutex with given initial count. + * + * @param lock - pointer to created mutex + * @param count - initial count 0 or 1 + * + * @return - status of function execution + */ +int env_create_mutex(void **lock , int count); + +/** + * env_delete_mutex + * + * Deletes the given lock. + * + * @param lock - mutex to delete + */ + +void env_delete_mutex(void *lock); + +/** + * env_lock_mutex + * + * Tries to acquire the lock, if lock is not available then call to + * this function will suspend. + * + * @param lock - mutex to lock + * + */ + +void env_lock_mutex(void *lock); + +/** + * env_unlock_mutex + * + * Releases the given lock. + * + * @param lock - mutex to unlock + */ + +void env_unlock_mutex(void *lock); + +/** + * env_create_sync_lock + * + * Creates a synchronization lock primitive. It is used + * when signal has to be sent from the interrupt context to main + * thread context. + * + * @param lock - pointer to created sync lock object + * @param state - initial state , lock or unlocked + * + * @returns - status of function execution + */ +#define LOCKED 0 +#define UNLOCKED 1 + +int env_create_sync_lock(void **lock , int state); + +/** + * env_create_sync_lock + * + * Deletes given sync lock object. + * + * @param lock - sync lock to delete. + * + */ + +void env_delete_sync_lock(void *lock); + + +/** + * env_acquire_sync_lock + * + * Tries to acquire the sync lock. + * + * @param lock - sync lock to acquire. + */ +void env_acquire_sync_lock(void *lock); + +/** + * env_release_sync_lock + * + * Releases synchronization lock. + * + * @param lock - sync lock to release. + */ +void env_release_sync_lock(void *lock); + +/** + * env_sleep_msec + * + * Suspends the calling thread for given time in msecs. + * + * @param num_msec - delay in msecs + */ +void env_sleep_msec(int num_msec); + +/** + * env_disable_interrupts + * + * Disables system interrupts + * + */ +void env_disable_interrupts(void); + +/** + * env_restore_interrupts + * + * Enables system interrupts + * + */ +void env_restore_interrupts(void); + +/** + * env_register_isr + * + * Registers interrupt handler for the given interrupt vector. + * + * @param vector - interrupt vector number + * @param data - private data + * @param isr - interrupt handler + */ + +void env_register_isr(int vector, void *data, + void (*isr)(int vector, void *data)); + +void env_update_isr(int vector, void *data, + void (*isr)(int vector, void *data)); + +/** + * env_enable_interrupt + * + * Enables the given interrupt. + * + * @param vector - interrupt vector number + * @param priority - interrupt priority + * @param polarity - interrupt polarity + */ + +void env_enable_interrupt(unsigned int vector, unsigned int priority, + unsigned int polarity); + +/** + * env_disable_interrupt + * + * Disables the given interrupt. + * + * @param vector - interrupt vector number + */ + +void env_disable_interrupt(unsigned int vector); + +/** + * env_map_memory + * + * Enables memory mapping for given memory region. + * + * @param pa - physical address of memory + * @param va - logical address of memory + * @param size - memory size + * param flags - flags for cache/uncached and access type + * + * Currently only first byte of flag parameter is used and bits mapping is defined as follow; + * + * Cache bits + * 0x0000_0001 = No cache + * 0x0000_0010 = Write back + * 0x0000_0100 = Write through + * 0x0000_x000 = Not used + * + * Memory types + * + * 0x0001_xxxx = Memory Mapped + * 0x0010_xxxx = IO Mapped + * 0x0100_xxxx = Shared + * 0x1000_xxxx = TLB + */ + +/* Macros for caching scheme used by the shared memory */ +#define UNCACHED (1 << 0) +#define WB_CACHE (1 << 1) +#define WT_CACHE (1 << 2) + +/* Memory Types */ +#define MEM_MAPPED (1 << 4) +#define IO_MAPPED (1 << 5) +#define SHARED_MEM (1 << 6) +#define TLB_MEM (1 << 7) + +void env_map_memory(unsigned int pa, unsigned int va, unsigned int size, + unsigned int flags); + +/** + * env_get_timestamp + * + * Returns a 64 bit time stamp. + * + * + */ +unsigned long long env_get_timestamp(void); + +/** + * env_disable_cache + * + * Disables system caches. + * + */ + +void env_disable_cache(void); + +typedef void LOCK; + +#endif /* _ENV_H_ */ diff --git a/middleware/multicore/open-amp/porting/env/freertos_env.c b/middleware/multicore/open-amp/porting/env/freertos_env.c new file mode 100644 index 0000000..18c2afc --- /dev/null +++ b/middleware/multicore/open-amp/porting/env/freertos_env.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * Copyright (c) 2015 Freescale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * freertos_env.c + * + * + * DESCRIPTION + * + * This file is Free RTOS Implementation of env layer for OpenAMP. + * + * + **************************************************************************/ + +#include "env.h" +#include "../config/config.h" + +#include +#include + +#if (defined(__CC_ARM)) +#define MEM_BARRIER() __schedule_barrier() +#elif (defined(__GNUC__)) +#define MEM_BARRIER() asm volatile("" ::: "memory") +#else +#define MEM_BARRIER() +#endif + +/* + * function decalaration for platform provided facility + */ +extern void platform_interrupt_enable(void); +extern void platform_interrupt_disable(void); + +/** + * Structure to keep track of registered ISR's. + */ +struct isr_info isr_table[ISR_COUNT]; +int Intr_Count = 0; + +/** + * env_init + * + * Initializes OS/BM environment. + * + */ +int env_init() { + return 0; +} + +/** + * env_deinit + * + * Uninitializes OS/BM environment. + * + * @returns - execution status + */ + +int env_deinit() { + return 0; +} +/** + * env_allocate_memory - implementation + * + * @param size + */ +void *env_allocate_memory(unsigned int size) +{ + return (pvPortMalloc(size)); +} + +/** + * env_free_memory - implementation + * + * @param ptr + */ +void env_free_memory(void *ptr) +{ + if (ptr != NULL) + { + vPortFree(ptr); + } +} + +/** + * + * env_memset - implementation + * + * @param ptr + * @param value + * @param size + */ +void env_memset(void *ptr, int value, unsigned long size) +{ + memset(ptr, value, size); +} + +/** + * + * env_memcpy - implementation + * + * @param dst + * @param src + * @param len + */ +void env_memcpy(void *dst, void const * src, unsigned long len) { + memcpy(dst,src,len); +} + +/** + * + * env_strcmp - implementation + * + * @param dst + * @param src + */ + +int env_strcmp(const char *dst, const char *src){ + return (strcmp(dst, src)); +} + +/** + * + * env_strncpy - implementation + * + * @param dest + * @param src + * @param len + */ +void env_strncpy(char * dest, const char *src, unsigned long len) +{ + strncpy(dest, src, len); +} + +/** + * + * env_strncmp - implementation + * + * @param dest + * @param src + * @param len + */ +int env_strncmp(char * dest, const char *src, unsigned long len) +{ + return (strncmp(dest, src, len)); +} + +/** + * + * env_mb - implementation + * + */ +void env_mb() +{ + MEM_BARRIER(); +} + +/** + * osalr_mb - implementation + */ +void env_rmb() +{ + MEM_BARRIER(); +} + +/** + * env_wmb - implementation + */ +void env_wmb() +{ + MEM_BARRIER(); +} + +/** + * env_map_vatopa - implementation + * + * @param address + */ +unsigned long env_map_vatopa(void *address) +{ + return (unsigned long)address; +} + +/** + * env_map_patova - implementation + * + * @param address + */ +void *env_map_patova(unsigned long address) +{ + return ((void *)address); +} + +/** + * env_create_mutex + * + * Creates a mutex with the given initial count. + * + */ +int env_create_mutex(void **lock, int count) +{ + return 0; +} + +/** + * env_delete_mutex + * + * Deletes the given lock + * + */ +void env_delete_mutex(void *lock) +{ +} + +/** + * env_lock_mutex + * + * Tries to acquire the lock, if lock is not available then call to + * this function will suspend. + * + * system level interrupt is disabled to avoid race condition, this has the same effect of a lock + * + */ +void env_lock_mutex(void *lock) +{ + env_disable_interrupts(); +} + +/** + * env_unlock_mutex + * + * Releases the given lock. + * + * system level interrupt is disabled to avoid race condition, this has the same effect of a lock + * + */ + +void env_unlock_mutex(void *lock) +{ + env_restore_interrupts(); +} + + +/** + * env_create_sync_lock + * + * Creates a synchronization lock primitive. It is used + * when signal has to be sent from the interrupt context to main + * thread context. + */ +int env_create_sync_lock(void **lock , int state) +{ + return 0; +} + +/** + * env_delete_sync_lock + * + * Deletes the given lock + * + */ +void env_delete_sync_lock(void *lock) +{ +} + +/** + * env_acquire_sync_lock + * + * Tries to acquire the lock, if lock is not available then call to + * this function waits for lock to become available. + */ +void env_acquire_sync_lock(void *lock) +{ +} + +/** + * env_release_sync_lock + * + * Releases the given lock. + */ + +void env_release_sync_lock(void *lock) +{ +} + +/** + * env_sleep_msec + * + * Suspends the calling thread for given time , in msecs. + */ + +void env_sleep_msec(int num_msec) +{ + /* portSUPPRESS_TICKS_AND_SLEEP */ + vTaskDelay(num_msec * portTICK_PERIOD_MS); +} + +/** + * env_disable_interrupts + * + * Disables system interrupts + * + */ +void env_disable_interrupts() +{ + taskDISABLE_INTERRUPTS(); +} + +/** + * env_restore_interrupts + * + * Enables system interrupts + * + */ +void env_restore_interrupts() +{ + taskENABLE_INTERRUPTS(); +} + +/** + * env_register_isr + * + * Registers interrupt handler for the given interrupt vector. + * + * @param vector - vring index + * @param isr - interrupt handler + */ +void env_register_isr(int vector , void *data , + void (*isr)(int vector , void *data)) +{ + env_disable_interrupts(); + + if(Intr_Count < ISR_COUNT) + { + /* Save interrupt data */ + isr_table[Intr_Count].vector = vector; + isr_table[Intr_Count].data = data; + isr_table[Intr_Count++].isr = isr; + } + + env_restore_interrupts(); +} + +/** + * env_enable_interrupt + * + * Enables the given interrupt + * + * @param vector - vring index + * @param priority - interrupt priority + * @param polarity - interrupt polarity + */ + +void env_enable_interrupt(unsigned int vector , unsigned int priority , + unsigned int polarity) +{ + platform_interrupt_enable(); +} + +/** + * env_disable_interrupt + * + * Disables the given interrupt + * + * @param vector - interrupt vector number + */ + +void env_disable_interrupt(unsigned int vector) +{ + platform_interrupt_disable(); +} + +/** + * env_map_memory + * + * Enables memory mapping for given memory region. + * + * @param pa - physical address of memory + * @param va - logical address of memory + * @param size - memory size + * param flags - flags for cache/uncached and access type + */ + +void env_map_memory(unsigned int pa, unsigned int va, unsigned int size, + unsigned int flags) +{ +} + +/** + * env_disable_cache + * + * Disables system caches. + * + */ + +void env_disable_cache() +{ +} + +/** + * + * env_get_timestamp + * + * Returns a 64 bit time stamp. + * + * + */ +unsigned long long env_get_timestamp(void) +{ + return 0; +} + +/*========================================================= */ +/* Util data / functions for MQX */ + +void freertos_env_isr(int vector) { + int idx; + struct isr_info *info; + + env_disable_interrupt(vector); + for(idx = 0; idx < ISR_COUNT; idx++) + { + info = &isr_table[idx]; + if(info->vector == vector) + { + info->isr(info->vector , info->data); /*platform_isr*/ + env_enable_interrupt(info->vector , info->priority, info->type); + break; + } + } +} diff --git a/middleware/multicore/open-amp/porting/imx7d_m4/plat_porting.h b/middleware/multicore/open-amp/porting/imx7d_m4/plat_porting.h new file mode 100644 index 0000000..4c25960 --- /dev/null +++ b/middleware/multicore/open-amp/porting/imx7d_m4/plat_porting.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Freescale Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * plat_porting.h + * + * DESCRIPTION + * + * This file contains the declaration of porting related function + * and macros + * + **************************************************************************/ +#ifndef PLAT_PORTING_H_ +#define PLAT_PORTING_H_ + +/* MU_RPMSG_CHANNEL is the MU channel used for master and remote to notify each other*/ +#define MU_RPMSG_CHANNEL 1 + +/* platform specific rpmsg handler which is invoked when a notification is received from peer*/ +void rpmsg_handler(void); + +#endif /* PLAT_PORTING_H_ */ diff --git a/middleware/multicore/open-amp/porting/imx7d_m4/platform.c b/middleware/multicore/open-amp/porting/imx7d_m4/platform.c new file mode 100644 index 0000000..6b1fc8f --- /dev/null +++ b/middleware/multicore/open-amp/porting/imx7d_m4/platform.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Freescale Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * platform.c + * + * DESCRIPTION + * + * This file is the Implementation of IPC hardware layer interface + * for Freescale i.MX7 Dual platform. + * + **************************************************************************/ + +#include "device_imx.h" +#include "platform.h" +#include "plat_porting.h" +#include "mu_imx.h" + +extern void freertos_env_isr(int vector); + +// uint32_t channel_id; +/*--------------------------- Globals ---------------------------------- */ + +struct hil_platform_ops proc_ops = { + .enable_interrupt = _enable_interrupt, + .notify = _notify, + .boot_cpu = _boot_cpu, + .shutdown_cpu = _shutdown_cpu, +}; + +void rpmsg_handler(void) +{ + uint32_t msg, channel; + + if (MU_TryReceiveMsg(MU0_B, MU_RPMSG_CHANNEL, &msg) == kStatus_MU_Success) { + channel = msg >> 16; + freertos_env_isr(channel); + } + + return; +} + +int _enable_interrupt(struct proc_vring *vring_hw) { /*enable_interrupt*/ + /* Register ISR*/ + env_register_isr(vring_hw->intr_info.vect_id, vring_hw, platform_isr); + + /* + * Prepare the MU Hardware, enable channel 1 interrupt + */ + MU_EnableRxFullInt(MU0_B, MU_RPMSG_CHANNEL); + + return 0; +} + +void _notify(int cpu_id, struct proc_intr *intr_info) +{ + /* + * As Linux suggests, use MU->Data Channle 1 as communication channel + */ + uint32_t msg = (intr_info->vect_id) << 16; + MU_SendMsg(MU0_B, MU_RPMSG_CHANNEL, msg); +} + + +int _boot_cpu(int cpu_id, unsigned int load_addr) +{ + return 0; +} + +void _shutdown_cpu(int cpu_id) +{ +} + +void platform_isr(int vect_id, void *data) +{ + hil_isr(((struct proc_vring *) data)); +} + +void platform_interrupt_enable() +{ + NVIC_EnableIRQ(MU_INT_M4_IRQn); +} + +void platform_interrupt_disable() +{ + NVIC_DisableIRQ(MU_INT_M4_IRQn); +} diff --git a/middleware/multicore/open-amp/porting/imx7d_m4/platform.h b/middleware/multicore/open-amp/porting/imx7d_m4/platform.h new file mode 100644 index 0000000..14a92cc --- /dev/null +++ b/middleware/multicore/open-amp/porting/imx7d_m4/platform.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORM_H_ +#define PLATFORM_H_ + +#include "../../common/hil/hil.h" + +int _enable_interrupt(struct proc_vring *vring_hw); +void _notify(int cpu_id, struct proc_intr *intr_info); +int _boot_cpu(int cpu_id, unsigned int load_addr); +void _shutdown_cpu(int cpu_id); +void platform_isr(int vect_id, void *data); + +#endif /* PLATFORM_H_ */ diff --git a/middleware/multicore/open-amp/porting/imx7d_m4/platform_info.c b/middleware/multicore/open-amp/porting/imx7d_m4/platform_info.c new file mode 100644 index 0000000..aaef04f --- /dev/null +++ b/middleware/multicore/open-amp/porting/imx7d_m4/platform_info.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2015, Freescale Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file implements APIs to get platform specific + * information for OpenAMP. + * + **************************************************************************/ + +#include "platform.h" +#include "../../rpmsg/rpmsg.h" + +/* Reference implementation that show cases platform_get_cpu_info and + platform_get_for_firmware API implementation for Bare metal environment */ + +extern struct hil_platform_ops proc_ops; + +/* + * Linux requirems the ALIGN to 0x1000 instead of 0x80 + */ +#define VRING_ALIGN 0x1000 + +/* + * Linux has a different alignment requirement, and its have 512 buffers instead of 32 buffers for the 2 ring + */ +#define VRING0_BASE 0xBFFF0000 +#define VRING1_BASE 0xBFFF8000 + +/* + * 32 MSG (16 rx, 16 tx), 512 bytes each, it is only used when RPMSG driver is working in master mode, otherwise + * the share memory is managed by the other side. + * When working with Linux, SHM_ADDR and SHM_SIZE is not used + */ +#define SHM_ADDR 0 +#define SHM_SIZE 0 + +/* IPI_VECT here defines VRING index in MU */ +#define VRING0_IPI_VECT 0 +#define VRING1_IPI_VECT 1 + +#define MASTER_CPU_ID 0 +#define REMOTE_CPU_ID 1 + +/** + * This array provdes defnition of CPU nodes for master and remote + * context. It contains two nodes beacuse the same file is intended + * to use with both master and remote configurations. On zynq platform + * only one node defintion is required for master/remote as there + * are only two cores present in the platform. + * + * Only platform specific info is populated here. Rest of information + * is obtained during resource table parsing.The platform specific + * information includes; + * + * -CPU ID + * -Shared Memory + * -Interrupts + * -Channel info. + * + * Although the channel info is not platform specific information + * but it is conveneient to keep it in HIL so that user can easily + * provide it without modifying the generic part. + * + * It is good idea to define hil_proc structure with platform + * specific fields populated as this can be easily copied to hil_proc + * structure passed as parameter in platform_get_processor_info. The + * other option is to populate the required structures individually + * and copy them one by one to hil_proc structure in platform_get_processor_info + * function. The first option is adopted here. + * + * + * 1) First node in the array is intended for the remote contexts and it + * defines Master CPU ID, shared memory, interrupts info, number of channels + * and there names. This node defines only one channel + * "rpmsg-data-channel". + * + * 2)Second node is required by the master and it defines remote CPU ID, + * shared memory and interrupts info. In general no channel info is required by the + * Master node, however in baremetal master and linux remote case the linux + * rpmsg bus driver behaves as master so the rpmsg driver on linux side still needs + * channel info. This information is not required by the masters for baremetal + * remotes. + * + */ +struct hil_proc proc_table []= +{ + /* CPU node for remote context */ + { + /* CPU ID of master */ + MASTER_CPU_ID, + + /* Shared memory info - Last field is not used currently */ + { + (void*)SHM_ADDR, SHM_SIZE, 0x00 + }, + + /* VirtIO device info */ /*struct proc_vdev*/ + { + 2, (1<lock, 1); + + if (status != RPMSG_SUCCESS) { + + /* Cleanup required in case of error is performed by caller */ + return status; + } + + rdev_loc->proc = proc; + rdev_loc->role = role; + rdev_loc->channel_created = channel_created; + rdev_loc->channel_destroyed = channel_destroyed; + rdev_loc->default_cb = default_cb; + + /* Initialize the virtio device */ + virt_dev = &rdev_loc->virt_dev; + virt_dev->device = proc; + virt_dev->func = &rpmsg_rdev_config_ops; + if (virt_dev->func->set_features != RPMSG_NULL) { + virt_dev->func->set_features(virt_dev, proc->vdev.dfeatures); /* rpmsg_rdev_set_feature */ + } + + /* + * Linux don't use this way to manage memory pool + */ + if (rdev_loc->role == RPMSG_REMOTE) { + /* + * Since device is RPMSG Remote so we need to manage the + * shared buffers. Create shared memory pool to handle buffers. + */ + shm = hil_get_shm_info(proc); /*proc_table*/ + rdev_loc->mem_pool = sh_mem_create_pool(shm->start_addr, shm->size, + RPMSG_BUFFER_SIZE); + + if (!rdev_loc->mem_pool) { + return RPMSG_ERR_NO_MEM; + } + } + + /* Initialize channels for RPMSG Remote */ + status = rpmsg_rdev_init_channels(rdev_loc); + + if (status != RPMSG_SUCCESS) { + return status; + } + + *rdev = rdev_loc; + + return RPMSG_SUCCESS; +} + +/** + * rpmsg_rdev_deinit + * + * This function un-initializes the remote device. + * + * @param rdev - pointer to remote device to deinit. + * + * @return - none + * + */ +void rpmsg_rdev_deinit(struct remote_device *rdev) { + struct llist *rp_chnl_head, *rp_chnl_temp, *node; + struct rpmsg_channel *rp_chnl; + + rp_chnl_head = rdev->rp_channels; + + while (rp_chnl_head != RPMSG_NULL ) { + + rp_chnl_temp = rp_chnl_head->next; + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + + if ((rdev->support_ns) && (rdev->role == RPMSG_MASTER)) { + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY); + } + + /* Delete default endpoint for channel */ + if (rp_chnl->rp_ept) { + rpmsg_destroy_ept(rp_chnl->rp_ept); + } + + _rpmsg_delete_channel(rp_chnl); + rp_chnl_head = rp_chnl_temp; + } + + /* Delete name service endpoint */ + node = rpmsg_rdev_get_endpoint_from_addr(rdev,RPMSG_NS_EPT_ADDR); + if (node) { + _destroy_endpoint(rdev, (struct rpmsg_endpoint *) node->data); + } + + if (rdev->rvq) { + virtqueue_free(rdev->rvq); + } + if (rdev->tvq) { + virtqueue_free(rdev->tvq); + } + if (rdev->mem_pool) { + sh_mem_delete_pool(rdev->mem_pool); + } + if (rdev->lock) { + env_delete_mutex(rdev->lock); + } + + env_free_memory(rdev); +} + +/** + * rpmsg_rdev_get_chnl_node_from_id + * + * This function returns channel node based on channel name. + * + * @param stack - pointer to remote device + * @param rp_chnl_id - rpmsg channel name + * + * @return - channel node + * + */ +struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev, + char *rp_chnl_id) { + struct rpmsg_channel *rp_chnl; + struct llist *rp_chnl_head; + + rp_chnl_head = rdev->rp_channels; + + env_lock_mutex(rdev->lock); + while (rp_chnl_head) { + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + if (env_strncmp(rp_chnl->name, rp_chnl_id, sizeof(rp_chnl->name)) + == 0) { + env_unlock_mutex(rdev->lock); + return rp_chnl_head; + } + rp_chnl_head = rp_chnl_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} + +/** + * rpmsg_rdev_get_chnl_from_addr + * + * This function returns channel node based on src/dst address. + * + * @param rdev - pointer remote device control block + * @param addr - src/dst address + * + * @return - channel node + * + */ +struct llist *rpmsg_rdev_get_chnl_from_addr(struct remote_device *rdev, + unsigned long addr) { + struct rpmsg_channel *rp_chnl; + struct llist *rp_chnl_head; + + rp_chnl_head = rdev->rp_channels; + + env_lock_mutex(rdev->lock); + while (rp_chnl_head) { + rp_chnl = (struct rpmsg_channel *) rp_chnl_head->data; + if ((rp_chnl->src == addr) || (rp_chnl->dst == addr)) { + env_unlock_mutex(rdev->lock); + return rp_chnl_head; + } + rp_chnl_head = rp_chnl_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} + +/** + * rpmsg_rdev_get_endpoint_from_addr + * + * This function returns endpoint node based on src address. + * + * @param rdev - pointer remote device control block + * @param addr - src address + * + * @return - endpoint node + * + */ +struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev, + unsigned long addr) { + struct llist *rp_ept_lut_head; + + rp_ept_lut_head = rdev->rp_endpoints; + + env_lock_mutex(rdev->lock); + while (rp_ept_lut_head) { + struct rpmsg_endpoint *rp_ept = + (struct rpmsg_endpoint *) rp_ept_lut_head->data; + if (rp_ept->addr == addr) { + env_unlock_mutex(rdev->lock); + return rp_ept_lut_head; + } + rp_ept_lut_head = rp_ept_lut_head->next; + } + env_unlock_mutex(rdev->lock); + + return RPMSG_NULL ; +} +/* + * rpmsg_rdev_notify + * + * This function checks whether remote device is up or not. If it is up then + * notification is sent based on device role to start IPC. + * + * @param rdev - pointer to remote device + * + * @return - status of function execution + * + */ +int rpmsg_rdev_notify(struct remote_device *rdev) { + int status = RPMSG_SUCCESS; + + if (rdev->role == RPMSG_REMOTE) { + status = hil_get_status(rdev->proc); + + /* + * Let the remote device know that Master is ready for + * communication. + */ + if (!status) + virtqueue_kick(rdev->rvq); /*will triggle rpmsg_tx_callback in the REMOTE side*/ + + } else { + status = hil_set_status(rdev->proc); + } + + if (status == RPMSG_SUCCESS) { + rdev->state = RPMSG_DEV_STATE_ACTIVE; + } + + return status; +} +/** + * rpmsg_rdev_init_channels + * + * This function is only applicable to RPMSG remote. It obtains channel IDs + * from the HIL and creates RPMSG channels corresponding to each ID. + * + * @param rdev - pointer to remote device + * + * @return - status of function execution + * + */ +int rpmsg_rdev_init_channels(struct remote_device *rdev) { + struct rpmsg_channel *rp_chnl; + struct proc_chnl *chnl_info; + int num_chnls, idx; + + if (rdev->role == RPMSG_MASTER) { + + chnl_info = hil_get_chnl_info(rdev->proc, &num_chnls); /*proc_table*/ + for (idx = 0; idx < num_chnls; idx++) { + + rp_chnl = _rpmsg_create_channel(rdev, chnl_info[idx].name, 0x00, + RPMSG_NS_EPT_ADDR); /*the channel is put to "rp_channels" field of remote_device data structure*/ + if (!rp_chnl) { + return RPMSG_ERR_NO_MEM; + } + + rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, + RPMSG_ADDR_ANY); /*the endpoint is put ot "rp_endpoints" field of remote_device data structure*/ + + if (!rp_chnl->rp_ept) { + return RPMSG_ERR_NO_MEM; + } + + rp_chnl->src = rp_chnl->rp_ept->addr; /*channel source is the default endpoint address, destination is NS endpoint address*/ + + } + } + + return RPMSG_SUCCESS; +} + +/** + *------------------------------------------------------------------------ + * The rest of the file implements the virtio device interface as defined + * by the virtio.h file. + *------------------------------------------------------------------------ + */ +int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, // invoked by virtio_device->create_virtqueues + const char *names[], vq_callback *callbacks[], + struct virtqueue *vqs_[]) { + struct remote_device *rdev; + struct vring_alloc_info ring_info; + struct virtqueue *vqs[RPMSG_MAX_VQ_PER_RDEV]; + struct proc_vring *vring_table; + void *buffer; + struct llist node; + int idx, num_vrings, status; + + rdev = (struct remote_device*) dev; + + /* Get the vring HW info for the given virtio device */ + vring_table = hil_get_vring_info(&rdev->proc->vdev, + &num_vrings); + + + if (num_vrings > nvqs) { + return RPMSG_ERR_MAX_VQ; + } + + /* Create virtqueue for each vring. */ + for (idx = 0; idx < num_vrings; idx++) { + + INIT_VRING_ALLOC_INFO( ring_info, vring_table[idx]); /*ring_info comes from proc_table*/ + + /* + * - phy_addr + * - align + * - num_descs + */ + + if (rdev->role == RPMSG_REMOTE) { + env_memset((void*) ring_info.phy_addr, 0x00, + vring_size(vring_table[idx].num_descs, + vring_table[idx].align)); + } + + /* + * created virtqueue should be in vqs[0], vqs[1] + */ + status = virtqueue_create(dev, idx, (char *) names[idx], &ring_info, + callbacks[idx], hil_vring_notify, + &vqs[idx]); + + if (status != RPMSG_SUCCESS) { + return status; + } + } + + //FIXME - a better way to handle this , tx for master is rx for remote and vice versa. + /* + * MASTER REMOTE + * vqs[0] RX TX + * vqs[1] TX RX + */ + if (rdev->role == RPMSG_MASTER) { + rdev->tvq = vqs[0]; + rdev->rvq = vqs[1]; + } else { + rdev->tvq = vqs[1]; + rdev->rvq = vqs[0]; + } + + if (rdev->role == RPMSG_REMOTE) { + for (idx = 0; ((idx < rdev->rvq->vq_nentries) + && (idx < rdev->mem_pool->total_buffs / 2)); + idx++) { + + + /* Initialize TX virtqueue buffers for remote device */ + buffer = sh_mem_get_buffer(rdev->mem_pool); + + if (!buffer) { + return RPMSG_ERR_NO_BUFF; + } + + node.data = buffer; + node.attr = RPMSG_BUFFER_SIZE; + node.next = RPMSG_NULL; + + env_memset(buffer, 0x00, RPMSG_BUFFER_SIZE); + status = virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); /* this is where vq_ring.avial.idx is updated by "vq_ring_update_avail"*/ + + if (status != RPMSG_SUCCESS) { + return status; + } + } + } + + return RPMSG_SUCCESS; +} + +unsigned char rpmsg_rdev_get_status(struct virtio_device *dev) { + return 0; +} + +void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status) { + +} + +uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev) { + return dev->features; +} + +void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature) { + dev->features |= feature; /*refer to name service endpoint creation logic in the caller rpmsg_start_ipc*/ +} + +uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev, + uint32_t features) { + return 0; +} +/* + * Read/write a variable amount from the device specific (ie, network) + * configuration region. This region is encoded in the same endian as + * the guest. + */ +void rpmsg_rdev_read_config(struct virtio_device *dev, uint32_t offset, + void *dst, int length) { + return; +} +void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset, + void *src, int length) { + return; +} +void rpmsg_rdev_reset(struct virtio_device *dev) { + return; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.c b/middleware/multicore/open-amp/rpmsg/rpmsg.c new file mode 100644 index 0000000..66db1ad --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * rpmsg.c + * + * COMPONENT + * + * OpenAMP stack. + * + * DESCRIPTION + * + * Main file for the RPMSG driver. This file implements APIs as defined by + * RPMSG documentation(Linux docs) and also provides some utility functions. + * + * RPMSG driver represents each processor/core to which it communicates with + * remote_device control block. + * Each remote device(processor) defines its role in the communication i.e + * whether it is RPMSG Master or Remote. If the device (processor) to which + * driver is talking is RPMSG master then RPMSG driver implicitly behaves as + * Remote and vice versa. + * RPMSG Master is responsible for initiating communications with the Remote + * and shared buffers management. Terms remote device/core/proc are used + * interchangeably for the processor to which RPMSG driver is communicating + * irrespective of the fact whether it is RPMSG Remote or Master. + * + **************************************************************************/ +#include "rpmsg.h" + +/** + * rpmsg_init + * + * Thus function allocates and initializes the rpmsg driver resources for + * given device ID(cpu id). The successful return from this function leaves + * fully enabled IPC link. + * + * @param dev_id - remote device for which driver is to + * be initialized + * @param rdev - pointer to newly created remote device + * @param channel_created - callback function for channel creation + * @param channel_destroyed - callback function for channel deletion + * @param default_cb - default callback for channel I/O + * @param role - role of the other device, Master or Remote + * + * @return - status of function execution + * + */ + +int rpmsg_init(int dev_id, struct remote_device **rdev, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb, int role) { + int status; + + PRINTF("init M4 as %s\r\n", role?"REMOTE":"MASTER"); + /* Initialize IPC environment */ + status = env_init(); + if (status == RPMSG_SUCCESS) { + /* Initialize the remote device for given cpu id */ + status = rpmsg_rdev_init(rdev, dev_id, role, channel_created, + channel_destroyed, default_cb); + if (status == RPMSG_SUCCESS) { + /* Kick off IPC with the remote device */ + status = rpmsg_start_ipc(*rdev); + } + } + + /* Deinit system in case of error */ + if (status != RPMSG_SUCCESS) { + rpmsg_deinit(*rdev); + } + + return status; +} + +/** + * rpmsg_deinit + * + * Thus function frees rpmsg driver resources for given remote device. + * + * @param rdev - pointer to device to de-init + * + */ + +void rpmsg_deinit(struct remote_device *rdev) { + if (rdev) { + rpmsg_rdev_deinit(rdev); + env_deinit(); + } +} + +/** + * This function sends rpmsg "message" to remote device. + * + * @param rp_chnl - pointer to rpmsg channel + * @param src - source address of channel + * @param dst - destination address of channel + * @param data - data to transmit + * @param size - size of data + * @param wait - boolean, wait or not for buffer to become + * available + * + * @return - status of function execution + * + */ + +int rpmsg_send_offchannel_raw(struct rpmsg_channel *rp_chnl, unsigned long src, + unsigned long dst, char *data, int size, int wait) { + struct remote_device *rdev; + struct rpmsg_hdr *rp_hdr; + void *buffer; + int status = RPMSG_SUCCESS; + unsigned short idx; + int tick_count = 0; + int buff_len; + + if (!rp_chnl) { + return RPMSG_ERR_PARAM; + } + + /* Get the associated remote device for channel. */ + rdev = rp_chnl->rdev; + + /* Validate device state */ + if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE + || rdev->state != RPMSG_DEV_STATE_ACTIVE) { + return RPMSG_ERR_DEV_STATE; + } + + /* Lock the device to enable exclusive access to virtqueues */ + env_lock_mutex(rdev->lock); + /* Get rpmsg buffer for sending message. */ + buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + if (!buffer && !wait) { + status = RPMSG_ERR_NO_MEM; + } + env_unlock_mutex(rdev->lock); + + if (status == RPMSG_SUCCESS) { + + while (!buffer) { + /* + * Wait parameter is true - pool the buffer for + * 15 secs as defined by the APIs. + */ + env_sleep_msec(RPMSG_TICKS_PER_INTERVAL); + env_lock_mutex(rdev->lock); + buffer = rpmsg_get_tx_buffer(rdev, &buff_len, &idx); + env_unlock_mutex(rdev->lock); + tick_count += RPMSG_TICKS_PER_INTERVAL; + if (tick_count >= (RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL)) { + status = RPMSG_ERR_NO_BUFF; + break; + } + } + + if (status == RPMSG_SUCCESS) { + //FIXME : may be just copy the data size equal to buffer length and Tx it. + if (size > (buff_len - sizeof(struct rpmsg_hdr))) + status = RPMSG_ERR_BUFF_SIZE; + + if (status == RPMSG_SUCCESS) { + rp_hdr = (struct rpmsg_hdr *) buffer; + + /* Initialize RPMSG header. */ + rp_hdr->dst = dst; + rp_hdr->src = src; + rp_hdr->len = size; + + /* Copy data to rpmsg buffer. */ + env_memcpy(rp_hdr->data, data, size); + + env_lock_mutex(rdev->lock); + /* Enqueue buffer on virtqueue. */ + status = rpmsg_enqueue_buffer(rdev, buffer, buff_len, idx); + if (status == RPMSG_SUCCESS) { + /* Let the other side know that there is a job to process. */ + virtqueue_kick(rdev->tvq); + } + env_unlock_mutex(rdev->lock); + } + + } + } + + /* Do cleanup in case of error.*/ + if (status != RPMSG_SUCCESS) { + rpmsg_free_buffer(rdev, buffer); + } + + return status; +} + +/** + * rpmsg_get_buffer_size + * + * Returns buffer size available for sending messages. + * + * @param channel - pointer to rpmsg channel + * + * @return - buffer size + * + */ +int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl) { + struct remote_device *rdev; + int length; + + if (!rp_chnl) { + return RPMSG_ERR_PARAM; + } + + /* Get associated remote device for channel. */ + rdev = rp_chnl->rdev; + + /* Validate device state */ + if (rp_chnl->state != RPMSG_CHNL_STATE_ACTIVE + || rdev->state != RPMSG_DEV_STATE_ACTIVE) { + return RPMSG_ERR_DEV_STATE; + } + + env_lock_mutex(rdev->lock); + + if (rdev->role == RPMSG_REMOTE) { + /* + * If device role is Remote then buffers are provided by us + * (RPMSG Master), so just provide the macro. + */ + length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr); + } else { + /* + * If other core is Master then buffers are provided by it, + * so get the buffer size from the virtqueue. + */ + length = (int) virtqueue_get_desc_size(rdev->tvq) - sizeof(struct rpmsg_hdr); + } + + env_unlock_mutex(rdev->lock); + + return length; +} + +/** + * rpmsg_create_ept + * + * This function creates rpmsg endpoint for the rpmsg channel. + * + * @param channel - pointer to rpmsg channel + * @param cb - Rx completion call back + * @param priv - private data + * @param addr - endpoint src address + * + * @return - pointer to endpoint control block + * + */ +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { + + /* + * Note : When calling rpmsg_create_ept to a channel, the endpoint + * is put into info field of remote_device, not the channel + * + * + * CHANNEL ---> RDEV ---> CHANNELs + * + * EPT ---> RDEV ---> EPTs + */ + struct remote_device *rdev = RPMSG_NULL; + struct rpmsg_endpoint *rp_ept = RPMSG_NULL; + + if (!rp_chnl || !cb) { + return RPMSG_NULL ; + } + + rdev = rp_chnl->rdev; + + rp_ept = _create_endpoint(rdev, cb, priv, addr); + + if (rp_ept) { + rp_ept->rp_chnl = rp_chnl; + } + + return rp_ept; +} + +/** + * rpmsg_destroy_ept + * + * This function deletes rpmsg endpoint and performs cleanup. + * + * @param rp_ept - pointer to endpoint to destroy + * + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *rp_ept) { + + struct remote_device *rdev; + struct rpmsg_channel *rp_chnl; + + if (!rp_ept) + return; + + rp_chnl = rp_ept->rp_chnl; + rdev = rp_chnl->rdev; + + _destroy_endpoint(rdev, rp_ept); +} + +/** + * rpmsg_create_channel + * + * This function provides facility to create channel dynamically. It sends + * Name Service announcement to remote device to let it know about the channel + * creation. There must be an active communication among the cores (or atleast + * one rpmsg channel must already exist) before using this API to create new + * channels. + * + * @param rdev - pointer to remote device + * @param name - channel name + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, + char *name) { + + struct rpmsg_channel *rp_chnl; + struct rpmsg_endpoint *rp_ept; + + if (!rdev || !name) { + return RPMSG_NULL ; + } + + /* Create channel instance */ + rp_chnl = _rpmsg_create_channel(rdev, name, RPMSG_NS_EPT_ADDR, + RPMSG_NS_EPT_ADDR); + if (!rp_chnl) { + return RPMSG_NULL ; + } + + /* Create default endpoint for the channel */ + rp_ept = rpmsg_create_ept(rp_chnl , rdev->default_cb, rdev, + RPMSG_ADDR_ANY); + + if (!rp_ept) { + _rpmsg_delete_channel(rp_chnl); + return RPMSG_NULL; + } + + rp_chnl->rp_ept = rp_ept; + rp_chnl->src = rp_ept->addr; + rp_chnl->state = RPMSG_CHNL_STATE_NS; + + /* Notify the application of channel creation event */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); + } + + /* Send NS announcement to remote processor */ + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + + return rp_chnl; +} + +/** + * rpmsg_delete_channel + * + * Deletes the given RPMSG channel. The channel must first be created with the + * rpmsg_create_channel API. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + */ +void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl) { + + struct remote_device *rdev; + + if (!rp_chnl) { + return; + } + + rdev = rp_chnl->rdev; + + if (rp_chnl->state > RPMSG_CHNL_STATE_IDLE) { + /* Notify the other processor that channel no longer exists */ + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_DESTROY); + } + + /* Notify channel deletion to application */ + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + + rpmsg_destroy_ept(rp_chnl->rp_ept); + _rpmsg_delete_channel(rp_chnl); + + return; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg.h b/middleware/multicore/open-amp/rpmsg/rpmsg.h new file mode 100644 index 0000000..746b792 --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg.h @@ -0,0 +1,411 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RPMSG_H_ +#define _RPMSG_H_ + +#include "../rpmsg/rpmsg_core.h" + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ +#define RPMSG_NAME_SIZE 32 + +/** + * struct rpmsg_hdr - common header for all rpmsg messages + * @src: source address + * @dst: destination address + * @reserved: reserved for future use + * @len: length of payload (in bytes) + * @flags: message flags + * @data: @len bytes of message payload data + * + * Every message sent(/received) on the rpmsg bus begins with this header. + */ +struct rpmsg_hdr { + unsigned long src; + unsigned long dst; + unsigned long reserved; + unsigned short len; + unsigned short flags; + unsigned char data[0]; +} /*__attribute__((packed))*/; + +/** + * struct rpmsg_ns_msg - dynamic name service announcement message + * @name: name of remote service that is published + * @addr: address of remote service that is published + * @flags: indicates whether service is created or destroyed + * + * This message is sent across to publish a new service, or announce + * about its removal. When we receive these messages, an appropriate + * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe() + * or ->remove() handler of the appropriate rpmsg driver will be invoked + * (if/as-soon-as one is registered). + */ +struct rpmsg_ns_msg { + char name[RPMSG_NAME_SIZE]; + unsigned long addr; + unsigned long flags; +} /*__attribute__((packed))*/; + +/** + * enum rpmsg_ns_flags - dynamic name service announcement flags + * + * @RPMSG_NS_CREATE: a new remote service was just created + * @RPMSG_NS_DESTROY: a known remote service was just destroyed + */ +enum rpmsg_ns_flags { + RPMSG_NS_CREATE = 0, + RPMSG_NS_DESTROY = 1, +}; + +#define RPMSG_ADDR_ANY 0xFFFFFFFF + + +/** + * rpmsg_channel - devices that belong to the rpmsg bus are called channels + * @name: channel name + * @src: local address + * @dst: destination address + * rdev: rpmsg remote device + * @ept: the rpmsg endpoint of this channel + * @state: channel state + */ +struct rpmsg_channel { + char name[RPMSG_NAME_SIZE]; + unsigned long src; + unsigned long dst; + struct remote_device *rdev; + struct rpmsg_endpoint *rp_ept; + unsigned int state; +}; + +/** + * channel_info - channel info + * @name: channel name + * @src: local address + * @dst: destination address + */ + +struct channel_info { + char name[RPMSG_NAME_SIZE]; + unsigned long src; + unsigned long dest; +}; + +/** + * struct rpmsg_endpoint - binds a local rpmsg address to its user + * @rp_chnl: rpmsg channel device + * @cb: rx callback handler + * @addr: local rpmsg address + * @priv: private data for the driver's use + * + * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as + * it binds an rpmsg address with an rx callback handler. + * + * Simple rpmsg drivers shouldn't use this struct directly, because + * things just work: every rpmsg driver provides an rx callback upon + * registering to the bus, and that callback is then bound to its rpmsg + * address when the driver is probed. When relevant inbound messages arrive + * (i.e. messages which their dst address equals to the src address of + * the rpmsg channel), the driver's handler is invoked to process it. + * + * More complicated drivers though, that do need to allocate additional rpmsg + * addresses, and bind them to different rx callbacks, must explicitly + * create additional endpoints by themselves (see rpmsg_create_ept()). + */ +struct rpmsg_endpoint { + struct rpmsg_channel *rp_chnl; + rpmsg_rx_cb_t cb; + unsigned long addr; + void *priv; +}; + +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rp_chnl, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr); + +void rpmsg_destroy_ept(struct rpmsg_endpoint *rp_ept); + +int +rpmsg_send_offchannel_raw(struct rpmsg_channel *, unsigned long, unsigned long, char *, int, int); + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, unsigned long dst) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, dst, (char *)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, rpdev->dst, (char*)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_TRUE); +} + +/** + * rpmsg_trysend() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len) +{ + + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, rpdev->src, rpdev->dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_trysendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, unsigned long dst) +{ + unsigned long src; + + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, unsigned long src, unsigned long dst, + void *data, int len) +{ + if (!rpdev || !data) + return RPMSG_ERR_PARAM; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, (char *)data, len, RPMSG_FALSE); +} + +/** + * rpmsg_init + * + * Thus function allocates and initializes the rpmsg driver resources for given + * device id (cpu id).The successful return from this function leaves + * fully enabled IPC link. + * + * @param dev_id - rpmsg remote device for which driver is to + * be initialized + * @param rdev - pointer to newly created remote device + * @param channel_created - callback function for channel creation + * @param channel_destroyed - callback function for channel deletion + * @default_cb - default callback for channel + * @param role - role of the other device, Master or Remote + * @return - status of function execution + * + */ + +int rpmsg_init(int dev_id, struct remote_device **rdev, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb, int role); + +/** + * rpmsg_deinit + * + * Thus function releases the rpmsg driver resources for given remote + * instance. + * + * @param rdev - pointer to device de-init + * + * @return - none + * + */ +void rpmsg_deinit(struct remote_device *rdev); + +/** + * rpmsg_get_buffer_size + * + * Returns buffer size available for sending messages. + * + * @param channel - pointer to rpmsg channel/device + * + * @return - buffer size + * + */ +int rpmsg_get_buffer_size(struct rpmsg_channel *rp_chnl); + +/** + * rpmsg_create_channel + * + * Creates RPMSG channel with the given name for remote device. + * + * @param rdev - pointer to rpmsg remote device + * @param name - channel name + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *rpmsg_create_channel(struct remote_device *rdev, char *name); + +/** + * rpmsg_delete_channel + * + * Deletes the given RPMSG channel. The channel must first be created with the + * rpmsg_create_channel API. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + */ +void rpmsg_delete_channel(struct rpmsg_channel *rp_chnl); + +/** + * + * rpmsg_handler + * + * Provide platform specific interrupt handler to application layer + * + */ +void rpmsg_handler(void); + +#endif /* _RPMSG_H_ */ diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.c b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c new file mode 100644 index 0000000..c8cb82f --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.c @@ -0,0 +1,796 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/************************************************************************** + * FILE NAME + * + * rpmsg_core.c + * + * COMPONENT + * + * OpenAMP + * + * DESCRIPTION + * + * This file provides the core functionality of RPMSG messaging part like + * message parsing ,Rx/Tx callbacks handling , channel creation/deletion + * and address management. + * + * + **************************************************************************/ +#include "rpmsg.h" + +/* Internal functions */ +static void rpmsg_rx_callback(struct virtqueue *vq); +static void rpmsg_tx_callback(struct virtqueue *vq); + +/** + * rpmsg_start_ipc + * + * This function creates communication links(virtqueues) for remote device + * and notifies it to start IPC. + * + * @param rdev - remote device handle + * + * @return - status of function execution + * + */ +int rpmsg_start_ipc(struct remote_device *rdev) { + struct virtio_device *virt_dev; + struct rpmsg_endpoint *ns_ept; + void (*callback[2])(struct virtqueue *vq); + const char *vq_names[2]; + unsigned long dev_features; + int status; + struct virtqueue *vqs[2]; + int i; + + virt_dev = &rdev->virt_dev; + + /* Initialize names and callbacks based on the device role */ + /* + * virtqueue[0] virtqueue[1] + * MASTER "tx_vq", "rpmsg_tx_callback" "rx_vq", "rpmsg_rx_callback" + * + * REMOTE "rx_vq", "rpmsg_rx_callback" "tx_vq", "rpmsg_tx_callback" + */ + if (rdev->role == RPMSG_MASTER) { + vq_names[0] = "tx_vq"; + vq_names[1] = "rx_vq"; + callback[0] = rpmsg_tx_callback; + callback[1] = rpmsg_rx_callback; + } else { + vq_names[0] = "rx_vq"; + vq_names[1] = "tx_vq"; + callback[0] = rpmsg_rx_callback; + callback[1] = rpmsg_tx_callback; + } + + /* Create virtqueues for remote device */ + /* + * the 2 created virtqueues are assigned to rdev's tvq and rvq respectively + * regarding on the MATER/REMOTE role + * + * REMOTE: proc_table.vring_info[0] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[0]" + * proc_table.vring_info[1] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[1]" + * MASTER: proc_table.vring_info[0] "rx_vq" "rpmsg_rx_callback" "rdev->rvq" -> "vqs[0]" + * proc_table.vring_info[1] "tx_vq" "rpmsg_tx_callback" "rdev->tvq" -> "vqs[1]" + */ + status = virt_dev->func->create_virtqueues(virt_dev, 0, /*rpmsg_rdev_create_virtqueues*/ + RPMSG_MAX_VQ_PER_RDEV, vq_names, callback, RPMSG_NULL); + if (status != RPMSG_SUCCESS) { + return status; + } + + dev_features = virt_dev->func->get_features(virt_dev); /*rpmsg_rdev_get_feature*/ + + /* + * Create name service announcement endpoint if device supports name + * service announcement feature. + */ + if ((dev_features & (1<support_ns = RPMSG_TRUE; + ns_ept = _create_endpoint(rdev, rpmsg_ns_callback, rdev, /* Is this necessary for a remote? */ + RPMSG_NS_EPT_ADDR); + if (!ns_ept) { + return RPMSG_ERR_NO_MEM; + } + } + + /* Initialize notifications for vring. */ + if (rdev->role == RPMSG_MASTER) { + vqs[0] = rdev->tvq; + vqs[1] = rdev->rvq; + } else { + vqs[0] = rdev->rvq; + vqs[1] = rdev->tvq; + } + for(i = 0; i <= 1; i++) { + status = hil_enable_vring_notifications(i, vqs[i]); + if (status != RPMSG_SUCCESS) { + return status; + } + } + + status = rpmsg_rdev_notify(rdev); + + return status; +} + +/** + * _rpmsg_create_channel + * + * Creates new rpmsg channel with the given parameters. + * + * @param rdev - pointer to remote device which contains the channel + * @param name - name of the device + * @param src - source address for the rpmsg channel + * @param dst - destination address for the rpmsg channel + * + * @return - pointer to new rpmsg channel + * + */ +struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, + char *name, unsigned long src, unsigned long dst) { + struct rpmsg_channel *rp_chnl; + struct llist *node; + + rp_chnl = env_allocate_memory(sizeof(struct rpmsg_channel)); + if (rp_chnl) { + env_memset(rp_chnl, 0x00, sizeof(struct rpmsg_channel)); + env_strncpy(rp_chnl->name, name, sizeof(rp_chnl->name)); + rp_chnl->src = src; + rp_chnl->dst = dst; + rp_chnl->rdev = rdev; + /* Place channel on channels list */ + node = env_allocate_memory(sizeof(struct llist)); + if (!node) { + env_free_memory(rp_chnl); + return RPMSG_NULL ; + } + node->data = rp_chnl; + env_lock_mutex(rdev->lock); + add_to_list(&rdev->rp_channels , node); + env_unlock_mutex(rdev->lock); + } + + return rp_chnl; +} + +/** + * _rpmsg_delete_channel + * + * Deletes given rpmsg channel. + * + * @param rp_chnl - pointer to rpmsg channel to delete + * + * return - none + */ +void _rpmsg_delete_channel(struct rpmsg_channel * rp_chnl) { + struct llist *node; + if (rp_chnl) { + node = rpmsg_rdev_get_chnl_node_from_id(rp_chnl->rdev, rp_chnl->name); + if (node) { + env_lock_mutex(rp_chnl->rdev->lock); + remove_from_list(&rp_chnl->rdev->rp_channels, node); + env_unlock_mutex(rp_chnl->rdev->lock); + env_free_memory(node); + } + env_free_memory(rp_chnl); + } +} + +/** + * _create_endpoint + * + * This function creates rpmsg endpoint. + * + * @param rdev - pointer to remote device + * @param cb - Rx completion call back + * @param priv - private data + * @param addr - endpoint src address + * + * @return - pointer to endpoint control block + * + */ +struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr) { + + struct rpmsg_endpoint *rp_ept; + struct llist *node; + int status = RPMSG_SUCCESS; + + rp_ept = env_allocate_memory(sizeof(struct rpmsg_endpoint)); + if (!rp_ept) { + return RPMSG_NULL ; + } + + node = env_allocate_memory(sizeof(struct llist)); + if (!node) { + env_free_memory(rp_ept); + return RPMSG_NULL; + } + + env_lock_mutex(rdev->lock); + + if (addr != RPMSG_ADDR_ANY) { + /* + * Application has requested a particular src address for endpoint, + * first check if address is available. + */ + if (!rpmsg_is_address_set(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr)) { + /* Mark the address as used in the address bitmap. */ + rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, addr); + + } else { + status = RPMSG_ERR_DEV_ADDR; + } + } else { + addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE); + if (addr < 0) { + status = RPMSG_ERR_DEV_ADDR; + } + } + + /* Do cleanup in case of error and return */ + if (status) { + env_free_memory(node); + env_free_memory(rp_ept); + env_unlock_mutex(rdev->lock); + return RPMSG_NULL; + } + + rp_ept->addr = addr; + rp_ept->cb = cb; + rp_ept->priv = priv; + + node->data = rp_ept; + add_to_list(&rdev->rp_endpoints, node); + + env_unlock_mutex(rdev->lock); + + return rp_ept; +} + +/** + * rpmsg_destroy_ept + * + * This function deletes rpmsg endpoint and performs cleanup. + * + * @param rdev - pointer to remote device + * @param rp_ept - pointer to endpoint to destroy + * + */ +void _destroy_endpoint(struct remote_device *rdev, + struct rpmsg_endpoint *rp_ept) { + struct llist *node; + node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_ept->addr); + if (node) { + env_lock_mutex(rdev->lock); + rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, rp_ept->addr); + remove_from_list(&rdev->rp_endpoints, node); + env_unlock_mutex(rdev->lock); + env_free_memory(node); + } + env_free_memory(rp_ept); +} + +/** + * rpmsg_send_ns_message + * + * Sends name service announcement to remote device + * + * @param rdev - pointer to remote device + * @param rp_chnl - pointer to rpmsg channel + * @param flags - Channel creation/deletion flags + * + */ +void rpmsg_send_ns_message(struct remote_device *rdev, + struct rpmsg_channel *rp_chnl, unsigned long flags) { + + struct rpmsg_hdr *rp_hdr; + struct rpmsg_ns_msg *ns_msg; + unsigned short idx; + int len; + + env_lock_mutex(rdev->lock); + + /* Get Tx buffer. */ + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_tx_buffer(rdev, &len, &idx); + if (!rp_hdr) + return; + + /* Fill out name service data. */ + rp_hdr->dst = RPMSG_NS_EPT_ADDR; + rp_hdr->len = sizeof(struct rpmsg_ns_msg); + ns_msg = (struct rpmsg_ns_msg *) rp_hdr->data; + env_strncpy(ns_msg->name, rp_chnl->name, sizeof(rp_chnl->name)); + ns_msg->flags = flags; + ns_msg->addr = rp_chnl->src; + + /* Place the buffer on virtqueue. */ + rpmsg_enqueue_buffer(rdev, rp_hdr, len, idx); + + /* Notify the other side that it has data to process. */ + virtqueue_kick(rdev->tvq); + + env_unlock_mutex(rdev->lock); +} + +/** + * rpmsg_enqueue_buffers + * + * Places buffer on the virtqueue for consumption by the other side. + * + * @param rdev - pointer to remote core + * @param buffer - buffer pointer + * @param len - buffer length + * @idx - buffer index + * + * @return - status of function execution + * + */ +int rpmsg_enqueue_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx) { + struct llist node; + int status; + + /* Initialize buffer node */ + node.data = buffer; + node.attr = len; + node.next = RPMSG_NULL; + node.prev = RPMSG_NULL; + + if (rdev->role == RPMSG_REMOTE) { + /*MASTER*/ + status = virtqueue_add_buffer(rdev->tvq, &node, 0, 1, buffer); + } else { + /*REMOTE*/ + status = virtqueue_add_consumed_buffer(rdev->tvq, idx, len); + } + + return status; +} + +/** + * rpmsg_return_buffer + * + * Places the used buffer back on the virtqueue. + * + * @param rdev - pointer to remote core + * @param buffer - buffer pointer + * @param len - buffer length + * @param idx - buffer index + * + */ +void rpmsg_return_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx) { + struct llist node; + + /* Initialize buffer node */ + node.data = buffer; + node.attr = len; + node.next = RPMSG_NULL; + node.prev = RPMSG_NULL; + + if (rdev->role == RPMSG_REMOTE) { + /*master*/ + virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer); + } else { + /*remote*/ + virtqueue_add_consumed_buffer(rdev->rvq, idx, len); + } +} + +/** + * rpmsg_get_tx_buffer + * + * Provides buffer to transmit messages. + * + * @param rdev - pointer to remote device + * @param len - length of returned buffer + * @param idx - buffer index + * + * return - pointer to buffer. + */ +void *rpmsg_get_tx_buffer(struct remote_device *rdev, int *len, + unsigned short *idx) { + void *data; + + if (rdev->role == RPMSG_REMOTE) { + /* MASTER */ + data = virtqueue_get_buffer(rdev->tvq, (uint32_t *) len); + if (data == RPMSG_NULL) { + /*Here is why Master don't need to pre link memory to vring*/ + data = sh_mem_get_buffer(rdev->mem_pool); + *len = RPMSG_BUFFER_SIZE; + } + } else { + /* REMOTE */ + data = virtqueue_get_available_buffer(rdev->tvq, idx, + (uint32_t *) len); + } + return ((void *) env_map_vatopa(data)); +} + +/** + * rpmsg_get_rx_buffer + * + * Retrieves the received buffer from the virtqueue. + * + * @param rdev - pointer to remote device + * @param len - size of received buffer + * @param idx - index of buffer + * + * @return - pointer to received buffer + * + */ +void *rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, + unsigned short *idx) { + + void *data; + if (rdev->role == RPMSG_REMOTE) { + /*MASTER*/ + data = virtqueue_get_buffer(rdev->rvq, (uint32_t *)len); + } else { + /*REMOTE*/ + data = virtqueue_get_available_buffer(rdev->rvq, idx, (uint32_t *)len); + } + return ((void *) env_map_vatopa(data)); +} + +/** + * rpmsg_free_buffer + * + * Frees the allocated buffers. + * + * @param rdev - pointer to remote device + * @param buffer - pointer to buffer to free + * + */ +void rpmsg_free_buffer(struct remote_device *rdev, void *buffer) { + if (rdev->role == RPMSG_REMOTE) { + sh_mem_free_buffer(rdev->mem_pool, buffer); + } +} + +/** + * rpmsg_tx_callback + * + * Tx callback function. + * + * @param vq - pointer to virtqueue on which Tx is has been + * completed. + * + */ +static void rpmsg_tx_callback(struct virtqueue *vq) { + struct remote_device *rdev; + struct virtio_device *vdev; + struct rpmsg_channel *rp_chnl; + struct llist *chnl_hd; + + vdev = (struct virtio_device *) vq->vq_dev; + rdev = (struct remote_device *) vdev; + chnl_hd = rdev->rp_channels; + + /* Check if the remote device is master. */ + if (rdev->role == RPMSG_MASTER) { + /* Notification is received from the master. Now the remote(us) can + * performs one of two operations; + * + * a. If name service announcement is supported then it will send NS message. + * else + * b. It will update the channel state to active so that further communication + * can take place. + */ + while (chnl_hd != RPMSG_NULL) { + rp_chnl = (struct rpmsg_channel *) chnl_hd->data; + + if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { + + if (rdev->support_ns) { + rp_chnl->state = RPMSG_CHNL_STATE_NS; + } else { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + } + + if (rp_chnl->state == RPMSG_CHNL_STATE_NS) { + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + } + } + + chnl_hd = chnl_hd->next; + } + } +} + +/** + * rpmsg_rx_callback + * + * Rx callback function. + * + * @param vq - pointer to virtqueue on which messages is received + * + */ +void rpmsg_rx_callback(struct virtqueue *vq) { + struct remote_device *rdev; + struct virtio_device *vdev; + struct rpmsg_channel *rp_chnl; + struct rpmsg_endpoint *rp_ept; + struct rpmsg_hdr *rp_hdr; + struct llist *node; + unsigned long len; + unsigned short idx; + struct llist *chnl_hd; + + vdev = (struct virtio_device *) vq->vq_dev; + rdev = (struct remote_device *) vdev; + + chnl_hd = rdev->rp_channels; + if ((chnl_hd != RPMSG_NULL) && (rdev->role == RPMSG_MASTER)) { + rp_chnl = (struct rpmsg_channel *) chnl_hd->data; + if (rp_chnl->state == RPMSG_CHNL_STATE_IDLE) { + if (rdev->support_ns) { + rp_chnl->state = RPMSG_CHNL_STATE_NS; + rpmsg_send_ns_message(rdev, rp_chnl, RPMSG_NS_CREATE); + } else { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + } + return; + } + } + + env_lock_mutex(rdev->lock); + + /* Process the received data from remote node */ + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); + + env_unlock_mutex(rdev->lock); + + while(rp_hdr) { + + /* Get the channel node from the remote device channels list. */ + node = rpmsg_rdev_get_endpoint_from_addr(rdev, rp_hdr->dst); /*in the "rp_endpoints" list, find the node whose addr equal"rp_hdr->dst"*/ + + if (!node) + /* Fatal error no endpoint for the given dst addr. */ + return; + + rp_ept = (struct rpmsg_endpoint *) node->data; + + rp_chnl = rp_ept->rp_chnl; + + /* + * Linux will not send the null message, so the first message not only + * update the state machine, and the callback is called as well + */ + if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) { + /* First message from RPMSG Master, update channel + * destination address and state */ + /* + * Only for Remote + */ + rp_chnl->dst = rp_hdr->src; + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + + /* Notify channel creation to application */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); /* assigned by rpmsg_rdev_init*/ + } + } + + rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv, // for NS message, this will triggle rpmsg_ns_callback /*not the case*/ + rp_hdr->src); // for none NS message, this will triggle APP registered callback "rpmsg_read_cb" + + env_lock_mutex(rdev->lock); + + /* Return used buffers. */ + rpmsg_return_buffer(rdev, rp_hdr, len, idx); + + rp_hdr = (struct rpmsg_hdr *) rpmsg_get_rx_buffer(rdev, &len, &idx); + env_unlock_mutex(rdev->lock); + } +} + +/** + * rpmsg_ns_callback + * + * This callback handles name service announcement from the remote device + * and creates/deletes rpmsg channels. + * + * @param server_chnl - pointer to server channel control block. + * @param data - pointer to received messages + * @param len - length of received data + * @param priv - any private data + * @param src - source address + * + * @return - none + */ +void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, void *data, int len, + void *priv, unsigned long src) { + struct remote_device *rdev; + struct rpmsg_channel *rp_chnl; + struct rpmsg_ns_msg *ns_msg; + struct llist *node; + + rdev = (struct remote_device *) priv; + + //FIXME: This assumes same name string size for channel name both on master + //and remote. If this is not the case then we will have to parse the + //message contents. + + ns_msg = (struct rpmsg_ns_msg *) data; + ns_msg->name[len - 1] = '\0'; + + if (ns_msg->flags & RPMSG_NS_DESTROY) { + node = rpmsg_rdev_get_chnl_node_from_id(rdev, ns_msg->name); + if (node) { + rp_chnl = (struct rpmsg_channel *) node->data; + if (rdev->channel_destroyed) { + rdev->channel_destroyed(rp_chnl); + } + rpmsg_destroy_ept(rp_chnl->rp_ept); + _rpmsg_delete_channel(rp_chnl); + } + } else { + /*RPMSG_NS_CREATE*/ + rp_chnl = _rpmsg_create_channel(rdev, ns_msg->name, 0x00, ns_msg->addr); + if (rp_chnl) { + rp_chnl->state = RPMSG_CHNL_STATE_ACTIVE; + /* Create default endpoint for channel */ + rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev, + RPMSG_ADDR_ANY); + if (rp_chnl->rp_ept) { + rp_chnl->src = rp_chnl->rp_ept->addr; + /* + * Echo back the NS message to remote in order to + * complete the connection stage. Remote will know the endpoint + * address from this point onward which will enable it to send + * message without waiting for any application level message from + * master. + */ + rpmsg_send(rp_chnl,data,len); /*Is this necessary? Infinite Echo Back ? */ + if (rdev->channel_created) { + rdev->channel_created(rp_chnl); + } + } + } + } +} + +/** + * rpmsg_get_address + * + * This function provides unique 32 bit address. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * + * return - a unique address + */ +int rpmsg_get_address(unsigned long *bitmap, int size) { + int addr = -1; + int i, tmp32; + + /* Find first available buffer */ + for (i = 0; i < size; i++) { + tmp32 = get_first_zero_bit(bitmap[i]); + + if (tmp32 < 32) { + addr = tmp32 + i + 1; /*This is strange*/ + bitmap[i] |= (1 << tmp32); + break; + } + } + + return addr; +} + +/** + * rpmsg_release_address + * + * Frees the given address. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - none + */ +int rpmsg_release_address(unsigned long *bitmap, int size, int addr) { + unsigned int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the addr as available */ + i = addr / 32; + j = addr % 32; + + mask = mask << j; + bitmap[i] = bitmap[i] & (~mask); + + return RPMSG_SUCCESS; +} + +/** + * rpmsg_is_address_set + * + * Checks whether address is used or free. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - TRUE/FALSE + */ +int rpmsg_is_address_set(unsigned long *bitmap, int size, + int addr) { + int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the id as available */ + i = addr / 32; + j = addr % 32; + mask = mask << j; + + return (bitmap[i] & mask); +} + +/** + * rpmsg_set_address + * + * Marks the address as consumed. + * + * @param bitmap - bit map for addresses + * @param size - size of bitmap + * @param addr - address to free + * + * return - none + */ +int rpmsg_set_address(unsigned long *bitmap, int size, int addr) { + int i, j; + unsigned long mask = 1; + + if (addr >= size * 32) + return -1; + + /* Mark the id as available */ + i = addr / 32; + j = addr % 32; + mask = mask << j; + bitmap[i] |= mask; + + return RPMSG_SUCCESS; +} diff --git a/middleware/multicore/open-amp/rpmsg/rpmsg_core.h b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h new file mode 100644 index 0000000..e70fd5c --- /dev/null +++ b/middleware/multicore/open-amp/rpmsg/rpmsg_core.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Mentor Graphics Corporation nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef _RPMSG_CORE_H_ +#define _RPMSG_CORE_H_ + +#include "../porting/env/env.h" +#include "../virtio/virtio.h" +#include "../common/hil/hil.h" +#include "../common/shm/sh_mem.h" +#include "../common/llist/llist.h" +#include "rpmsg.h" + +/* Configurable parameters */ +#define RPMSG_BUFFER_SIZE 512 +#define RPMSG_MAX_VQ_PER_RDEV 2 +#define RPMSG_NS_EPT_ADDR 0x35 +#define RPMSG_ADDR_BMP_SIZE 4 + +/* Definitions for device types , null pointer, etc.*/ +#define RPMSG_SUCCESS 0 +#define RPMSG_NULL (void *)0 +#define RPMSG_REMOTE 0 +#define RPMSG_MASTER 1 +#define RPMSG_TRUE 1 +#define RPMSG_FALSE 0 + +/* RPMSG channel states. */ +#define RPMSG_CHNL_STATE_IDLE 0 +#define RPMSG_CHNL_STATE_NS 1 +#define RPMSG_CHNL_STATE_ACTIVE 2 + +/* Remote processor/device states. */ +#define RPMSG_DEV_STATE_IDLE 0 +#define RPMSG_DEV_STATE_ACTIVE 1 + +/* Total tick count for 15secs - 1msec tick. */ +#define RPMSG_TICK_COUNT 15000 + +/* Time to wait - In multiple of 10 msecs. */ +#define RPMSG_TICKS_PER_INTERVAL 10 + +/* Error macros. */ +#define RPMSG_ERRORS_BASE -3000 +#define RPMSG_ERR_NO_MEM (RPMSG_ERRORS_BASE - 1) +#define RPMSG_ERR_NO_BUFF (RPMSG_ERRORS_BASE - 2) +#define RPMSG_ERR_MAX_VQ (RPMSG_ERRORS_BASE - 3) +#define RPMSG_ERR_PARAM (RPMSG_ERRORS_BASE - 4) +#define RPMSG_ERR_DEV_STATE (RPMSG_ERRORS_BASE - 5) +#define RPMSG_ERR_BUFF_SIZE (RPMSG_ERRORS_BASE - 6) +#define RPMSG_ERR_DEV_ID (RPMSG_ERRORS_BASE - 7) +#define RPMSG_ERR_DEV_ADDR (RPMSG_ERRORS_BASE - 8) + +struct rpmsg_channel; +typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, unsigned long); +typedef void (*rpmsg_chnl_cb_t)(struct rpmsg_channel *rp_chl); +/** + * remote_device + * + * This structure is maintained by RPMSG driver to represent remote device/core. + * + * @virtd_dev - virtio device for remote core + * @rvq - Rx virtqueue for virtio device + * @tvq - Tx virtqueue for virtio device + * @proc - reference to remote processor + * @rp_channels - rpmsg channels list for the device + * @rp_endpoints - rpmsg endpoints list for the device + * @mem_pool - shared memory pool + * @bitmap - bitmap for channels addresses + * @channel_created - create channel callback + * @channel_destroyed - delete channel callback + * @default_cb - default callback handler for RX data on channel + * @lock - remote device mutex + * @role - role of the remote device, RPMSG_MASTER/RPMSG_REMOTE + * @state - remote device state, IDLE/ACTIVE + * @support_ns - if device supports name service announcement + * + */ +struct remote_device { + struct virtio_device virt_dev; + struct virtqueue *rvq; + struct virtqueue *tvq; + struct hil_proc *proc; + struct llist *rp_channels; + struct llist *rp_endpoints; + struct sh_mem_pool *mem_pool; + unsigned long bitmap[RPMSG_ADDR_BMP_SIZE]; + rpmsg_chnl_cb_t channel_created; + rpmsg_chnl_cb_t channel_destroyed; + rpmsg_rx_cb_t default_cb; + LOCK *lock; + unsigned int role; + unsigned int state; + int support_ns; +}; + +/* Core functions */ +int rpmsg_start_ipc(struct remote_device *rdev); +struct rpmsg_channel *_rpmsg_create_channel(struct remote_device *rdev, + char *name, unsigned long src, unsigned long dst); +void _rpmsg_delete_channel(struct rpmsg_channel * rp_chnl); +struct rpmsg_endpoint *_create_endpoint(struct remote_device *rdev, + rpmsg_rx_cb_t cb, void *priv, unsigned long addr); +void _destroy_endpoint(struct remote_device *rdev, + struct rpmsg_endpoint *rp_ept); +void rpmsg_send_ns_message(struct remote_device *rdev, + struct rpmsg_channel *rp_chnl, unsigned long flags); +int rpmsg_enqueue_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx); +void rpmsg_return_buffer(struct remote_device *rdev, void *buffer, + unsigned long len, unsigned short idx); +void *rpmsg_get_tx_buffer(struct remote_device *rdev, int *len, + unsigned short *idx); +void rpmsg_free_buffer(struct remote_device *rdev, void *buffer); +void rpmsg_free_channel(struct rpmsg_channel* rp_chnl); +void * rpmsg_get_rx_buffer(struct remote_device *rdev, unsigned long *len, + unsigned short *idx); +int rpmsg_get_address(unsigned long *bitmap, int size); +int rpmsg_release_address(unsigned long *bitmap, int size, int addr); +int rpmsg_is_address_set(unsigned long *bitmap, int size, + int addr); +int rpmsg_set_address(unsigned long *bitmap, int size, int addr); +void rpmsg_ns_callback(struct rpmsg_channel *server_chnl, + void *data, int len, void *priv, unsigned long src); + +/* Remote device functions */ +int rpmsg_rdev_init(struct remote_device **rdev, int dev_id, int role, + rpmsg_chnl_cb_t channel_created, + rpmsg_chnl_cb_t channel_destroyed, + rpmsg_rx_cb_t default_cb); +void rpmsg_rdev_deinit(struct remote_device *rdev); +struct llist *rpmsg_rdev_get_chnl_node_from_id(struct remote_device *rdev, + char *rp_chnl_id); +struct llist *rpmsg_rdev_get_chnl_from_addr(struct remote_device *rdev, + unsigned long addr); +struct llist *rpmsg_rdev_get_endpoint_from_addr(struct remote_device *rdev, + unsigned long addr); +int rpmsg_rdev_notify(struct remote_device *rdev); +int rpmsg_rdev_create_virtqueues(struct virtio_device *dev, int flags, int nvqs, + const char *names[], vq_callback *callbacks[], + struct virtqueue *vqs[]); +unsigned char rpmsg_rdev_get_status(struct virtio_device *dev); + +void rpmsg_rdev_set_status(struct virtio_device *dev, unsigned char status); + +uint32_t rpmsg_rdev_get_feature(struct virtio_device *dev); + +void rpmsg_rdev_set_feature(struct virtio_device *dev, uint32_t feature); + +uint32_t rpmsg_rdev_negotiate_feature(struct virtio_device *dev, + uint32_t features); +/* + * Read/write a variable amount from the device specific (ie, network) + * configuration region. This region is encoded in the same endian as + * the guest. + */ +void rpmsg_rdev_read_config(struct virtio_device *dev, uint32_t offset, + void *dst, int length); +void rpmsg_rdev_write_config(struct virtio_device *dev, uint32_t offset, + void *src, int length); +void rpmsg_rdev_reset(struct virtio_device *dev); + +#endif /* _RPMSG_CORE_H_ */ diff --git a/middleware/multicore/open-amp/virtio/virtio.c b/middleware/multicore/open-amp/virtio/virtio.c new file mode 100644 index 0000000..b2de79e --- /dev/null +++ b/middleware/multicore/open-amp/virtio/virtio.c @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "virtio.h" + +static const char *virtio_feature_name(unsigned long feature, struct virtio_feature_desc *); + +//TODO : This structure may change depending on the types of devices we support. +static struct virtio_ident { + unsigned short devid; + const char *name; +} virtio_ident_table[] = { + { VIRTIO_ID_NETWORK, "Network" }, + { VIRTIO_ID_BLOCK, "Block" }, + { VIRTIO_ID_CONSOLE, "Console" }, + { VIRTIO_ID_ENTROPY, "Entropy" }, + { VIRTIO_ID_BALLOON, "Balloon" }, + { VIRTIO_ID_IOMEMORY, "IOMemory" }, + { VIRTIO_ID_SCSI, "SCSI" }, + { VIRTIO_ID_9P, "9P Transport" }, + + { 0, NULL } +}; + +/* Device independent features. */ +static struct virtio_feature_desc virtio_common_feature_desc[] = { + { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" }, + { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" }, + { VIRTIO_RING_F_EVENT_IDX, "EventIdx" }, + { VIRTIO_F_BAD_FEATURE, "BadFeature" }, + + { 0, NULL } +}; + +const char * +virtio_dev_name(unsigned short devid) +{ + struct virtio_ident *ident; + + for (ident = virtio_ident_table; ident->name != NULL; ident++) { + if (ident->devid == devid) + return (ident->name); + } + + return (NULL); +} + +static const char * +virtio_feature_name(unsigned long val, struct virtio_feature_desc *desc) +{ + int i, j; + struct virtio_feature_desc *descs[2] = { desc, + virtio_common_feature_desc }; + + for (i = 0; i < 2; i++) { + if (descs[i] == NULL) + continue; + + for (j = 0; descs[i][j].vfd_val != 0; j++) { + if (val == descs[i][j].vfd_val) + return (descs[i][j].vfd_str); + } + } + + return (NULL); +} + +void virtio_describe(struct virtio_device *dev, const char *msg, + uint32_t features, struct virtio_feature_desc *desc) +{ + // TODO: Not used currently - keeping it for future use + virtio_feature_name(0,desc); +} + diff --git a/middleware/multicore/open-amp/virtio/virtio.h b/middleware/multicore/open-amp/virtio/virtio.h new file mode 100644 index 0000000..ab11dcd --- /dev/null +++ b/middleware/multicore/open-amp/virtio/virtio.h @@ -0,0 +1,151 @@ +/*- + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_H_ +#define _VIRTIO_H_ + +#include "virtqueue.h" + +/* VirtIO device IDs. */ +#define VIRTIO_ID_NETWORK 0x01 +#define VIRTIO_ID_BLOCK 0x02 +#define VIRTIO_ID_CONSOLE 0x03 +#define VIRTIO_ID_ENTROPY 0x04 +#define VIRTIO_ID_BALLOON 0x05 +#define VIRTIO_ID_IOMEMORY 0x06 +#define VIRTIO_ID_RPMSG 0x07 /* virtio remote remote_proc messaging */ +#define VIRTIO_ID_SCSI 0x08 +#define VIRTIO_ID_9P 0x09 + +/* Status byte for guest to report progress. */ +#define VIRTIO_CONFIG_STATUS_RESET 0x00 +#define VIRTIO_CONFIG_STATUS_ACK 0x01 +#define VIRTIO_CONFIG_STATUS_DRIVER 0x02 +#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04 +#define VIRTIO_CONFIG_STATUS_FAILED 0x80 + +/* + * Generate interrupt when the virtqueue ring is + * completely used, even if we've suppressed them. + */ +#define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24) + +/* + * The guest should never negotiate this feature; it + * is used to detect faulty drivers. + */ +#define VIRTIO_F_BAD_FEATURE (1 << 30) + +/* + * Some VirtIO feature bits (currently bits 28 through 31) are + * reserved for the transport being used (eg. virtio_ring), the + * rest are per-device feature bits. + */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + +typedef struct _virtio_dispatch_ virtio_dispatch; + +struct virtio_feature_desc { + uint32_t vfd_val; + const char *vfd_str; +}; + +/* + * Structure definition for virtio devices for use by the + * applications/drivers + * + */ + +struct virtio_device { + /* + * Since there is no generic device structure so + * keep its type as void. The driver layer will take + * care of it. + */ + void *device; + + /* Device name */ + char *name; + + /* List of virtqueues encapsulated by virtio device. */ + //TODO : Need to implement a list service for ipc stack. + void *vq_list; + + /* Virtio device specific features */ + uint32_t features; + + /* Virtio dispatch table */ + virtio_dispatch *func; + + /* + * Pointer to hold some private data, useful + * in callbacks. + */ + void *data; +}; + +/* + * Helper functions. + */ +const char *virtio_dev_name(uint16_t devid); +void virtio_describe(struct virtio_device *dev, const char *msg, + uint32_t features, struct virtio_feature_desc *feature_desc); + +/* + * Functions for virtio device configuration as defined in Rusty Russell's paper. + * Drivers are expected to implement these functions in their respective codes. + * + */ + +struct _virtio_dispatch_ { + int (*create_virtqueues)(struct virtio_device *dev, int flags, int nvqs, + const char *names[], vq_callback *callbacks[], + struct virtqueue *vqs[]); + uint8_t (*get_status)(struct virtio_device *dev); + void (*set_status)(struct virtio_device *dev, uint8_t status); + uint32_t (*get_features)(struct virtio_device *dev); + void (*set_features)(struct virtio_device *dev, uint32_t feature); + uint32_t (*negotiate_features)(struct virtio_device *dev, uint32_t features); + + /* + * Read/write a variable amount from the device specific (ie, network) + * configuration region. This region is encoded in the same endian as + * the guest. + */ + void (*read_config)(struct virtio_device *dev, uint32_t offset, void *dst, + int length); + void (*write_config)(struct virtio_device *dev, uint32_t offset, void *src, + int length); + void (*reset_device)(struct virtio_device *dev); + +}; + +#endif /* _VIRTIO_H_ */ diff --git a/middleware/multicore/open-amp/virtio/virtio_ring.h b/middleware/multicore/open-amp/virtio/virtio_ring.h new file mode 100644 index 0000000..5bea08b --- /dev/null +++ b/middleware/multicore/open-amp/virtio/virtio_ring.h @@ -0,0 +1,165 @@ +/*- + * Copyright Rusty Russell IBM Corporation 2007. + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef VIRTIO_RING_H +#define VIRTIO_RING_H + +/* This marks a buffer as continuing via the next field. */ +#define VRING_DESC_F_NEXT 1 +/* This marks a buffer as write-only (otherwise read-only). */ +#define VRING_DESC_F_WRITE 2 +/* This means the buffer contains a list of buffer descriptors. */ +#define VRING_DESC_F_INDIRECT 4 /*not used?*/ + +/* The Host uses this in used->flags to advise the Guest: don't kick me + * when you add a buffer. It's unreliable, so it's simply an + * optimization. Guest will still kick if it's out of buffers. */ +#define VRING_USED_F_NO_NOTIFY 1 +/* The Guest uses this in avail->flags to advise the Host: don't + * interrupt me when you consume a buffer. It's unreliable, so it's + * simply an optimization. */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* VirtIO ring descriptors: 16 bytes. + * These can chain together via "next". */ +struct vring_desc { + /* Address (guest-physical). */ + uint64_t addr; + /* Length. */ + uint32_t len; + /* The flags as indicated above. */ + uint16_t flags; + /* We chain unused descriptors via this, too. */ + uint16_t next; +}; + +struct vring_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[0]; +}; + +/* uint32_t is used here for ids for padding reasons. */ +struct vring_used_elem { + /* Index of start of used descriptor chain. */ + uint32_t id; + /* Total length of the descriptor chain which was written to. */ + uint32_t len; +}; + +struct vring_used { + uint16_t flags; + uint16_t idx; + struct vring_used_elem ring[0]; +}; + +struct vring { + unsigned int num; + + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +/* The standard layout for the ring is a continuous chunk of memory which + * looks like this. We assume num is a power of 2. + * + * struct vring { + * // The actual descriptors (16 bytes each) + * struct vring_desc desc[num]; + * + * // A ring of available descriptor heads with free-running index. + * __u16 avail_flags; + * __u16 avail_idx; + * __u16 available[num]; + * __u16 used_event_idx; + * + * // Padding to the next align boundary. + * char pad[]; + * + * // A ring of used descriptor heads with free-running index. + * __u16 used_flags; + * __u16 used_idx; + * struct vring_used_elem used[num]; + * __u16 avail_event_idx; + * }; + * + * NOTE: for VirtIO PCI, align is 4096. + */ + +/* + * We publish the used event index at the end of the available ring, and vice + * versa. They are at the end for backwards compatibility. + */ +#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) +#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num]) + +static inline int +vring_size(unsigned int num, unsigned long align) +{ + int size; + + size = num * sizeof(struct vring_desc); + size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) + + sizeof(uint16_t); + size = (size + align - 1) & ~(align - 1); + size += sizeof(struct vring_used) + + (num * sizeof(struct vring_used_elem)) + sizeof(uint16_t); + return (size); +} + +static inline void +vring_init(struct vring *vr, unsigned int num, uint8_t *p, + unsigned long align) +{ + vr->num = num; + vr->desc = (struct vring_desc *) p; + vr->avail = (struct vring_avail *) (p + + num * sizeof(struct vring_desc)); + vr->used = (void *) + (((unsigned long) &vr->avail->ring[num] + align-1) & ~(align-1)); +} + +/* + * The following is used with VIRTIO_RING_F_EVENT_IDX. + * + * Assuming a given event_idx value from the other size, if we have + * just incremented index from old to new_idx, should we trigger an + * event? + */ +static inline int +vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old) +{ + + return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old); +} +#endif /* VIRTIO_RING_H */ diff --git a/middleware/multicore/open-amp/virtio/virtqueue.c b/middleware/multicore/open-amp/virtio/virtqueue.c new file mode 100644 index 0000000..2ea87cd --- /dev/null +++ b/middleware/multicore/open-amp/virtio/virtqueue.c @@ -0,0 +1,693 @@ +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "virtqueue.h" + +/* Prototype for internal functions. */ +static void vq_ring_init(struct virtqueue *); +static void vq_ring_update_avail(struct virtqueue *, uint16_t); +static uint16_t vq_ring_add_buffer(struct virtqueue *, struct vring_desc *, + uint16_t, struct llist *, int, int); +static int vq_ring_enable_interrupt(struct virtqueue *, uint16_t); +static void vq_ring_free_chain(struct virtqueue *, uint16_t); +static int vq_ring_must_notify_host(struct virtqueue *vq); +static void vq_ring_notify_host(struct virtqueue *vq); +static int virtqueue_nused(struct virtqueue *vq); + +/** + * virtqueue_create - Creates new VirtIO queue + * + * @param device - Pointer to VirtIO device + * @param id - VirtIO queue ID , must be unique + * @param name - Name of VirtIO queue + * @param ring - Pointer to vring_alloc_info control block + * @param callback - Pointer to callback function, invoked + * when message is available on VirtIO queue + * @param notify - Pointer to notify function, used to notify + * other side that there is job available for it + * @param v_queue - Created VirtIO queue. + * + * @return - Function status + */ +int virtqueue_create(struct virtio_device *virt_dev, unsigned short id, char *name, + struct vring_alloc_info *ring, void (*callback)(struct virtqueue *vq), + void (*notify)(struct virtqueue *vq), + struct virtqueue **v_queue) { + + struct virtqueue *vq = VQ_NULL; + int status = VQUEUE_SUCCESS; + uint32_t vq_size = 0; + + VQ_PARAM_CHK(ring == VQ_NULL, status, ERROR_VQUEUE_INVLD_PARAM); + VQ_PARAM_CHK(ring->num_descs == 0, status, ERROR_VQUEUE_INVLD_PARAM); + VQ_PARAM_CHK(ring->num_descs & (ring->num_descs - 1), status, + ERROR_VRING_ALIGN); + + //TODO : Error check for indirect buffer addition + + if (status == VQUEUE_SUCCESS) { + + vq_size = sizeof(struct virtqueue) + + (ring->num_descs) * sizeof(struct vq_desc_extra); + vq = (struct virtqueue *) env_allocate_memory(vq_size); + + + if (vq == VQ_NULL) { + return (ERROR_NO_MEM); + } + + env_memset(vq, 0x00, vq_size); + + + vq->vq_dev = virt_dev; + env_strncpy(vq->vq_name, name, VIRTQUEUE_MAX_NAME_SZ); + vq->vq_queue_index = id; + vq->vq_alignment = ring->align; /*ring info comes from proc_table*/ + vq->vq_nentries = ring->num_descs; + vq->vq_free_cnt = vq->vq_nentries; + vq->callback = callback; /*rpmsg_tx_callback, rpmsg_rx_callback*/ + vq->notify = notify; /*hil_vring_notify*/ + + //TODO : Whether we want to support indirect addition or not. + vq->vq_ring_size = vring_size(ring->num_descs, ring->align); + vq->vq_ring_mem = (void *) ring->phy_addr; + + /* Initialize vring control block in virtqueue. */ + vq_ring_init(vq); + + /* Disable callbacks - will be enabled by the application + * once initialization is completed. + */ + virtqueue_disable_cb(vq); + + *v_queue = vq; + + //TODO : Need to add cleanup in case of error used with the indirect buffer addition + //TODO: do we need to save the new queue in db based on its id + } + + return (status); +} + +/** + * virtqueue_add_buffer() - Enqueues new buffer in vring for consumption + * by other side. Readable buffers are always + * inserted before writable buffers + * + * @param vq - Pointer to VirtIO queue control block. + * @param buffer - Pointer to buffer list + * @param readable - Number of readable buffers + * @param writable - Number of writable buffers + * @param cookie - Pointer to hold call back data + * + * @return - Function status + */ +int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer, + int readable, int writable, void *cookie) { + + struct vq_desc_extra *dxp = VQ_NULL; + int status = VQUEUE_SUCCESS; + uint16_t head_idx; + uint16_t idx; + int needed; + + needed = readable + writable; /*only one can be set to 1*/ + + VQ_PARAM_CHK(vq == VQ_NULL, status, ERROR_VQUEUE_INVLD_PARAM); + VQ_PARAM_CHK(needed < 1, status, ERROR_VQUEUE_INVLD_PARAM); + VQ_PARAM_CHK(vq->vq_free_cnt == 0, status, ERROR_VRING_FULL); + + //TODO: Add parameters validation for indirect buffer addition + + VQUEUE_BUSY(vq); + + if (status == VQUEUE_SUCCESS) { + + //TODO : Indirect buffer addition support + + VQASSERT(vq, cookie != VQ_NULL, "enqueuing with no cookie"); + + head_idx = vq->vq_desc_head_idx; + VQ_RING_ASSERT_VALID_IDX(vq, head_idx); + dxp = &vq->vq_descx[head_idx]; + + VQASSERT(vq, (dxp->cookie == VQ_NULL), "cookie already exists for index"); + + dxp->cookie = cookie; + dxp->ndescs = needed; + + /* Enqueue buffer onto the ring. */ + idx = vq_ring_add_buffer(vq, vq->vq_ring.desc, head_idx, buffer, /*idx now "desc.next"*/ + readable, writable); + + vq->vq_desc_head_idx = idx; + vq->vq_free_cnt -= needed; + + if (vq->vq_free_cnt == 0) + VQ_RING_ASSERT_CHAIN_TERM(vq); + else + VQ_RING_ASSERT_VALID_IDX(vq, idx); + + /* + * Update vring_avail control block fields so that other + * side can get buffer using it. + */ + vq_ring_update_avail(vq, head_idx); + } + + VQUEUE_IDLE(vq); + + return (status); +} + +/** + * virtqueue_add_single_buffer - Enqueues single buffer in vring + * + * @param vq - Pointer to VirtIO queue control block + * @param cookie - Pointer to hold call back data + * @param buffer_addr - Address of buffer + * @param len - Length of buffer + * @param writable - If buffer writable + * @param has_next - If buffers for subsequent call are + * to be chained + * + * @return - Function status + */ +int virtqueue_add_single_buffer(struct virtqueue *vq, void *cookie, /*this function is not used at all*/ + void *buffer_addr, uint_t len, int writable, boolean has_next) { + + struct vq_desc_extra *dxp; + struct vring_desc *dp; + uint16_t head_idx; + uint16_t idx; + int status = VQUEUE_SUCCESS; + + VQ_PARAM_CHK(vq == VQ_NULL, status, ERROR_VQUEUE_INVLD_PARAM); + VQ_PARAM_CHK(vq->vq_free_cnt == 0, status, ERROR_VRING_FULL); + + VQUEUE_BUSY(vq); + + if (status == VQUEUE_SUCCESS) { + + VQASSERT(vq, cookie != VQ_NULL, "enqueuing with no cookie"); + + head_idx = vq->vq_desc_head_idx; + dxp = &vq->vq_descx[head_idx]; + + dxp->cookie = cookie; + dxp->ndescs = 1; + idx = head_idx; + + dp = &vq->vq_ring.desc[idx]; + dp->addr = env_map_vatopa(buffer_addr); + dp->len = len; + dp->flags = 0; + idx = dp->next; + + if (has_next) + dp->flags |= VRING_DESC_F_NEXT; + if (writable) + dp->flags |= VRING_DESC_F_WRITE; + + vq->vq_desc_head_idx = idx; + vq->vq_free_cnt--; + + if (vq->vq_free_cnt == 0) + VQ_RING_ASSERT_CHAIN_TERM(vq); + else + VQ_RING_ASSERT_VALID_IDX(vq, idx); + + vq_ring_update_avail(vq, head_idx); + } + + VQUEUE_IDLE(vq); + + return (status); +} + +/** + * virtqueue_get_buffer - Returns used buffers from VirtIO queue + * + * @param vq - Pointer to VirtIO queue control block + * @param len - Length of conumed buffer + * + * @return - Pointer to used buffer + */ +void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len) { + struct vring_used_elem *uep; + void *cookie; + uint16_t used_idx, desc_idx; + + if ((vq == VQ_NULL) || (vq->vq_used_cons_idx == vq->vq_ring.used->idx)) + return (VQ_NULL); + + VQUEUE_BUSY(vq); + + used_idx = vq->vq_used_cons_idx++ & (vq->vq_nentries - 1); + uep = &vq->vq_ring.used->ring[used_idx]; + + env_rmb(); + + desc_idx = (uint16_t) uep->id; + if (len != VQ_NULL) + *len = uep->len; + + vq_ring_free_chain(vq, desc_idx); + + cookie = vq->vq_descx[desc_idx].cookie; + vq->vq_descx[desc_idx].cookie = VQ_NULL; + + VQUEUE_IDLE(vq); + + return (cookie); +} + +/** + * virtqueue_free - Frees VirtIO queue resources + * + * @param vq - Pointer to VirtIO queue control block + * + */ +void virtqueue_free(struct virtqueue *vq) { + + if (vq != VQ_NULL) { + + if (vq->vq_free_cnt != vq->vq_nentries) { + env_print("\r\nWARNING %s: freeing non-empty virtqueue\r\n", vq->vq_name); + } + + //TODO : Need to free indirect buffers here + + if (vq->vq_ring_mem != VQ_NULL) { + vq->vq_ring_size = 0; + vq->vq_ring_mem = VQ_NULL; + } + + env_free_memory(vq); + } +} + +/** + * virtqueue_get_available_buffer - Returns buffer available for use in the + * VirtIO queue + * + * @param vq - Pointer to VirtIO queue control block + * @param avail_idx - Pointer to index used in vring desc table + * @param len - Length of buffer + * + * @return - Pointer to available buffer + */ +void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx, + uint32_t *len) { + + uint16_t head_idx = 0; + void *buffer; + + if (vq->vq_available_idx == vq->vq_ring.avail->idx) { /*vq->vq_ring.avial->idx is updated in "rpmsg_rdev_create_virtqueues" "virtqueue_add_buffer" */ + return (VQ_NULL); + } + + VQUEUE_BUSY(vq); + + head_idx = vq->vq_available_idx++ & (vq->vq_nentries - 1); + *avail_idx = vq->vq_ring.avail->ring[head_idx]; + + env_rmb(); + + buffer = env_map_patova(vq->vq_ring.desc[*avail_idx].addr); + *len = vq->vq_ring.desc[*avail_idx].len; + + VQUEUE_IDLE(vq); + + return (buffer); +} + +/** + * virtqueue_add_consumed_buffer - Returns consumed buffer back to VirtIO queue + * + * @param vq - Pointer to VirtIO queue control block + * @param head_idx - Index of vring desc containing used buffer + * @param len - Length of buffer + * + * @return - Function status + */ +int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx, + uint_t len) { + + struct vring_used_elem *used_desc = VQ_NULL; + uint16_t used_idx; + + if ((head_idx > vq->vq_nentries) || (head_idx < 0)) { + return (ERROR_VRING_NO_BUFF); + } + + VQUEUE_BUSY(vq); + + used_idx = vq->vq_ring.used->idx & (vq->vq_nentries - 1); + used_desc = &(vq->vq_ring.used->ring[used_idx]); + used_desc->id = head_idx; + used_desc->len = len; + + env_wmb(); + + vq->vq_ring.used->idx++; + + VQUEUE_IDLE(vq); + + return (VQUEUE_SUCCESS); +} + +/** + * virtqueue_enable_cb - Enables callback generation + * + * @param vq - Pointer to VirtIO queue control block + * + * @return - Function status + */ +int virtqueue_enable_cb(struct virtqueue *vq) { + + return (vq_ring_enable_interrupt(vq, 0)); +} + +/** + * virtqueue_enable_cb - Disables callback generation + * + * @param vq - Pointer to VirtIO queue control block + * + */ +void virtqueue_disable_cb(struct virtqueue *vq) { + + VQUEUE_BUSY(vq); + + if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) { + vring_used_event(&vq->vq_ring)= vq->vq_used_cons_idx - vq->vq_nentries + - 1; + } else { + vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + } + + VQUEUE_IDLE(vq); +} + +/** + * virtqueue_kick - Notifies other side that there is buffer available for it. + * + * @param vq - Pointer to VirtIO queue control block + */ +void virtqueue_kick(struct virtqueue *vq) { + + VQUEUE_BUSY(vq); + + /* Ensure updated avail->idx is visible to host. */ + env_mb(); + + if (vq_ring_must_notify_host(vq)) + vq_ring_notify_host(vq); + + vq->vq_queued_cnt = 0; + + VQUEUE_IDLE(vq); +} + +/** + * virtqueue_dump Dumps important virtqueue fields , use for debugging purposes + * + * @param vq - Pointer to VirtIO queue control block + */ +void virtqueue_dump(struct virtqueue *vq) { + + if (vq == VQ_NULL) + return; + + env_print("VQ: %s - size=%d; free=%d; used=%d; queued=%d; " + "desc_head_idx=%d; avail.idx=%d; used_cons_idx=%d; " + "used.idx=%d; avail.flags=0x%x; used.flags=0x%x\r\n", vq->vq_name, + vq->vq_nentries, vq->vq_free_cnt, virtqueue_nused(vq), + vq->vq_queued_cnt, vq->vq_desc_head_idx, vq->vq_ring.avail->idx, + vq->vq_used_cons_idx, vq->vq_ring.used->idx, + vq->vq_ring.avail->flags, vq->vq_ring.used->flags); +} + +/** + * virtqueue_get_desc_size - Returns vring descriptor size + * + * @param vq - Pointer to VirtIO queue control block + * + * @return - Descriptor length + */ +uint32_t virtqueue_get_desc_size(struct virtqueue *vq) { + uint16_t head_idx = 0; + uint16_t avail_idx = 0; + uint32_t len = 0; + + if (vq->vq_available_idx == vq->vq_ring.avail->idx) { + return (VQ_NULL); + } + + VQUEUE_BUSY(vq); + + head_idx = vq->vq_available_idx & (vq->vq_nentries - 1); + avail_idx = vq->vq_ring.avail->ring[head_idx]; + len = vq->vq_ring.desc[avail_idx].len; + + VQUEUE_IDLE(vq); + + return (len); +} +/************************************************************************** + * Helper Functions * + **************************************************************************/ + +/** + * + * vq_ring_add_buffer + * + */ +static uint16_t vq_ring_add_buffer(struct virtqueue *vq, + struct vring_desc *desc, uint16_t head_idx, struct llist *buffer, + int readable, int writable) { + + struct vring_desc *dp; + int i, needed; + uint16_t idx; + + needed = readable + writable; + + for (i = 0, idx = head_idx; (i < needed && buffer != VQ_NULL); + i++, idx = dp->next, buffer = buffer->next) { + + VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END, + "premature end of free desc chain"); + + dp = &desc[idx]; + dp->addr = env_map_vatopa(buffer->data); + dp->len = buffer->attr; + dp->flags = 0; + + if (i < needed - 1) + dp->flags |= VRING_DESC_F_NEXT; + + /* Readable buffers are inserted into vring before the writable buffers.*/ + if (i >= readable) + dp->flags |= VRING_DESC_F_WRITE; + } + + return (idx); +} + +/** + * + * vq_ring_free_chain + * + */ +static void vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx) { + struct vring_desc *dp; + struct vq_desc_extra *dxp; + + VQ_RING_ASSERT_VALID_IDX(vq, desc_idx); + dp = &vq->vq_ring.desc[desc_idx]; + dxp = &vq->vq_descx[desc_idx]; + + if (vq->vq_free_cnt == 0) + VQ_RING_ASSERT_CHAIN_TERM(vq); + + vq->vq_free_cnt += dxp->ndescs; + dxp->ndescs--; + + if ((dp->flags & VRING_DESC_F_INDIRECT) == 0) { + while (dp->flags & VRING_DESC_F_NEXT) { + VQ_RING_ASSERT_VALID_IDX(vq, dp->next); + dp = &vq->vq_ring.desc[dp->next]; + dxp->ndescs--; + } + } + + VQASSERT(vq, (dxp->ndescs == 0), + "failed to free entire desc chain, remaining"); + + /* + * We must append the existing free chain, if any, to the end of + * newly freed chain. If the virtqueue was completely used, then + * head would be VQ_RING_DESC_CHAIN_END (ASSERTed above). + */ + dp->next = vq->vq_desc_head_idx; + vq->vq_desc_head_idx = desc_idx; +} + +/** + * + * vq_ring_init + * + */ +static void vq_ring_init(struct virtqueue *vq) { + struct vring *vr; + unsigned char *ring_mem; + int i, size; + + ring_mem = vq->vq_ring_mem; + size = vq->vq_nentries; + vr = &vq->vq_ring; + + vring_init(vr, size, ring_mem, vq->vq_alignment); + + for (i = 0; i < size - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = VQ_RING_DESC_CHAIN_END; +} + +/** + * + * vq_ring_update_avail + * + */ +static void vq_ring_update_avail(struct virtqueue *vq, uint16_t desc_idx) { + uint16_t avail_idx; + + /* + * Place the head of the descriptor chain into the next slot and make + * it usable to the host. The chain is made available now rather than + * deferring to virtqueue_notify() in the hopes that if the host is + * currently running on another CPU, we can keep it processing the new + * descriptor. + */ + avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1); + vq->vq_ring.avail->ring[avail_idx] = desc_idx; + + env_wmb(); + + vq->vq_ring.avail->idx++; + + /* Keep pending count until virtqueue_notify(). */ + vq->vq_queued_cnt++; +} + +/** + * + * vq_ring_enable_interrupt + * + */ +static int vq_ring_enable_interrupt(struct virtqueue *vq, uint16_t ndesc) { + + /* + * Enable interrupts, making sure we get the latest index of + * what's already been consumed. + */ + if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) { + vring_used_event(&vq->vq_ring)= vq->vq_used_cons_idx + ndesc; + } else { + vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; + } + + env_mb(); + + /* + * Enough items may have already been consumed to meet our threshold + * since we last checked. Let our caller know so it processes the new + * entries. + */ + if (virtqueue_nused(vq) > ndesc) { + return (1); + } + + return (0); +} + +/** + * + * virtqueue_interrupt + * + */ +void virtqueue_notification(struct virtqueue *vq) { + + if (vq->callback != VQ_NULL) + vq->callback(vq); +} + +/** + * + * vq_ring_must_notify_host + * + */ +static int vq_ring_must_notify_host(struct virtqueue *vq) { + uint16_t new_idx, prev_idx, event_idx; + + if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) { + new_idx = vq->vq_ring.avail->idx; + prev_idx = new_idx - vq->vq_queued_cnt; + event_idx = vring_avail_event(&vq->vq_ring); + + return (vring_need_event(event_idx, new_idx, prev_idx) != 0); + } + + return ((vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) == 0); +} + +/** + * + * vq_ring_notify_host + * + */ +static void vq_ring_notify_host(struct virtqueue *vq) { + + if (vq->notify != VQ_NULL) + vq->notify(vq); /*hil_vring_notify*/ +} + +/** + * + * virtqueue_nused + * + */ +static int virtqueue_nused(struct virtqueue *vq) { + uint16_t used_idx, nused; + + used_idx = vq->vq_ring.used->idx; + + nused = (uint16_t) (used_idx - vq->vq_used_cons_idx); + VQASSERT(vq, nused <= vq->vq_nentries, "used more than available"); + + return (nused); +} diff --git a/middleware/multicore/open-amp/virtio/virtqueue.h b/middleware/multicore/open-amp/virtio/virtqueue.h new file mode 100644 index 0000000..4671f6e --- /dev/null +++ b/middleware/multicore/open-amp/virtio/virtqueue.h @@ -0,0 +1,227 @@ +#ifndef VIRTQUEUE_H_ +#define VIRTQUEUE_H_ + +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include + +typedef unsigned int uint_t; +typedef uint8_t boolean; + +#include "virtio_ring.h" +#include "../porting/env/env.h" +#include "../common/llist/llist.h" + +/*Error Codes*/ +#define VQ_ERROR_BASE -3000 +#define ERROR_VRING_FULL (VQ_ERROR_BASE - 1) +#define ERROR_INVLD_DESC_IDX (VQ_ERROR_BASE - 2) +#define ERROR_EMPTY_RING (VQ_ERROR_BASE - 3) +#define ERROR_NO_MEM (VQ_ERROR_BASE - 4) +#define ERROR_VRING_MAX_DESC (VQ_ERROR_BASE - 5) +#define ERROR_VRING_ALIGN (VQ_ERROR_BASE - 6) +#define ERROR_VRING_NO_BUFF (VQ_ERROR_BASE - 7) +#define ERROR_VQUEUE_INVLD_PARAM (VQ_ERROR_BASE - 8) + +#define true 1 +#define false 0 +#define VQUEUE_SUCCESS 0 +#define VQUEUE_DEBUG false + +//TODO: +/* This is temporary macro to replace C NULL support. + * At the moment all the RTL specific functions are present in env. + * */ +#define VQ_NULL 0 + +/* The maximum virtqueue size is 2^15. Use that value as the end of + * descriptor chain terminator since it will never be a valid index + * in the descriptor table. This is used to verify we are correctly + * handling vq_free_cnt. + */ +#define VQ_RING_DESC_CHAIN_END 32768 +#define VIRTQUEUE_FLAG_INDIRECT 0x0001 +#define VIRTQUEUE_FLAG_EVENT_IDX 0x0002 +#define VIRTQUEUE_MAX_NAME_SZ 32 + +/* Support for indirect buffer descriptors. */ +#define VIRTIO_RING_F_INDIRECT_DESC (1 << 28) + +/* Support to suppress interrupt until specific index is reached. */ +#define VIRTIO_RING_F_EVENT_IDX (1 << 29) + +/* + * Hint on how long the next interrupt should be postponed. This is + * only used when the EVENT_IDX feature is negotiated. + */ +typedef enum { + VQ_POSTPONE_SHORT, + VQ_POSTPONE_LONG, + VQ_POSTPONE_EMPTIED /* Until all available desc are used. */ +} vq_postpone_t; + +struct virtqueue { + //TODO: Need to define proper structure for + // virtio device with RPmsg and paravirtualization. + + struct virtio_device *vq_dev; + char vq_name[VIRTQUEUE_MAX_NAME_SZ]; + uint16_t vq_queue_index; + uint16_t vq_nentries; + uint32_t vq_flags; + int vq_alignment; + int vq_ring_size; /*Seems not used at all*/ + boolean vq_inuse; + void *vq_ring_mem; + void (*callback)(struct virtqueue *vq); + void (*notify)(struct virtqueue *vq); + int vq_max_indirect_size; + int vq_indirect_mem_size; + struct vring vq_ring; + uint16_t vq_free_cnt; + uint16_t vq_queued_cnt; + + /* + * Head of the free chain in the descriptor table. If + * there are no free descriptors, this will be set to + * VQ_RING_DESC_CHAIN_END. + */ + uint16_t vq_desc_head_idx; + + /* + * Last consumed descriptor in the used table, + * trails vq_ring.used->idx. + */ + uint16_t vq_used_cons_idx; + + /* + * Last consumed descriptor in the available table - + * used by the consumer side. + */ + uint16_t vq_available_idx; + + uint8_t padd; + + /* + * Used by the host side during callback. Cookie + * holds the address of buffer received from other side. + * Other fields in this structure are not used currently. + */ + + struct vq_desc_extra { + void *cookie; + struct vring_desc *indirect; + uint32_t indirect_paddr; + uint16_t ndescs; + } vq_descx[0]; +}; + +/* struct to hold vring specific information */ +struct vring_alloc_info { + void *phy_addr; + uint32_t align; + uint16_t num_descs; + uint16_t pad; +}; + +typedef void vq_callback(struct virtqueue *); +typedef void vq_notify(struct virtqueue *); + +#if (VQUEUE_DEBUG == true) + +#define VQASSERT(_vq, _exp, _msg) do{ \ + if (!(_exp)){ env_print("%s: %s - "_msg, __func__, (_vq)->vq_name); while(1);} \ + } while(0) + +#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) \ + VQASSERT((_vq), (_idx) < (_vq)->vq_nentries, \ + "invalid ring index") + +#define VQ_RING_ASSERT_CHAIN_TERM(_vq) \ + VQASSERT((_vq), (_vq)->vq_desc_head_idx == \ + VQ_RING_DESC_CHAIN_END, "full ring terminated incorrectly: invalid head") + +#define VQ_PARAM_CHK(condition, status_var, status_err) \ + if ((status_var == 0) && (condition)) \ + { \ + status_var = status_err; \ + } + +#define VQUEUE_BUSY(vq) if ((vq)->vq_inuse == false) \ + (vq)->vq_inuse = true; \ + else \ + VQASSERT(vq, (vq)->vq_inuse == false, \ + "VirtQueue already in use") + +#define VQUEUE_IDLE(vq) ((vq)->vq_inuse = false) + +#else + +#define KASSERT(cond, str) +#define VQASSERT(_vq, _exp, _msg) +#define VQ_RING_ASSERT_VALID_IDX(_vq, _idx) +#define VQ_RING_ASSERT_CHAIN_TERM(_vq) +#define VQ_PARAM_CHK(condition, status_var, status_err) +#define VQUEUE_BUSY(vq) +#define VQUEUE_IDLE(vq) + +#endif + +int virtqueue_create(struct virtio_device *device, unsigned short id, char *name, + struct vring_alloc_info *ring, void (*callback)(struct virtqueue *vq), + void (*notify)(struct virtqueue *vq), struct virtqueue **v_queue); + +int virtqueue_add_buffer(struct virtqueue *vq, struct llist *buffer, + int readable, int writable, void *cookie); + +int virtqueue_add_single_buffer(struct virtqueue *vq, void *cookie, + void* buffer_addr, uint_t len, int writable, boolean has_next); + +void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len); + +void *virtqueue_get_available_buffer(struct virtqueue *vq, uint16_t *avail_idx, + uint32_t *len); + +int virtqueue_add_consumed_buffer(struct virtqueue *vq, uint16_t head_idx, + uint_t len); + +void virtqueue_disable_cb(struct virtqueue *vq); + +int virtqueue_enable_cb(struct virtqueue *vq); + +void virtqueue_kick(struct virtqueue *vq); + +void virtqueue_free(struct virtqueue *vq); + +void virtqueue_dump(struct virtqueue *vq); + +void virtqueue_notification(struct virtqueue *vq); + +uint32_t virtqueue_get_desc_size(struct virtqueue *vq); + +#endif /* VIRTQUEUE_H_ */ -- cgit v1.2.3