lib: added initial version of openamp

This patch contains initial version of xilopenamp library. Currently
the library supports cortex-R5 processor

Signed-off-by: Kinjal Pravinbhai Patel <patelki@xilinx.com>
This commit is contained in:
Kinjal Pravinbhai Patel 2015-03-16 14:06:05 +05:30 committed by Nava kishore Manne
parent dc90d1033a
commit 322504c6c6
35 changed files with 9228 additions and 0 deletions

View file

@ -0,0 +1,51 @@
###############################################################################
#
# Copyright (C) 2015 Xilinx, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# Use of the Software is limited solely to applications:
# (a) running on a Xilinx device, or
# (b) that interact with a Xilinx device through a bus or interconnect.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Except as contained in this notice, the name of the Xilinx shall not be used
# in advertising or otherwise to promote the sale, use or other dealings in
# this Software without prior written authorization from Xilinx.
#
###############################################################################
#
# Modification History
#
# Ver Who Date Changes
# ----- ---- -------- -----------------------------------------------
# 1.00 pkp 12/03/15 Initial Release
#
##############################################################################
OPTION psf_version = 2.1;
BEGIN LIBRARY xilopenamp
OPTION DRC = openamp_drc;
OPTION COPYFILES = all;
OPTION REQUIRES_OS = (standalone);
OPTION APP_LINKER_FLAGS = "-Wl,--start-group,-lxil,-lxilopenamp,-lgcc,-lc,--end-group";
OPTION DESC = "Xilinx openamp Library ";
OPTION VERSION = 1.0;
OPTION NAME = xilopenamp;
END LIBRARY

View file

@ -0,0 +1,86 @@
###############################################################################
#
# Copyright (C) 2015 Xilinx, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# Use of the Software is limited solely to applications:
# (a) running on a Xilinx device, or
# (b) that interact with a Xilinx device through a bus or interconnect.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Except as contained in this notice, the name of the Xilinx shall not be used
# in advertising or otherwise to promote the sale, use or other dealings in
# this Software without prior written authorization from Xilinx.
#
###############################################################################
#
# Modification History
#
# Ver Who Date Changes
# ----- ---- -------- -----------------------------------------------
# 1.00 pkp 12/03/15 Initial Release
##############################################################################
#---------------------------------------------
# openamp_drc
#---------------------------------------------
proc openamp_drc {libhandle} {
}
proc generate {libhandle} {
}
#-------
# post_generate: called after generate called on all libraries
#-------
proc post_generate {libhandle} {
xgen_opts_file $libhandle
}
#-------
# execs_generate: called after BSP's, libraries and drivers have been compiled
# This procedure builds the libxilopenamp.a library
#-------
proc execs_generate {libhandle} {
}
proc xgen_opts_file {libhandle} {
# Copy the include files to the include directory
set srcdir src
set dstdir [file join .. .. include]
# Create dstdir if it does not exist
if { ! [file exists $dstdir] } {
file mkdir $dstdir
}
puts "its openamp"
# Get list of files in the srcdir
set sources [glob -join $srcdir *.h]
# Copy each of the files in the list to dstdir
foreach source $sources {
file copy -force $source $dstdir
}
}

View file

@ -0,0 +1,115 @@
###############################################################################
#
# Copyright (C) 2015 Xilinx, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# Use of the Software is limited solely to applications:
# (a) running on a Xilinx device, or
# (b) that interact with a Xilinx device through a bus or interconnect.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Except as contained in this notice, the name of the Xilinx shall not be used
# in advertising or otherwise to promote the sale, use or other dealings in
# this Software without prior written authorization from Xilinx.
#
###############################################################################
COMPILER=
ARCHIVER=
CP=cp
COMPILER_FLAGS =
EXTRA_ARCHIVE_FLAGS=rc
CC_FLAGS= -mcpu=cortex-r5
RELEASEDIR=../../../lib
#RELEASEDIR= .
INCLUDEDIR=../../../include
INCLUDES=-I./include/ -I${INCLUDEDIR}
OPENAMP_DIR = .
LIB_SRCS = $(OPENAMP_DIR)/config.c \
$(OPENAMP_DIR)/bm_env.c \
$(OPENAMP_DIR)/elf_loader.c \
$(OPENAMP_DIR)/hil.c \
$(OPENAMP_DIR)/llist.c \
$(OPENAMP_DIR)/remote_device.c \
$(OPENAMP_DIR)/remoteproc.c \
$(OPENAMP_DIR)/remoteproc_loader.c \
$(OPENAMP_DIR)/rpmsg.c \
$(OPENAMP_DIR)/rpmsg_core.c \
$(OPENAMP_DIR)/rpmsg_retarget.c\
$(OPENAMP_DIR)/rsc_table_parser.c \
$(OPENAMP_DIR)/sh_mem.c \
$(OPENAMP_DIR)/virtio.c \
$(OPENAMP_DIR)/virtqueue.c
# create SECURE_SRCS based on configured options
OPENAMP_SRCS = $(LIB_SRCS)
OPENAMP_OBJS = $(OPENAMP_SRCS:%.c=%.o)
EXPORT_INCLUDE_FILES = $(OPENAMP_DIR)/config.h \
$(OPENAMP_DIR)/bm_env.h \
$(OPENAMP_DIR)/elf_loader.h \
$(OPENAMP_DIR)/env.h \
$(OPENAMP_DIR)/hil.h \
$(OPENAMP_DIR)/llist.h \
$(OPENAMP_DIR)/open_amp.h \
$(OPENAMP_DIR)/remoteproc.h \
$(OPENAMP_DIR)/remoteproc_loader.h \
$(OPENAMP_DIR)/rpmsg_retarget.h \
$(OPENAMP_DIR)/rpmsg.h \
$(OPENAMP_DIR)/rpmsg_core.h \
$(OPENAMP_DIR)/rsc_table_parser.h \
$(OPENAMP_DIR)/sh_mem.h \
$(OPENAMP_DIR)/virtio.h \
$(OPENAMP_DIR)/virtio_ring.h\
$(OPENAMP_DIR)/virtqueue.h
libs: libxilopenamp.a
cp libxilopenamp.a $(RELEASEDIR)
make clean
include:
@for i in $(EXPORT_INCLUDE_FILES); do \
${CP} -r $$i ${INCLUDEDIR}; \
done
clean:
rm -rf obj/config.o obj/bm_env.o obj/rpmsg_retarget.o obj/elf_loader.o obj/hil.o obj/llist.o obj/remote_device.o obj/remoteproc.o obj/remoteproc_loader.o obj/rpmsg.o obj/rpmsg_core.o obj/rsc_table_parser.o obj/sh_mem.o obj/virtio.o obj/virtqueue.o
rmdir obj
rm libxilopenamp.a
libxilopenamp.a: obj_dir print_msg_secure_base $(OPENAMP_OBJS)
@echo "Creating archive $@"
$(ARCHIVER) $(EXTRA_ARCHIVE_FLAGS) $@ obj/config.o obj/bm_env.o obj/rpmsg_retarget.o obj/elf_loader.o obj/hil.o obj/llist.o obj/remote_device.o obj/remoteproc.o obj/remoteproc_loader.o obj/rpmsg.o obj/rpmsg_core.o obj/rsc_table_parser.o obj/sh_mem.o obj/virtio.o obj/virtqueue.o
obj_dir:
mkdir obj
print_msg_secure_base:
@echo "Compiling XilOpenAmp Library"
.c.o:
$(COMPILER) $(COMPILER_FLAGS) $(EXTRA_COMPILER_FLAGS) $(CC_FLAGS) $(INCLUDES) -c $< -o obj/$(@F)

View file

@ -0,0 +1,609 @@
/*
* 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
*
* bm_env.c
*
*
* DESCRIPTION
*
* This file is Bare Metal Implementation of env layer for OpenAMP.
*
*
**************************************************************************/
#include "env.h"
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "xil_exception.h"
#include "xpseudo_asm.h"
#include "bm_env.h"
#include "xscugic.h"
#if (defined(__CC_ARM))
#define MEM_BARRIER() __schedule_barrier()
#elif (defined(__GNUC__))
#define MEM_BARRIER() asm volatile("" ::: "memory")
#else
#define MEM_BARRIER()
#endif
static void acquire_spin_lock(void *plock);
static void release_spin_lock(void *plock);
struct isr_info isr_table[ISR_COUNT];
int Intr_Count = 0;
/* Flag to show status of global interrupts. 0 for disabled and 1 for enabled. This
* is added to prevent recursive global interrupts enablement/disablement.
*/
int Intr_Enable_Flag = 1;
unsigned int old_value = 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 (malloc(size));
}
/**
* env_free_memory - implementation
*
* @param ptr
*/
void env_free_memory(void *ptr)
{
if (ptr != NULL)
{
free(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 platform_vatopa(address);
}
/**
* env_map_patova - implementation
*
* @param address
*/
void *env_map_patova(unsigned long address)
{
return platform_patova(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.
*/
void env_lock_mutex(void *lock)
{
env_disable_interrupts();
}
/**
* env_unlock_mutex
*
* Releases the given 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) {
int *slock;
slock = (int *)malloc(sizeof(int));
if(slock){
*slock = state;
*lock = slock;
}
else{
*lock = NULL;
return -1;
}
return 0;
}
/**
* env_delete_sync_lock
*
* Deletes the given lock
*
*/
void env_delete_sync_lock(void *lock){
if(lock)
free(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){
acquire_spin_lock(lock);
}
/**
* env_release_sync_lock
*
* Releases the given lock.
*/
void env_release_sync_lock(void *lock){
release_spin_lock(lock);
}
/**
* env_sleep_msec
*
* Suspends the calling thread for given time , in msecs.
*/
void env_sleep_msec(int num_msec)
{
}
/**
* env_disable_interrupts
*
* Disables system interrupts
*
*/
void env_disable_interrupts()
{
if(Intr_Enable_Flag == 1) {
disable_global_interrupts();
Intr_Enable_Flag = 0;
}
}
/**
* env_restore_interrupts
*
* Enables system interrupts
*
*/
void env_restore_interrupts()
{
if(Intr_Enable_Flag == 0) {
restore_global_interrupts();
Intr_Enable_Flag = 1;
}
}
/**
* env_register_isr
*
* Registers interrupt handler for the given interrupt vector.
*
* @param vector - interrupt vector number
* @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();
}
void env_update_isr(int vector , void *data ,
void (*isr)(int vector , void *data))
{
int idx;
struct isr_info *info;
env_disable_interrupts();
for(idx = 0; idx < ISR_COUNT; idx++)
{
info = &isr_table[idx];
if(info->vector == vector)
{
info->data = data;
info->isr = isr;
break;
}
}
env_restore_interrupts();
}
/**
* 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)
{
int idx;
env_disable_interrupts();
for(idx = 0; idx < ISR_COUNT; idx++)
{
if(isr_table[idx].vector == vector)
{
isr_table[idx].priority = priority;
isr_table[idx].type = polarity;
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR,vector);
break;
}
}
env_restore_interrupts();
}
/**
* env_disable_interrupt
*
* Disables the given interrupt
*
* @param vector - interrupt vector number
*/
void env_disable_interrupt(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR,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
*/
void env_map_memory(unsigned int pa, unsigned int va, unsigned int size,
unsigned int flags) {
int is_mem_mapped = 0;
int cache_type = 0;
if ((flags & (0x0f << 4 )) == MEM_MAPPED)
{
is_mem_mapped = 1;
}
if ((flags & 0x0f) == WB_CACHE) {
cache_type = WRITEBACK;
}
else if((flags & 0x0f) == WT_CACHE) {
cache_type = WRITETHROUGH;
}
else {
cache_type = NOCACHE;
}
platform_map_mem_region(va, pa, size, is_mem_mapped, cache_type);
}
/**
* env_disable_cache
*
* Disables system caches.
*
*/
void env_disable_cache() {
platform_cache_all_flush_invalidate();
platform_cache_disable();
}
/**
*
* env_get_timestamp
*
* Returns a 64 bit time stamp.
*
*
*/
unsigned long long env_get_timestamp(void) {
/* TODO: Provide implementation for baremetal*/
return 0;
}
/*========================================================= */
/* Util data / functions for BM */
void bm_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);
env_enable_interrupt(info->vector , info->priority, info->type);
break;
}
}
}
static inline unsigned int xchg(void* plock, unsigned int lockVal)
{
volatile unsigned int tmpVal = 0;
volatile unsigned int tmpVal1 = 0;
#ifdef __GNUC__
asm (
"1: \n\t"
"LDREX %[tmpVal], [%[plock]] \n\t"
"STREX %[tmpVal1], %[lockVal], [%[plock]] \n\t"
"CMP %[tmpVal1], #0 \n\t"
"BNE 1b \n\t"
"DMB \n\t"
: [tmpVal] "=&r"(tmpVal)
: [tmpVal1] "r" (tmpVal1), [lockVal] "r"(lockVal), [plock] "r"(plock)
: "cc", "memory"
);
#endif
return tmpVal;
}
/**
*
* acquire_spin_lock
*
*/
static void acquire_spin_lock(void *plock)
{
const int lockVal = 0;
volatile unsigned int retVal;
do {
retVal = xchg(plock, lockVal);
} while (retVal==lockVal);
}
/**
* release_spin_lock
*/
static void release_spin_lock(void *plock)
{
MEM_BARRIER();
xchg(plock, 1);
}
/*
* restore global interrupts
*/
void restore_global_interrupts() {
ARM_AR_INT_BITS_SET(old_value);
}
/*
* disable global interrupts
*/
void disable_global_interrupts() {
unsigned int value = 0;
ARM_AR_INT_BITS_GET(&value);
if (value != old_value) {
ARM_AR_INT_BITS_SET(CORTEXR5_CPSR_INTERRUPTS_BITS);
old_value = value;
}
}

View file

@ -0,0 +1,133 @@
/*
* 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 the <ORGANIZATION> 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 _BM_ENV_H
#define _BM_ENV_H
#include "xil_cache.h"
#include "xreg_cortexr5.h"
#include "xpseudo_asm_gcc.h"
#include "xparameters.h"
/* IPI REGs OFFSET */
#define IPI_TRIG_OFFSET 0x00000000 /* IPI trigger register offset */
#define IPI_OBS_OFFSET 0x00000004 /* IPI observation register offset */
#define IPI_ISR_OFFSET 0x00000010 /* IPI interrupt status register offset */
#define IPI_IMR_OFFSET 0x00000014 /* IPI interrupt mask register offset */
#define IPI_IER_OFFSET 0x00000018 /* IPI interrupt enable register offset */
#define IPI_IDR_OFFSET 0x0000001C /* IPI interrupt disable register offset */
#ifndef BAREMETAL_MASTER
#define BAREMETAL_MASTER 0
#endif
/* The vector table address is the same as image entry point */
#define RAM_VECTOR_TABLE_ADDR ELF_START
typedef enum {
TRIG_NOT_SUPPORTED,
TRIG_RISING_EDGE,
TRIG_FALLING_EDGE,
TRIG_LEVEL_LOW,
TRIG_LEVEL_HIGH,
TRIG_RISING_FALLING_EDGES,
TRIG_HIGH_LOW_RISING_FALLING_EDGES
} INT_TRIG_TYPE;
typedef enum {
NOCACHE,
WRITEBACK,
WRITETHROUGH
} CACHE_TYPE;
#define CORTEXR5_CPSR_INTERRUPTS_BITS (XREG_CPSR_IRQ_ENABLE | XREG_CPSR_FIQ_ENABLE)
/* This macro writes the current program status register (CPSR - all fields) */
#define ARM_AR_CPSR_CXSF_WRITE(cpsr_cxsf_value) \
{ \
asm volatile(" MSR CPSR_cxsf, %0" \
: /* No outputs */ \
: "r" (cpsr_cxsf_value) ); \
}
/* This macro sets the interrupt related bits in the status register / control
register to the specified value. */
#define ARM_AR_INT_BITS_SET(set_bits) \
{ \
int tmp_val; \
tmp_val = mfcpsr(); \
tmp_val &= ~((unsigned int)CORTEXR5_CPSR_INTERRUPTS_BITS); \
tmp_val |= set_bits; \
ARM_AR_CPSR_CXSF_WRITE(tmp_val); \
}
/* This macro gets the interrupt related bits from the status register / control
register. */
#define ARM_AR_INT_BITS_GET(get_bits_ptr) \
{ \
int tmp_val; \
tmp_val = mfcpsr(); \
tmp_val &= CORTEXR5_CPSR_INTERRUPTS_BITS; \
*get_bits_ptr = tmp_val; \
}
#define SWITCH_TO_SYS_MODE() \
{ \
mtcpsr((mfcpsr() | XREG_CPSR_SYSTEM_MODE) & ~((unsigned int)CORTEXR5_CPSR_INTERRUPTS_BITS));\
}
void restore_global_interrupts();
void disable_global_interrupts();
/* define function macros for OpenAMP API */
#define platform_cache_all_flush_invalidate() \
{ \
Xil_DCacheFlush(); \
Xil_DCacheInvalidate(); \
Xil_ICacheInvalidate(); \
}
#define platform_cache_disable() \
{ \
Xil_DCacheDisable(); \
Xil_ICacheDisable(); \
}
#define platform_dcache_all_flush() { Xil_DCacheFlush(); }
#define platform_dcache_flush_range(addr, len) { Xil_DCacheFlushRange(addr, len); }
#define platform_map_mem_region(...)
#define platform_vatopa(addr) ((unsigned long)addr)
#define platform_patova(addr) ((void *)addr)
#endif /* _BAREMETAL_H */

View file

@ -0,0 +1,101 @@
/*
* 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"
/* Start and end addresses of firmware image for remotes. These are defined in the
* object files that are obtained by converting the remote ELF Image into object
* files. These symbols are not used for remotes.
*/
extern unsigned char _binary_firmware1_start;
extern unsigned char _binary_firmware1_end;
extern unsigned char _binary_firmware2_start;
extern unsigned char _binary_firmware2_end;
#define FIRMWARE1_START (void *)&_binary_firmware1_start
#define FIRMWARE1_END (void *)&_binary_firmware1_end
#define FIRMWARE2_START (void *)&_binary_firmware2_start
#define FIRMWARE2_END (void *)&_binary_firmware2_end
/* IPI REGs OFFSET */
#define IPI_TRIG_OFFSET 0x00000000 /* IPI trigger register offset */
#define IPI_OBS_OFFSET 0x00000004 /* IPI observation register offset */
#define IPI_ISR_OFFSET 0x00000010 /* IPI interrupt status register offset */
#define IPI_IMR_OFFSET 0x00000014 /* IPI interrupt mask register offset */
#define IPI_IER_OFFSET 0x00000018 /* IPI interrupt enable register offset */
#define IPI_IDR_OFFSET 0x0000001C /* IPI interrupt disable register offset */
/* Init firmware table */
const struct firmware_info fw_table[] = { { "firmware1",
(unsigned int) FIRMWARE1_START, (unsigned int) FIRMWARE1_END },
{ "firmware2", (unsigned int) FIRMWARE2_START,
(unsigned int) FIRMWARE2_END } };
/**
* config_get_firmware
*
* Searches the given firmware in firmware table list and provides
* it to caller.
*
* @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 config_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size) {
int idx;
for (idx = 0; idx < sizeof(fw_table) / (sizeof(struct firmware_info));
idx++) {
if (!env_strncmp((char *) fw_table[idx].name, fw_name,
sizeof(fw_table[idx].name))) {
*start_addr = fw_table[idx].start_addr;
*size = fw_table[idx].end_addr - fw_table[idx].start_addr + 1;
return 0;
}
}
return -1;
}

View file

@ -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.
*/
#ifndef CONFIG_H
#define CONFIG_H
#include "env.h"
/* Max supprted ISR counts */
#define ISR_COUNT 4
/* Max supported firmwares */
#define FW_COUNT 4
/**
* 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);
};
struct firmware_info {
char name[32];
unsigned int start_addr;
unsigned int end_addr;
};
int config_get_firmware(char *fw_name, unsigned int *start_addr, unsigned int *size);
#endif

View file

@ -0,0 +1,643 @@
/*
* 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.
*/
#include "elf_loader.h"
/* Local functions. */
static int elf_loader_get_needed_sections(struct elf_decode_info *elf_info);
static int elf_loader_relocs_specific(struct elf_decode_info *elf_info,
Elf32_Shdr *section);
static void *elf_loader_get_entry_point_address(
struct elf_decode_info *elf_info);
static int elf_loader_relocate_link(struct elf_decode_info *elf_info);
static int elf_loader_seek_and_read(void *firmware, void *destination,
Elf32_Off offset, Elf32_Word size);
static int elf_loader_read_headers(void *firmware,
struct elf_decode_info *elf_info);
static int elf_loader_load_sections(void *firmware,
struct elf_decode_info *elf_info);
static int elf_loader_get_decode_info(void *firmware,
struct elf_decode_info *elf_info);
static int elf_loader_reloc_entry(struct elf_decode_info *elf_info,
Elf32_Rel *rel_entry);
static Elf32_Addr elf_loader_get_dynamic_symbol_addr(
struct elf_decode_info *elf_info, int index);
/**
* elf_loader_init
*
* Initializes ELF loader.
*
* @param loader - pointer to remoteproc loader
*
* @return - 0 if success, error otherwise
*/
int elf_loader_init(struct remoteproc_loader *loader) {
/* Initialize loader function table */
loader->load_firmware = elf_loader_load_remote_firmware;
loader->retrieve_entry = elf_loader_retrieve_entry_point;
loader->retrieve_rsc = elf_loader_retrieve_resource_section;
loader->attach_firmware = elf_loader_attach_firmware;
loader->detach_firmware = elf_loader_detach_firmware;
loader->retrieve_load_addr = elf_get_load_address;
return RPROC_SUCCESS;
}
/**
* elf_loader_attach_firmware
*
* Attaches an ELF firmware to the loader
*
* @param loader - pointer to remoteproc loader
* @param firmware - pointer to the firmware start location
*
* @return - 0 if success, error otherwise
*/
int elf_loader_attach_firmware(struct remoteproc_loader *loader, void *firmware) {
struct elf_decode_info *elf_info;
int status;
/* Allocate memory for decode info structure. */
elf_info = env_allocate_memory(sizeof(struct elf_decode_info));
if (!elf_info) {
return RPROC_ERR_NO_MEM;
}
/* Clear the ELF decode struct. */
env_memset(elf_info, 0, sizeof(struct elf_decode_info));
/* Get the essential information to decode the ELF. */
status = elf_loader_get_decode_info(firmware, elf_info);
if (status) {
/* Free memory. */
env_free_memory(elf_info);
return status;
}
elf_info->firmware = firmware;
loader->fw_decode_info = elf_info;
return status;
}
/**
* elf_loader_detach_firmware
*
* Detaches ELF firmware from the loader
*
* @param loader - pointer to remoteproc loader
*
* @return - 0 if success, error otherwise
*/
int elf_loader_detach_firmware(struct remoteproc_loader *loader) {
struct elf_decode_info *elf_info =
(struct elf_decode_info *) loader->fw_decode_info;
if (elf_info) {
/* Free memory. */
env_free_memory(elf_info->shstrtab);
env_free_memory(elf_info->section_headers_start);
env_free_memory(elf_info);
}
return RPROC_SUCCESS;
}
/**
* elf_loader_retrieve_entry_point
*
* Retrieves the ELF entrypoint.
*
* @param loader - pointer to remoteproc loader
*
* @return - entrypoint
*/
void *elf_loader_retrieve_entry_point(struct remoteproc_loader *loader) {
return elf_loader_get_entry_point_address(
(struct elf_decode_info *)loader->fw_decode_info);
}
/**
* elf_loader_retrieve_resource_section
*
* Retrieves the resource section.
*
* @param loader - pointer to remoteproc loader
* @param size - pointer to contain the size of the section
*
* @return - pointer to resource section
*/
void *elf_loader_retrieve_resource_section(struct remoteproc_loader *loader,
unsigned int *size) {
Elf32_Shdr *rsc_header;
void* resource_section = NULL;
struct elf_decode_info *elf_info =
(struct elf_decode_info *) loader->fw_decode_info;
if (elf_info->rsc) {
/* Retrieve resource section header. */
rsc_header = elf_info->rsc;
/* Retrieve resource section size. */
*size = rsc_header->sh_size;
/* Locate the start of resource section. */
resource_section = (void *) ((unsigned int) elf_info->firmware
+ rsc_header->sh_offset);
}
/* Return the address of resource section. */
return resource_section;
}
/**
* elf_loader_load_remote_firmware
*
* Loads the ELF firmware.
*
* @param loader - pointer to remoteproc loader
*
* @return - 0 if success, error otherwise
*/
int elf_loader_load_remote_firmware(struct remoteproc_loader *loader) {
struct elf_decode_info *elf_info =
(struct elf_decode_info *) loader->fw_decode_info;
int status;
/* Load ELF sections. */
status = elf_loader_load_sections(elf_info->firmware, elf_info);
if (!status) {
/* Perform dynamic relocations if needed. */
status = elf_loader_relocate_link(elf_info);
}
return status;
}
/**
* elf_get_load_address
*
* Provides firmware load address.
*
* @param loader - pointer to remoteproc loader
*
* @return - load address pointer
*/
void *elf_get_load_address(struct remoteproc_loader *loader) {
struct elf_decode_info *elf_info =
(struct elf_decode_info *) loader->fw_decode_info;
int status = 0;
Elf32_Shdr *current = (Elf32_Shdr *) (elf_info->section_headers_start);
/* Traverse all sections except the reserved null section. */
int section_count = elf_info->elf_header.e_shnum - 1;
while ((section_count > 0) && (status == 0)) {
/* Compute the pointer to section header. */
current = (Elf32_Shdr *) (((unsigned char *) current)
+ elf_info->elf_header.e_shentsize);
/* Get the name of current section. */
char *current_name = elf_info->shstrtab + current->sh_name;
if(!env_strcmp(current_name , ".text")){
return ((void *) (current->sh_addr));
}
/* Move to the next section. */
section_count--;
}
return (RPROC_ERR_PTR);
}
/**
* elf_loader_get_needed_sections
*
* Retrieves the sections we need during the load and link from the
* section headers list.
*
* @param elf_info - ELF object decode info container.
*
* @return- Pointer to the ELF section header.
*/
static int elf_loader_get_needed_sections(struct elf_decode_info *elf_info) {
Elf32_Shdr *current = (Elf32_Shdr *) (elf_info->section_headers_start);
/* We are interested in the following sections:
.dynsym
.dynstr
.rel.plt
.rel.dyn
*/
int sections_to_find = 5;
/* Search for sections but skip the reserved null section. */
int section_count = elf_info->elf_header.e_shnum - 1;
while ((section_count > 0) && (sections_to_find > 0)) {
/* Compute the section header pointer. */
current = (Elf32_Shdr *) (((unsigned char *) current)
+ elf_info->elf_header.e_shentsize);
/* Get the name of current section. */
char *current_name = elf_info->shstrtab + current->sh_name;
/* Proceed if the section is allocatable and is not executable. */
if ((current->sh_flags & SHF_ALLOC)
&& !(current->sh_flags & SHF_EXECINSTR)) {
/* Check for '.dynsym' or '.dynstr' or '.rel.plt' or '.rel.dyn'. */
if (*current_name == '.') {
current_name++;
/* Check for '.dynsym' or 'dynstr'. */
if (*current_name == 'd') {
current_name++;
/* Check for '.dynsym'. */
if (env_strncmp(current_name, "ynsym", 5) == 0) {
elf_info->dynsym = current;
sections_to_find--;
}
/* Check for '.dynstr'. */
else if (env_strncmp(current_name, "ynstr", 5) == 0) {
elf_info->dynstr = current;
sections_to_find--;
}
}
/* Check for '.rel.plt' or '.rel.dyn'. */
else if (*current_name == 'r') {
current_name++;
/* Check for '.rel.plt'. */
if (env_strncmp(current_name, "el.plt", 6) == 0) {
elf_info->rel_plt = current;
sections_to_find--;
}
/* Check for '.rel.dyn'. */
else if (env_strncmp(current_name, "el.dyn", 6) == 0) {
elf_info->rel_dyn = current;
sections_to_find--;
}
/* Check for '.resource_table'. */
else if (env_strncmp(current_name, "esource_table", 13)
== 0) {
elf_info->rsc = current;
sections_to_find--;
}
}
}
}
/* Move to the next section. */
section_count--;
}
/* Return remaining sections section. */
return (sections_to_find);
}
/**
* elf_loader_relocs_specific
*
* Processes the relocations contained in the specified section.
*
* @param elf_info - elf decoding information.
* @param section - header of the specified relocation section.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_relocs_specific(struct elf_decode_info *elf_info,
Elf32_Shdr *section) {
unsigned char *section_load_addr = (unsigned char*) section->sh_addr;
int status = 0;
int i;
/* Check the section type. */
if (section->sh_type == SHT_REL) {
/* Traverse the list of relocation entries contained in the section. */
for (i = 0; (i < section->sh_size) && (status == 0);
i += section->sh_entsize) {
/* Compute the relocation entry address. */
Elf32_Rel *rel_entry = (Elf32_Rel *) (section_load_addr + i);
/* Process the relocation entry. */
status = elf_loader_reloc_entry(elf_info, rel_entry);
}
}
/* Return status to caller. */
return (status);
}
/**
* elf_loader_get_entry_point_address
*
* Retrieves the entry point address from the specified ELF object.
*
* @param elf_info - elf object decode info container.
* @param runtime_buffer - buffer containing ELF sections which are
* part of runtime.
*
* @return - entry point address of the specified ELF object.
*/
static void *elf_loader_get_entry_point_address(
struct elf_decode_info *elf_info) {
return ((void *) elf_info->elf_header.e_entry);
}
/**
* elf_loader_relocate_link
*
* Relocates and links the given ELF object.
*
* @param elf_info - elf object decode info container.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_relocate_link(struct elf_decode_info *elf_info) {
int status = 0;
/* Check of .rel.dyn section exists in the ELF. */
if (elf_info->rel_dyn) {
/* Relocate and link .rel.dyn section. */
status = elf_loader_relocs_specific(elf_info, elf_info->rel_dyn);
}
/* Proceed to check if .rel.plt section exists, if no error encountered yet. */
if (status == 0 && elf_info->rel_plt) {
/* Relocate and link .rel.plt section. */
status = elf_loader_relocs_specific(elf_info, elf_info->rel_plt);
}
/* Return status to caller */
return (status);
}
/**
* elf_loader_seek_and_read
*
* Seeks to the specified offset in the given file and reads the data
* into the specified destination location.
*
* @param firmware - firmware to read from.
* @param destination - Location into which the data should be read.
* @param offset - Offset to seek in the file.
* @param size - Size of the data to read.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_seek_and_read(void *firmware, void *destination,
Elf32_Off offset, Elf32_Word size) {
char *src = (char *) firmware;
/* Seek to the specified offset. */
src = src + offset;
/* Read the data. */
env_memcpy((char *) destination, src, size);
/* Return status to caller. */
return (0);
}
/**
* elf_loader_read_headers
*
* Reads the ELF headers (ELF header, section headers and the section
* headers string table) essential to access further information from
* the file containing the ELF object.
*
* @param firmware - firmware to read from.
* @param elf_info - ELF object decode info container.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_read_headers(void *firmware,
struct elf_decode_info *elf_info) {
int status = 0;
unsigned int section_count;
/* Read the ELF header. */
status = elf_loader_seek_and_read(firmware, &(elf_info->elf_header), 0,
sizeof(Elf32_Ehdr));
/* Ensure the read was successful. */
if (!status) {
/* Get section count from the ELF header. */
section_count = elf_info->elf_header.e_shnum;
/* Allocate memory to read in the section headers. */
elf_info->section_headers_start = env_allocate_memory(
section_count * elf_info->elf_header.e_shentsize);
/* Check if the allocation was successful. */
if (elf_info->section_headers_start) {
/* Read the section headers list. */
status = elf_loader_seek_and_read(firmware,
elf_info->section_headers_start,
elf_info->elf_header.e_shoff,
section_count * elf_info->elf_header.e_shentsize);
/* Ensure the read was successful. */
if (!status) {
/* Compute the pointer to section header string table section. */
Elf32_Shdr *section_header_string_table =
(Elf32_Shdr *) (elf_info->section_headers_start
+ elf_info->elf_header.e_shstrndx
* elf_info->elf_header.e_shentsize);
/* Allocate the memory for section header string table. */
elf_info->shstrtab = env_allocate_memory(
section_header_string_table->sh_size);
/* Ensure the allocation was successful. */
if (elf_info->shstrtab) {
/* Read the section headers string table. */
status = elf_loader_seek_and_read(firmware,
elf_info->shstrtab,
section_header_string_table->sh_offset,
section_header_string_table->sh_size);
}
}
}
}
/* Return status to caller. */
return (status);
}
/**
* elf_loader_file_read_sections
*
* Reads the ELF section contents from the specified file containing
* the ELF object.
*
* @param firmware - firmware to read from.
* @param elf_info - ELF object decode info container.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_load_sections(void *firmware,
struct elf_decode_info *elf_info) {
int status = 0;
Elf32_Shdr *current = (Elf32_Shdr *) (elf_info->section_headers_start);
/* Traverse all sections except the reserved null section. */
int section_count = elf_info->elf_header.e_shnum - 1;
while ((section_count > 0) && (status == 0)) {
/* Compute the pointer to section header. */
current = (Elf32_Shdr *) (((unsigned char *) current)
+ elf_info->elf_header.e_shentsize);
/* Make sure the section can be allocated and is not empty. */
if ((current->sh_flags & SHF_ALLOC) && (current->sh_size)) {
char *destination = NULL;
/* Check if the section is part of runtime and is not section with
* no-load attributes such as BSS or heap. */
if ((current->sh_type & SHT_NOBITS) == 0) {
/* Compute the destination address where the section should
* be copied. */
destination = (char *) (current->sh_addr);
status = elf_loader_seek_and_read(firmware, destination,
current->sh_offset, current->sh_size);
}
}
/* Move to the next section. */
section_count--;
}
/* Return status to caller. */
return (status);
}
/**
* elf_loader_get_decode_info
*
* Retrieves the information necessary to decode the ELF object for
* loading, relocating and linking.
*
* @param firmware - firmware to read from.
* @param elf_info - ELF object decode info container.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_get_decode_info(void *firmware,
struct elf_decode_info *elf_info) {
int status;
/* Read the ELF headers (ELF header and section headers including
* the section header string table). */
status = elf_loader_read_headers(firmware, elf_info);
/* Ensure that ELF headers were read successfully. */
if (!status) {
/* Retrieve the sections required for load. */
elf_loader_get_needed_sections(elf_info);
}
/* Return status to caller. */
return (status);
}
/**
* elf_loader_get_dynamic_symbol_addr
*
* Retrieves the (relocatable) address of the symbol specified as
* index from the given ELF object.
*
* @param elf_info - ELF object decode info container.
* @param index - Index of the desired symbol in the dynamic symbol table.
*
* @return - Address of the specified symbol.
*/
static Elf32_Addr elf_loader_get_dynamic_symbol_addr(
struct elf_decode_info *elf_info, int index) {
Elf32_Sym *symbol_entry = (Elf32_Sym *) (elf_info->dynsym_addr
+ index * elf_info->dynsym->sh_entsize);
/* Return the symbol address. */
return (symbol_entry->st_value);
}
/**
* elf_loader_reloc_entry
*
* Processes the specified relocation entry. It handles the relocation
* and linking both cases.
*
*
* @param elf_info - ELF object decode info container.
*
* @return - 0 if success, error otherwise
*/
static int elf_loader_reloc_entry(struct elf_decode_info *elf_info,
Elf32_Rel *rel_entry) {
unsigned char rel_type = ELF32_R_TYPE(rel_entry->r_info);
int status = 0;
switch (rel_type) {
case R_ARM_ABS32: /* 0x02 */
{
Elf32_Addr sym_addr = elf_loader_get_dynamic_symbol_addr(elf_info,
ELF32_R_SYM(rel_entry->r_info));
if (sym_addr) {
*((unsigned int *) (rel_entry->r_offset)) = (unsigned int) sym_addr;
break;
}
}
break;
default:
break;
}
return status;
}

View file

@ -0,0 +1,234 @@
/*
* 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 ELF_LOADER_H_
#define ELF_LOADER_H_
#include "remoteproc_loader.h"
/* ELF base types - 32-bit. */
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;
/* Size of ELF identifier field in the ELF file header. */
#define EI_NIDENT 16
/* ELF file header */
typedef struct
{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
/* e_ident */
#define ET_NONE 0
#define ET_REL 1 /* Re-locatable file */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
#define ET_CORE 4 /* Core file */
#define ET_LOOS 0xfe00 /* Operating system-specific */
#define ET_HIOS 0xfeff /* Operating system-specific */
#define ET_LOPROC 0xff00 /* remote_proc-specific */
#define ET_HIPROC 0xffff /* remote_proc-specific */
/* e_machine */
#define EM_ARM 40 /* ARM/Thumb Architecture */
/* e_version */
#define EV_CURRENT 1 /* Current version */
/* e_ident[] Identification Indexes */
#define EI_MAG0 0 /* File identification */
#define EI_MAG1 1 /* File identification */
#define EI_MAG2 2 /* File identification */
#define EI_MAG3 3 /* File identification */
#define EI_CLASS 4 /* File class */
#define EI_DATA 5 /* Data encoding */
#define EI_VERSION 6 /* File version */
#define EI_OSABI 7 /* Operating system/ABI identification */
#define EI_ABIVERSION 8 /* ABI version */
#define EI_PAD 9 /* Start of padding bytes */
#define EI_NIDENT 16 /* Size of e_ident[] */
/* EI_MAG0 to EI_MAG3 - A file's first 4 bytes hold amagic number, identifying the file as an ELF object file */
#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
/* EI_CLASS - The next byte, e_ident[EI_CLASS], identifies the file's class, or capacity. */
#define ELFCLASSNONE 0 /* Invalid class */
#define ELFCLASS32 1 /* 32-bit objects */
#define ELFCLASS64 2 /* 64-bit objects */
/* EI_DATA - Byte e_ident[EI_DATA] specifies the data encoding of the remote_proc-specific data in the object
file. The following encodings are currently defined. */
#define ELFDATANONE 0 /* Invalid data encoding */
#define ELFDATA2LSB 1 /* See Data encodings, below */
#define ELFDATA2MSB 2 /* See Data encodings, below */
/* EI_OSABI - We do not define an OS specific ABI */
#define ELFOSABI_NONE 0
/* ELF section header. */
typedef struct
{
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
/* sh_type */
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_INIT_ARRAY 14
#define SHT_FINI_ARRAY 15
#define SHT_PREINIT_ARRAY 16
#define SHT_GROUP 17
#define SHT_SYMTAB_SHNDX 18
#define SHT_LOOS 0x60000000
#define SHT_HIOS 0x6fffffff
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
/* sh_flags */
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_MASKPROC 0xf0000000
/* Relocation entry (without addend) */
typedef struct
{
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
/* Relocation entry with addend */
typedef struct
{
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
/* Macros to extract information from 'r_info' field of relocation entries */
#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
/* Symbol table entry */
typedef struct
{
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
/* ARM specific dynamic relocation codes */
#define R_ARM_GLOB_DAT 21 /* 0x15 */
#define R_ARM_JUMP_SLOT 22 /* 0x16 */
#define R_ARM_RELATIVE 23 /* 0x17 */
#define R_ARM_ABS32 2 /* 0x02 */
/* ELF decoding information */
struct elf_decode_info
{
Elf32_Ehdr elf_header;
unsigned char *section_headers_start;
char *shstrtab;
Elf32_Shdr *dynsym;
Elf32_Shdr *dynstr;
Elf32_Shdr *rel_plt;
Elf32_Shdr *rel_dyn;
Elf32_Shdr *rsc;
unsigned char *dynsym_addr;
unsigned char *dynstr_addr;
char *firmware;
};
/* ELF Loader functions. */
int elf_loader_init(struct remoteproc_loader *loader);
void *elf_loader_retrieve_entry_point(struct remoteproc_loader *loader);
void *elf_loader_retrieve_resource_section(struct remoteproc_loader *loader, unsigned int *size);
int elf_loader_load_remote_firmware(struct remoteproc_loader *loader);
int elf_loader_attach_firmware(struct remoteproc_loader *loader, void *firmware);
int elf_loader_detach_firmware(struct remoteproc_loader *loader);
void *elf_get_load_address(struct remoteproc_loader *loader);
#endif /* ELF_LOADER_H_ */

View file

@ -0,0 +1,428 @@
/*
* 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
*
* 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 <stdio.h>
/**
* env_init
*
* Initializes OS/BM environment.
*
* @returns - execution status
*/
int env_init();
/**
* env_deinit
*
* Uninitializes OS/BM environment.
*
* @returns - execution status
*/
int env_deinit();
/**
* -------------------------------------------------------------------------
*
* 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();
/**
* env_rmb
*
* Inserts read memory barrier
*/
void env_rmb();
/**
* env_wmb
*
* Inserts write memory barrier
*/
void env_wmb();
/**
*-----------------------------------------------------------------------------
*
* 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();
/**
* env_restore_interrupts
*
* Enables system interrupts
*
*/
void env_restore_interrupts();
/**
* 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();
typedef void LOCK;
#endif /* _ENV_H_ */

View file

@ -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);
}
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);
}
}
/**
* 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));
}

View file

@ -0,0 +1,486 @@
#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 <ORGANIZATION> 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.h"
#include "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);
};
#endif /* _HIL_H_ */

View file

@ -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;
}
}

View file

@ -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_ */

View file

@ -0,0 +1,37 @@
/*
* 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 OPEN_AMP_H_
#define OPEN_AMP_H_
#include "rpmsg.h"
#include "remoteproc.h"
#endif /* OPEN_AMP_H_ */

View file

@ -0,0 +1,513 @@
/*
* 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
*
* remote_device.c
*
* COMPONENT
*
* OpenAMP Stack
*
* DESCRIPTION
*
* This file provides services to manage the remote devices.It also implements
* the interface defined by the virtio and provides few other utility functions.
*
*
**************************************************************************/
#include "rpmsg.h"
/* Macro to initialize vring HW info */
#define INIT_VRING_ALLOC_INFO(ring_info,vring_hw) \
(ring_info).phy_addr = (vring_hw).phy_addr; \
(ring_info).align = (vring_hw).align; \
(ring_info).num_descs = (vring_hw).num_descs
/* Local functions */
static int rpmsg_rdev_init_channels(struct remote_device *rdev);
/* Ops table for virtio device */
virtio_dispatch rpmsg_rdev_config_ops =
{
rpmsg_rdev_create_virtqueues,
rpmsg_rdev_get_status,
rpmsg_rdev_set_status,
rpmsg_rdev_get_feature,
rpmsg_rdev_set_feature,
rpmsg_rdev_negotiate_feature,
rpmsg_rdev_read_config,
rpmsg_rdev_write_config,
rpmsg_rdev_reset
};
/**
* rpmsg_rdev_init
*
* This function creates and initializes the remote device. The remote device
* encapsulates virtio device.
*
* @param rdev - pointer to newly created remote device
* @param dev-id - ID of device to create , remote cpu id
* @param role - role of the other device, Master or Remote
* @param channel_created - callback function for channel creation
* @param channel_destroyed - callback function for channel deletion
* @param default_cb - default callback for channel
*
* @return - status of function execution
*
*/
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) {
struct remote_device *rdev_loc;
struct virtio_device *virt_dev;
struct hil_proc *proc;
struct proc_shm *shm;
int status;
/* Initialize HIL data structures for given device */
proc = hil_create_proc(dev_id);
if (!proc) {
return RPMSG_ERR_DEV_ID;
}
/* Create software representation of remote processor. */
rdev_loc = (struct remote_device *) env_allocate_memory(
sizeof(struct remote_device));
if (!rdev_loc) {
return RPMSG_ERR_NO_MEM;
}
env_memset(rdev_loc, 0x00, sizeof(struct remote_device));
status = env_create_mutex(&rdev_loc->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);
}
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);
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);
} 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);
for (idx = 0; idx < num_chnls; idx++) {
rp_chnl = _rpmsg_create_channel(rdev, chnl_info[idx].name, 0x00,
RPMSG_NS_EPT_ADDR);
if (!rp_chnl) {
return RPMSG_ERR_NO_MEM;
}
rp_chnl->rp_ept = rpmsg_create_ept(rp_chnl, rdev->default_cb, rdev,
RPMSG_ADDR_ANY);
if (!rp_chnl->rp_ept) {
return RPMSG_ERR_NO_MEM;
}
rp_chnl->src = rp_chnl->rp_ept->addr;
}
}
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,
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]);
if (rdev->role == RPMSG_REMOTE) {
env_memset((void*) ring_info.phy_addr, 0x00,
vring_size(vring_table[idx].num_descs,
vring_table[idx].align));
}
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.
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);
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) {
}
unsigned long rpmsg_rdev_get_feature(struct virtio_device *dev) {
return dev->features;
}
void rpmsg_rdev_set_feature(struct virtio_device *dev, unsigned long feature) {
dev->features |= feature;
}
unsigned long rpmsg_rdev_negotiate_feature(struct virtio_device *dev,
unsigned long 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, unsigned long offset,
void *dst, int length) {
return;
}
void rpmsg_rdev_write_config(struct virtio_device *dev, unsigned long offset,
void *src, int length) {
return;
}
void rpmsg_rdev_reset(struct virtio_device *dev) {
return;
}

View file

@ -0,0 +1,359 @@
/*
* 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.
*/
#include "remoteproc.h"
#include "remoteproc_loader.h"
#include "rsc_table_parser.h"
#include "env.h"
#include "hil.h"
/**
* remoteproc_resource_init
*
* Initializes resources for remoteproc remote configuration. Only
* remoteproc remote applications are allowed to call this function.
*
* @param rsc_info - pointer to resource table info control
* block
* @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 rproc_handle - pointer to new remoteproc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_resource_init(struct rsc_table_info *rsc_info,
rpmsg_chnl_cb_t channel_created, rpmsg_chnl_cb_t channel_destroyed,
rpmsg_rx_cb_t default_cb, struct remote_proc** rproc_handle) {
struct remote_proc *rproc;
int status;
if(!rsc_info) {
return RPROC_ERR_PARAM;
}
/* Initialize environment component */
status = env_init();
if (status != RPROC_SUCCESS) {
return status;
}
rproc = env_allocate_memory(sizeof(struct remote_proc));
if (rproc) {
env_memset(rproc, 0x00, sizeof(struct remote_proc));
/* There can be only one master for remote configuration so use the
* rsvd cpu id for creating hil proc */
rproc->proc = hil_create_proc(HIL_RSVD_CPU_ID);
if (rproc->proc) {
/* Parse resource table */
status = handle_rsc_table(rproc, rsc_info->rsc_tab, rsc_info->size);
if (status == RPROC_SUCCESS) {
/* Initialize RPMSG "messaging" component */
*rproc_handle = rproc;
status = rpmsg_init(rproc->proc->cpu_id, &rproc->rdev,
channel_created, channel_destroyed, default_cb,
RPMSG_MASTER);
} else {
status = RPROC_ERR_NO_RSC_TABLE;
}
} else {
status = RPROC_ERR_CPU_ID;
}
} else {
status = RPROC_ERR_NO_MEM;
}
/* Cleanup in case of error */
if (status != RPROC_SUCCESS) {
*rproc_handle = 0;
(void) remoteproc_resource_deinit(rproc);
return status;
}
return status;
}
/**
* remoteproc_resource_deinit
*
* Uninitializes resources for remoteproc "remote" configuration.
*
* @param rproc - pointer to rproc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_resource_deinit(struct remote_proc *rproc) {
int i = 0;
struct proc_vring *vring_hw = 0;
if (rproc) {
if (rproc->rdev) {
/* disable IPC interrupts */
if (rproc->proc->ops->reg_ipi_after_deinit) {
for(i = 0; i < 2; i++) {
vring_hw = &rproc->proc->vdev.vring_info[i];
rproc->proc->ops->reg_ipi_after_deinit(vring_hw);
}
}
rpmsg_deinit(rproc->rdev);
}
if (rproc->proc) {
hil_delete_proc(rproc->proc);
}
env_free_memory(rproc);
}
env_deinit();
/* Disable the caches - This is required if master boots firmwares
* multiple times without hard reset on same core. If caches are
* not invalidated at this point in time then subsequent firmware
* boots on the same core may experience cache inconsistencies.
*
*/
env_disable_cache();
return RPROC_SUCCESS;
}
/**
* remoteproc_init
*
* Initializes resources for remoteproc master configuration. Only
* remoteproc master applications are allowed to call this function.
*
* @param fw_name - name of frimware
* @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 rproc_handle - pointer to new remoteproc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_init(char *fw_name, rpmsg_chnl_cb_t channel_created,
rpmsg_chnl_cb_t channel_destroyed, rpmsg_rx_cb_t default_cb,
struct remote_proc** rproc_handle) {
struct remote_proc *rproc;
struct resource_table *rsc_table;
unsigned int fw_addr, fw_size, rsc_size;
int status, cpu_id;
if (!fw_name) {
return RPROC_ERR_PARAM;
}
/* Initialize environment component */
status = env_init();
if (status != RPROC_SUCCESS) {
return status;
}
rproc = env_allocate_memory(sizeof(struct remote_proc));
if (rproc) {
env_memset((void *) rproc, 0x00, sizeof(struct remote_proc));
/* Get CPU ID for the given firmware name */
cpu_id = hil_get_cpuforfw(fw_name);
if (cpu_id >= 0) {
/* Create proc instance */
rproc->proc = hil_create_proc(cpu_id);
if (rproc->proc) {
/* Retrieve firmware attributes */
status = hil_get_firmware(fw_name, &fw_addr, &fw_size);
if (!status) {
/* Initialize ELF loader - currently only ELF format is supported */
rproc->loader = remoteproc_loader_init(ELF_LOADER);
if (rproc->loader) {
/* Attach the given firmware with the ELF parser/loader */
status = remoteproc_loader_attach_firmware(
rproc->loader, (void *) fw_addr);
} else {
status = RPROC_ERR_LOADER;
}
}
} else {
status = RPROC_ERR_NO_MEM;
}
} else {
status = RPROC_ERR_INVLD_FW;
}
} else {
status = RPROC_ERR_NO_MEM;
}
if (!status) {
rproc->role = RPROC_MASTER;
/* Get resource table from firmware */
rsc_table = remoteproc_loader_retrieve_resource_section(rproc->loader,
&rsc_size);
if (rsc_table) {
/* Parse resource table */
status = handle_rsc_table(rproc, rsc_table, rsc_size);
} else {
status = RPROC_ERR_NO_RSC_TABLE;
}
}
/* Cleanup in case of error */
if (status != RPROC_SUCCESS) {
(void) remoteproc_deinit(rproc);
return status;
}
rproc->channel_created = channel_created;
rproc->channel_destroyed = channel_destroyed;
rproc->default_cb = default_cb;
*rproc_handle = rproc;
return status;
}
/**
* remoteproc_deinit
*
* Uninitializes resources for remoteproc "master" configuration.
*
* @param rproc - pointer to remote proc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_deinit(struct remote_proc *rproc) {
if (rproc) {
if (rproc->loader) {
(void) remoteproc_loader_delete(rproc->loader);
rproc->loader = RPROC_NULL;
}
if (rproc->proc) {
hil_delete_proc(rproc->proc);
rproc->proc = RPROC_NULL;
}
env_free_memory(rproc);
}
env_deinit();
return RPROC_SUCCESS;
}
/**
* remoteproc_boot
*
* This function loads the image on the remote processor and starts
* its execution from image load address.
*
* @param rproc - pointer to remoteproc instance to boot
*
* @param returns - status of function execution
*/
int remoteproc_boot(struct remote_proc *rproc) {
void *load_addr;
int status;
if (!rproc) {
return RPROC_ERR_PARAM;
}
/* Stop the remote CPU */
hil_shutdown_cpu(rproc->proc);
/* Load the firmware */
status = remoteproc_loader_load_remote_firmware(rproc->loader);
if (status == RPROC_SUCCESS) {
load_addr = remoteproc_get_load_address(rproc->loader);
if (load_addr != RPROC_ERR_PTR) {
/* Start the remote cpu */
status = hil_boot_cpu(rproc->proc,
(unsigned int) load_addr);
if (status == RPROC_SUCCESS) {
/* Wait for remote side to come up. This delay is arbitrary and may
* need adjustment for different configuration of remote systems */
env_sleep_msec(RPROC_BOOT_DELAY);
/* Initialize RPMSG "messaging" component */
/* It is a work-around to work with remote Linux context.
Since the upstream Linux rpmsg implementation always
assumes itself to be an rpmsg master, we initialize
the remote device as an rpmsg master for remote Linux
configuration only. */
#if defined (OPENAMP_REMOTE_LINUX_ENABLE)
status = rpmsg_init(rproc->proc->cpu_id, &rproc->rdev,
rproc->channel_created,
rproc->channel_destroyed, rproc->default_cb,
RPMSG_MASTER);
#else
status = rpmsg_init(rproc->proc->cpu_id, &rproc->rdev,
rproc->channel_created,
rproc->channel_destroyed, rproc->default_cb,
RPMSG_REMOTE);
#endif
}
} else {
status = RPROC_ERR_LOADER;
}
} else {
status = RPROC_ERR_LOADER;
}
return status;
}
/**
* remoteproc_shutdown
*
* This function shutdowns the remote execution context
*
* @param rproc - pointer to remote proc instance to shutdown
*
* @param returns - status of function execution
*/
int remoteproc_shutdown(struct remote_proc *rproc) {
if (rproc) {
if (rproc->rdev) {
rpmsg_deinit(rproc->rdev);
rproc->rdev = RPROC_NULL;
}
if (rproc->proc) {
hil_shutdown_cpu(rproc->proc);
}
}
return RPROC_SUCCESS;
}

View file

@ -0,0 +1,466 @@
/*
* Remote remote_proc Framework
*
* 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 REMOTEPROC_H
#define REMOTEPROC_H
#include "rpmsg.h"
#include "config.h"
/**
* struct resource_table - firmware resource table header
* @ver: version number
* @num: number of resource entries
* @reserved: reserved (must be zero)
* @offset: array of offsets pointing at the various resource entries
*
* A resource table is essentially a list of system resources required
* by the remote remote_proc. It may also include configuration entries.
* If needed, the remote remote_proc firmware should contain this table
* as a dedicated ".resource_table" ELF section.
*
* Some resources entries are mere announcements, where the host is informed
* of specific remoteproc configuration. Other entries require the host to
* do something (e.g. allocate a system resource). Sometimes a negotiation
* is expected, where the firmware requests a resource, and once allocated,
* the host should provide back its details (e.g. address of an allocated
* memory region).
*
* The header of the resource table, as expressed by this structure,
* contains a version number (should we need to change this format in the
* future), the number of available resource entries, and their offsets
* in the table.
*
* Immediately following this header are the resource entries themselves,
* each of which begins with a resource entry header (as described below).
*/
struct resource_table {
unsigned int ver;
unsigned int num;
unsigned int reserved[2];
unsigned int offset[0];
} __attribute__((__packed__));
/**
* struct fw_rsc_hdr - firmware resource entry header
* @type: resource type
* @data: resource data
*
* Every resource entry begins with a 'struct fw_rsc_hdr' header providing
* its @type. The content of the entry itself will immediately follow
* this header, and it should be parsed according to the resource type.
*/
struct fw_rsc_hdr {
unsigned int type;
unsigned char data[0];
} __attribute__((__packed__));
/**
* enum fw_resource_type - types of resource entries
*
* @RSC_CARVEOUT: request for allocation of a physically contiguous
* memory region.
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
* @RSC_TRACE: announces the availability of a trace buffer into which
* the remote remote_proc will be writing logs.
* @RSC_VDEV: declare support for a virtio device, and serve as its
* virtio header.
* @RSC_LAST: just keep this one at the end
*
* For more details regarding a specific resource type, please see its
* dedicated structure below.
*
* Please note that these values are used as indices to the rproc_handle_rsc
* lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
* check the validity of an index before the lookup table is accessed, so
* please update it as needed.
*/
enum fw_resource_type {
RSC_CARVEOUT = 0,
RSC_DEVMEM = 1,
RSC_TRACE = 2,
RSC_VDEV = 3,
RSC_LAST = 4,
};
#define FW_RSC_ADDR_ANY (0xFFFFFFFFFFFFFFFF)
/**
* struct fw_rsc_carveout - physically contiguous memory request
* @da: device address
* @pa: physical address
* @len: length (in bytes)
* @flags: iommu protection flags
* @reserved: reserved (must be zero)
* @name: human-readable name of the requested memory region
*
* This resource entry requests the host to allocate a physically contiguous
* memory region.
*
* These request entries should precede other firmware resource entries,
* as other entries might request placing other data objects inside
* these memory regions (e.g. data/code segments, trace resource entries, ...).
*
* Allocating memory this way helps utilizing the reserved physical memory
* (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
* needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
* pressure is important; it may have a substantial impact on performance.
*
* If the firmware is compiled with static addresses, then @da should specify
* the expected device address of this memory region. If @da is set to
* FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then
* overwrite @da with the dynamically allocated address.
*
* We will always use @da to negotiate the device addresses, even if it
* isn't using an iommu. In that case, though, it will obviously contain
* physical addresses.
*
* Some remote remote_procs needs to know the allocated physical address
* even if they do use an iommu. This is needed, e.g., if they control
* hardware accelerators which access the physical memory directly (this
* is the case with OMAP4 for instance). In that case, the host will
* overwrite @pa with the dynamically allocated physical address.
* Generally we don't want to expose physical addresses if we don't have to
* (remote remote_procs are generally _not_ trusted), so we might want to
* change this to happen _only_ when explicitly required by the hardware.
*
* @flags is used to provide IOMMU protection flags, and @name should
* (optionally) contain a human readable name of this carveout region
* (mainly for debugging purposes).
*/
struct fw_rsc_carveout {
unsigned int type;
unsigned int da;
unsigned int pa;
unsigned int len;
unsigned int flags;
unsigned int reserved;
unsigned char name[32];
} __attribute__((__packed__));
/**
* struct fw_rsc_devmem - iommu mapping request
* @da: device address
* @pa: physical address
* @len: length (in bytes)
* @flags: iommu protection flags
* @reserved: reserved (must be zero)
* @name: human-readable name of the requested region to be mapped
*
* This resource entry requests the host to iommu map a physically contiguous
* memory region. This is needed in case the remote remote_proc requires
* access to certain memory-based peripherals; _never_ use it to access
* regular memory.
*
* This is obviously only needed if the remote remote_proc is accessing memory
* via an iommu.
*
* @da should specify the required device address, @pa should specify
* the physical address we want to map, @len should specify the size of
* the mapping and @flags is the IOMMU protection flags. As always, @name may
* (optionally) contain a human readable name of this mapping (mainly for
* debugging purposes).
*
* Note: at this point we just "trust" those devmem entries to contain valid
* physical addresses, but this isn't safe and will be changed: eventually we
* want remoteproc implementations to provide us ranges of physical addresses
* the firmware is allowed to request, and not allow firmwares to request
* access to physical addresses that are outside those ranges.
*/
struct fw_rsc_devmem {
unsigned int type;
unsigned int da;
unsigned int pa;
unsigned int len;
unsigned int flags;
unsigned int reserved;
unsigned char name[32];
} __attribute__((__packed__));
/**
* struct fw_rsc_trace - trace buffer declaration
* @da: device address
* @len: length (in bytes)
* @reserved: reserved (must be zero)
* @name: human-readable name of the trace buffer
*
* This resource entry provides the host information about a trace buffer
* into which the remote remote_proc will write log messages.
*
* @da specifies the device address of the buffer, @len specifies
* its size, and @name may contain a human readable name of the trace buffer.
*
* After booting the remote remote_proc, the trace buffers are exposed to the
* user via debugfs entries (called trace0, trace1, etc..).
*/
struct fw_rsc_trace {
unsigned int type;
unsigned int da;
unsigned int len;
unsigned int reserved;
unsigned char name[32];
} __attribute__((__packed__));
/**
* struct fw_rsc_vdev_vring - vring descriptor entry
* @da: device address
* @align: the alignment between the consumer and producer parts of the vring
* @num: num of buffers supported by this vring (must be power of two)
* @notifyid is a unique rproc-wide notify index for this vring. This notify
* index is used when kicking a remote remote_proc, to let it know that this
* vring is triggered.
* @reserved: reserved (must be zero)
*
* This descriptor is not a resource entry by itself; it is part of the
* vdev resource type (see below).
*
* Note that @da should either contain the device address where
* the remote remote_proc is expecting the vring, or indicate that
* dynamically allocation of the vring's device address is supported.
*/
struct fw_rsc_vdev_vring {
unsigned int da;
unsigned int align;
unsigned int num;
unsigned int notifyid;
unsigned int reserved;
} __attribute__((__packed__));
/**
* struct fw_rsc_vdev - virtio device header
* @id: virtio device id (as in virtio_ids.h)
* @notifyid is a unique rproc-wide notify index for this vdev. This notify
* index is used when kicking a remote remote_proc, to let it know that the
* status/features of this vdev have changes.
* @dfeatures specifies the virtio device features supported by the firmware
* @gfeatures is a place holder used by the host to write back the
* negotiated features that are supported by both sides.
* @config_len is the size of the virtio config space of this vdev. The config
* space lies in the resource table immediate after this vdev header.
* @status is a place holder where the host will indicate its virtio progress.
* @num_of_vrings indicates how many vrings are described in this vdev header
* @reserved: reserved (must be zero)
* @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'.
*
* This resource is a virtio device header: it provides information about
* the vdev, and is then used by the host and its peer remote remote_procs
* to negotiate and share certain virtio properties.
*
* By providing this resource entry, the firmware essentially asks remoteproc
* to statically allocate a vdev upon registration of the rproc (dynamic vdev
* allocation is not yet supported).
*
* Note: unlike virtualization systems, the term 'host' here means
* the Linux side which is running remoteproc to control the remote
* remote_procs. We use the name 'gfeatures' to comply with virtio's terms,
* though there isn't really any virtualized guest OS here: it's the host
* which is responsible for negotiating the final features.
* Yeah, it's a bit confusing.
*
* Note: immediately following this structure is the virtio config space for
* this vdev (which is specific to the vdev; for more info, read the virtio
* spec). the size of the config space is specified by @config_len.
*/
struct fw_rsc_vdev {
unsigned int type;
unsigned int id;
unsigned int notifyid;
unsigned int dfeatures;
unsigned int gfeatures;
unsigned int config_len;
unsigned char status;
unsigned char num_of_vrings;
unsigned char reserved[2];
struct fw_rsc_vdev_vring vring[0];
} __attribute__((__packed__));
/**
* struct remote_proc
*
* This structure is maintained by the remoteproc to represent the remote
* processor instance. This structure acts as a prime parameter to use
* the remoteproc APIs.
*
* @proc : hardware interface layer processor control
* @rdev : remote device , used by RPMSG "messaging" framework.
* @loader : pointer remoteproc loader
* @channel_created : create channel callback
* @channel_destroyed : delete channel callback
* @default_cb : default callback for channel
* @role : remote proc role , RPROC_MASTER/RPROC_REMOTE
*
*/
struct remote_proc {
struct hil_proc *proc;
struct remote_device *rdev;
struct remoteproc_loader *loader;
rpmsg_chnl_cb_t channel_created;
rpmsg_chnl_cb_t channel_destroyed;
rpmsg_rx_cb_t default_cb;
int role;
};
/**
* struct resc_table_info
*
* This structure is maintained by the remoteproc to allow applications
* to pass resource table info during remote initialization.
*
* @rsc_tab : pointer to resource table control block
* @size : size of resource table.
*
*/
struct rsc_table_info {
struct resource_table *rsc_tab;
int size;
};
/* Definitions for device types , null pointer, etc.*/
#define RPROC_SUCCESS 0
#define RPROC_NULL (void *)0
#define RPROC_TRUE 1
#define RPROC_FALSE 0
#define RPROC_MASTER 1
#define RPROC_REMOTE 0
/* Number of msecs to wait for remote context to come up */
#define RPROC_BOOT_DELAY 500
/* Remoteproc error codes */
#define RPROC_ERR_BASE -4000
#define RPROC_ERR_CPU_ID (RPROC_ERR_BASE -1)
#define RPROC_ERR_NO_RSC_TABLE (RPROC_ERR_BASE -2)
#define RPROC_ERR_NO_MEM (RPROC_ERR_BASE -3)
#define RPROC_ERR_RSC_TAB_TRUNC (RPROC_ERR_BASE -4)
#define RPROC_ERR_RSC_TAB_VER (RPROC_ERR_BASE -5)
#define RPROC_ERR_RSC_TAB_RSVD (RPROC_ERR_BASE -6)
#define RPROC_ERR_RSC_TAB_VDEV_NRINGS (RPROC_ERR_BASE -7)
#define RPROC_ERR_RSC_TAB_NP (RPROC_ERR_BASE -8)
#define RPROC_ERR_RSC_TAB_NS (RPROC_ERR_BASE -9)
#define RPROC_ERR_INVLD_FW (RPROC_ERR_BASE -10)
#define RPROC_ERR_LOADER (RPROC_ERR_BASE -11)
#define RPROC_ERR_PARAM (RPROC_ERR_BASE -12)
#define RPROC_ERR_PTR (void*)0xDEADBEAF
/**
* remoteproc_resource_init
*
* Initializes resources for remoteproc remote configuration.Only
* remoteproc remote applications are allowed to call this function.
*
* @param rsc_info - pointer to resource table info control
* block
* @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 rproc_handle - pointer to new remoteproc instance
*
* @param returns - status of execution
*
*/
int remoteproc_resource_init(
struct rsc_table_info *rsc_info,
rpmsg_chnl_cb_t channel_created,
rpmsg_chnl_cb_t channel_destroyed,
rpmsg_rx_cb_t default_cb,
struct remote_proc** rproc_handle);
/**
* remoteproc_resource_deinit
*
* Uninitializes resources for remoteproc remote configuration.
*
* @param rproc - pointer to remoteproc instance
*
* @param returns - status of execution
*
*/
int remoteproc_resource_deinit(struct remote_proc *rproc);
/**
* remoteproc_init
*
* Initializes resources for remoteproc master configuration. Only
* remoteproc master applications are allowed to call this function.
*
* @param fw_name - name of firmware
* @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 rproc_handle - pointer to new remoteproc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_init(char *fw_name,
rpmsg_chnl_cb_t channel_created,
rpmsg_chnl_cb_t channel_destroyed,
rpmsg_rx_cb_t default_cb, struct remote_proc** rproc_handle);
/**
* remoteproc_deinit
*
* Uninitializes resources for remoteproc "master" configuration.
*
* @param rproc - pointer to remoteproc instance
*
* @param returns - status of function execution
*
*/
int remoteproc_deinit(struct remote_proc *rproc);
/**
* remoteproc_boot
*
* This function loads the image on the remote processor and starts
* its execution from image load address.
*
* @param rproc - pointer to remoteproc instance to boot
*
* @param returns - status of function execution
*/
int remoteproc_boot(struct remote_proc *rproc);
/**
* remoteproc_shutdown
*
* This function shutdowns the remote execution context.
*
* @param rproc - pointer to remoteproc instance to shutdown
*
* @param returns - status of function execution
*/
int remoteproc_shutdown(struct remote_proc *rproc);
#endif /* REMOTEPROC_H_ */

View file

@ -0,0 +1,241 @@
/*
* 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.
*/
#include "remoteproc_loader.h"
/**
* remoteproc_loader_init
*
* Initializes the remoteproc loader.
*
* @param type - loader type
*
* @return - remoteproc_loader
*/
struct remoteproc_loader * remoteproc_loader_init(enum loader_type type) {
struct remoteproc_loader *loader;
/* Check for valid loader type. */
if (type >= LAST_LOADER) {
return RPROC_NULL ;
}
/* Allocate a loader handle. */
loader = env_allocate_memory(sizeof(struct remoteproc_loader));
if (!loader) {
return RPROC_NULL ;
}
/* Clear loader handle. */
env_memset(loader, 0, sizeof(struct remoteproc_loader));
/* Save loader type. */
loader->type = type;
switch (type) {
case ELF_LOADER:
elf_loader_init(loader);
break;
default:
/* Loader not supported. */
env_free_memory(loader);
loader = RPROC_NULL;
break;
}
return loader;
}
/**
* remoteproc_loader_delete
*
* Deletes the remoteproc loader.
*
* @param loader - pointer to remoteproc loader
*
* @return - 0 if success, error otherwise
*/
int remoteproc_loader_delete(struct remoteproc_loader *loader) {
int status = 0;
if (!loader) {
return RPROC_ERR_PARAM;
}
/* Check if a firmware is attached. */
if (loader->remote_firmware) {
/* Detach firmware first. */
status = loader->detach_firmware(loader);
}
/* Recover the allocated memory. */
env_free_memory(loader);
return status;
}
/**
* remoteproc_loader_attach_firmware
*
* Attaches an ELF firmware to the loader
*
* @param loader - pointer to remoteproc loader
* @param firmware - pointer to the firmware start location
*
* @return - 0 if success, error otherwise
*/
int remoteproc_loader_attach_firmware(struct remoteproc_loader *loader,
void *firmware_image) {
int status = RPROC_SUCCESS;
if (!loader || !firmware_image) {
return RPROC_ERR_PARAM;
}
if (loader->attach_firmware) {
/* Check if a firmware is already attached. */
if (loader->remote_firmware) {
/* Detach firmware first. */
status = loader->detach_firmware(loader);
}
/* Attach firmware. */
if (!status) {
status = loader->attach_firmware(loader, firmware_image);
/* Save firmware address. */
if (!status) {
loader->remote_firmware = firmware_image;
}
}
}else{
status = RPROC_ERR_LOADER;
}
return status;
}
/**
* remoteproc_loader_retrieve_entry_point
*
* Provides entry point address.
*
* @param loader - pointer to remoteproc loader
*
* @return - entrypoint
*/
void *remoteproc_loader_retrieve_entry_point(struct remoteproc_loader *loader) {
if (!loader) {
return RPROC_NULL ;
}
if (loader->retrieve_entry) {
return loader->retrieve_entry(loader);
} else {
return RPROC_NULL ;
}
}
/**
* remoteproc_loader_retrieve_resource_section
*
* Provides resource section address.
*
* @param loader - pointer to remoteproc loader
* @param size - pointer to hold size of resource section
*
* @return - pointer to resource section
*/
void *remoteproc_loader_retrieve_resource_section(
struct remoteproc_loader *loader, unsigned int *size) {
if (!loader) {
return RPROC_NULL ;
}
if (loader->retrieve_rsc) {
return loader->retrieve_rsc(loader, size);
} else {
return RPROC_NULL ;
}
}
/**
* remoteproc_loader_load_remote_firmware
*
* Loads the firmware in memory
*
* @param loader - pointer to remoteproc loader
*
* @return - 0 if success, error otherwise
*/
int remoteproc_loader_load_remote_firmware(struct remoteproc_loader *loader) {
if (!loader) {
return RPROC_ERR_PARAM;
}
if (loader->load_firmware) {
return loader->load_firmware(loader);
} else {
return RPROC_ERR_LOADER;
}
}
/**
* remoteproc_get_load_address
*
* Provides firmware load address.
*
* @param loader - pointer to remoteproc loader
*
* @return - load address pointer
*/
void *remoteproc_get_load_address(struct remoteproc_loader *loader){
if (!loader) {
return RPROC_ERR_PTR;
}
if (loader->retrieve_load_addr) {
return loader->retrieve_load_addr(loader);
} else {
return RPROC_ERR_PTR;
}
}

View file

@ -0,0 +1,92 @@
/*
* 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
*
* remoteproc_loader.h
*
* COMPONENT
*
* OpenAMP stack.
*
* DESCRIPTION
*
* This file provides definitions for remoteproc loader
*
*
**************************************************************************/
#ifndef REMOTEPROC_LOADER_H_
#define REMOTEPROC_LOADER_H_
#include "remoteproc.h"
/**
* enum loader_type - dynamic name service announcement flags
*
* @ELF_LOADER: an ELF loader
* @FIT_LOADER: a loader for Flattened Image Trees
*/
enum loader_type {
ELF_LOADER = 0, FIT_LOADER = 1, LAST_LOADER = 2,
};
/* Loader structure definition. */
struct remoteproc_loader {
enum loader_type type;
void *remote_firmware;
/* Pointer to firmware decoded info control block */
void *fw_decode_info;
/* Loader callbacks. */
void *(*retrieve_entry)(struct remoteproc_loader *loader);
void *(*retrieve_rsc)(struct remoteproc_loader *loader, unsigned int *size);
int (*load_firmware)(struct remoteproc_loader *loader);
int (*attach_firmware)(struct remoteproc_loader *loader, void *firmware);
int (*detach_firmware)(struct remoteproc_loader *loader);
void *(*retrieve_load_addr)(struct remoteproc_loader *loader);
};
/* RemoteProc Loader functions. */
struct remoteproc_loader * remoteproc_loader_init(enum loader_type type);
int remoteproc_loader_delete(struct remoteproc_loader *loader);
int remoteproc_loader_attach_firmware(struct remoteproc_loader *loader,
void *firmware_image);
void *remoteproc_loader_retrieve_entry_point(struct remoteproc_loader *loader);
void *remoteproc_loader_retrieve_resource_section(
struct remoteproc_loader *loader, unsigned int* size);
int remoteproc_loader_load_remote_firmware(struct remoteproc_loader *loader);
void *remoteproc_get_load_address(struct remoteproc_loader *loader);
/* Supported loaders */
extern int elf_loader_init(struct remoteproc_loader *loader);
#endif /* REMOTEPROC_LOADER_H_ */

View file

@ -0,0 +1,414 @@
/*
* 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;
/* 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) {
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;
}

View file

@ -0,0 +1,402 @@
/*
* 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_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);
#endif /* _RPMSG_H_ */

View file

@ -0,0 +1,766 @@
/*
* 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_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 */
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 */
status = virt_dev->func->create_virtqueues(virt_dev, 0,
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);
/*
* Create name service announcement endpoint if device supports name
* service announcement feature.
*/
if ((dev_features & (1 << VIRTIO_RPMSG_F_NS))) {
rdev->support_ns = RPMSG_TRUE;
ns_ept = _create_endpoint(rdev, rpmsg_ns_callback, rdev,
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) {
status = virtqueue_add_buffer(rdev->tvq, &node, 0, 1, buffer);
} else {
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) {
virtqueue_add_buffer(rdev->rvq, &node, 0, 1, buffer);
} else {
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) {
data = virtqueue_get_buffer(rdev->tvq, (unsigned long *) len);
if (data == RPMSG_NULL) {
data = sh_mem_get_buffer(rdev->mem_pool);
*len = RPMSG_BUFFER_SIZE;
}
} else {
data = virtqueue_get_available_buffer(rdev->tvq, idx,
(unsigned long *) 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) {
data = virtqueue_get_buffer(rdev->rvq, len);
} else {
data = virtqueue_get_available_buffer(rdev->rvq, idx, 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);
if (!rp_hdr) {
return;
}
/* Get the channel node from the remote device channels list. */
node = rpmsg_rdev_get_endpoint_from_addr(rdev, 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;
if ((rp_chnl) && (rp_chnl->state == RPMSG_CHNL_STATE_NS)) {
/* First message from RPMSG Master, update channel
* destination address and state */
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);
}
} else {
rp_ept->cb(rp_chnl, rp_hdr->data, rp_hdr->len, rp_ept->priv,
rp_hdr->src);
}
env_lock_mutex(rdev->lock);
/* Return used buffers. */
rpmsg_return_buffer(rdev, rp_hdr, 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 {
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);
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;
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;
}

View file

@ -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 "env.h"
#include "virtio.h"
#include "hil.h"
#include "sh_mem.h"
#include "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);
unsigned long rpmsg_rdev_get_feature(struct virtio_device *dev);
void rpmsg_rdev_set_feature(struct virtio_device *dev, unsigned long feature);
unsigned long rpmsg_rdev_negotiate_feature(struct virtio_device *dev,
unsigned long 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, unsigned long offset,
void *dst, int length);
void rpmsg_rdev_write_config(struct virtio_device *dev, unsigned long offset,
void *src, int length);
void rpmsg_rdev_reset(struct virtio_device *dev);
#endif /* _RPMSG_CORE_H_ */

View file

@ -0,0 +1,245 @@
#include "open_amp.h"
#include "rpmsg_retarget.h"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "sleep.h"
/*************************************************************************
* Description
* This files contains rpmsg based redefinitions for C RTL system calls
* such as _open, _read, _write, _close.
*************************************************************************/
static struct _rpc_data* rpc_data;
static unsigned int rpc_data_synclock = 0;
int get_response = 0;
int send_rpc(void *data, int len);
static int rpc_count=0;
void rpc_cb(struct rpmsg_channel *rtl_rp_chnl, void *data, int len, void * priv,
unsigned long src) {
memcpy(rpc_data->rpc_response, data, len);
env_release_sync_lock(rpc_data->sync_lock);
get_response=1;
if (rpc_data->rpc_response->id == TERM_SYSCALL_ID) {
/* Application terminate signal is received from the proxy app,
* so let the application know of terminate message.
*/
rpc_data->shutdown_cb(rtl_rp_chnl);
}
}
int send_rpc(void *data, int len) {
int retval;
retval = rpmsg_sendto(rpc_data->rpmsg_chnl, data, len, PROXY_ENDPOINT);
return retval;
}
int rpmsg_retarget_init(struct rpmsg_channel *rp_chnl, rpc_shutdown_cb cb) {
int status;
/* Allocate memory for rpc control block */
rpc_data = (struct _rpc_data*) env_allocate_memory(
sizeof(struct _rpc_data));
/* Create a mutex for synchronization */
status = env_create_mutex(&rpc_data->rpc_lock, 1);
/* Create a mutex for synchronization */
status = env_create_sync_lock(&rpc_data->sync_lock, LOCKED);
/* Create a endpoint to handle rpc response from master */
rpc_data->rpmsg_chnl = rp_chnl;
rpc_data->rp_ept = rpmsg_create_ept(rpc_data->rpmsg_chnl, rpc_cb,
RPMSG_NULL, PROXY_ENDPOINT);
rpc_data->rpc = env_allocate_memory(RPC_BUFF_SIZE);
rpc_data->rpc_response = env_allocate_memory(RPC_BUFF_SIZE);
rpc_data->shutdown_cb = cb;
return status;
}
int rpmsg_retarget_deinit(struct rpmsg_channel *rp_chnl) {
env_free_memory(rpc_data->rpc);
env_free_memory(rpc_data->rpc_response);
env_delete_mutex(rpc_data->rpc_lock);
env_delete_sync_lock(rpc_data->sync_lock);
rpmsg_destroy_ept(rpc_data->rp_ept);
env_free_memory(rpc_data);
return 0;
}
/*************************************************************************
*
* FUNCTION
*
* _open
*
* DESCRIPTION
*
* Open a file. Minimal implementation
*
*************************************************************************/
int _open(const char * filename, int flags, int mode) {
int filename_len = strlen(filename) + 1;
int payload_size = sizeof(struct _sys_rpc) + filename_len;
int retval = -1;
if ((!filename) || (filename_len > FILE_NAME_LEN)) {
return -1;
}
/* Construct rpc payload */
rpc_data->rpc->id = OPEN_SYSCALL_ID;
rpc_data->rpc->sys_call_args.int_field1 = flags;
rpc_data->rpc->sys_call_args.int_field2 = mode;
rpc_data->rpc->sys_call_args.data_len = filename_len;
memcpy(&rpc_data->rpc->sys_call_args.data, filename, filename_len);
/* Transmit rpc request */
env_lock_mutex(rpc_data->rpc_lock);
send_rpc((void*) rpc_data->rpc, payload_size);
env_unlock_mutex(rpc_data->rpc_lock);
/* Wait for response from proxy on master */
env_acquire_sync_lock(rpc_data->sync_lock);
/* Obtain return args and return to caller */
if (rpc_data->rpc_response->id == OPEN_SYSCALL_ID) {
retval = rpc_data->rpc_response->sys_call_args.int_field1;
}
return retval;
}
/*************************************************************************
*
* FUNCTION
*
* _read
*
* DESCRIPTION
*
* Low level function to redirect IO to serial.
*
*************************************************************************/
int _read(int fd, char * buffer, int buflen) {
int payload_size = sizeof(struct _sys_rpc);
int retval = -1;
if (!buffer || !buflen)
return retval;
/* Construct rpc payload */
rpc_data->rpc->id = READ_SYSCALL_ID;
rpc_data->rpc->sys_call_args.int_field1 = fd;
rpc_data->rpc->sys_call_args.int_field2 = buflen;
rpc_data->rpc->sys_call_args.data_len = 0; /*not used*/
/* Transmit rpc request */
env_lock_mutex(rpc_data->rpc_lock);
get_response=0;
send_rpc((void*) rpc_data->rpc, payload_size);
env_unlock_mutex(rpc_data->rpc_lock);
/* Wait for response from proxy on master */
env_acquire_sync_lock(rpc_data->sync_lock);
/* Obtain return args and return to caller */
if (rpc_data->rpc_response->id == READ_SYSCALL_ID) {
if (rpc_data->rpc_response->sys_call_args.int_field1 > 0) {
memcpy(buffer, rpc_data->rpc_response->sys_call_args.data,
rpc_data->rpc_response->sys_call_args.data_len);
}
retval = rpc_data->rpc_response->sys_call_args.int_field1;
}
return retval;
}
/*************************************************************************
*
* FUNCTION
*
* _write
*
* DESCRIPTION
*
* Low level function to redirect IO to serial.
*
*************************************************************************/
int _write(int fd, const char * ptr, int len) {
int retval = -1;
int payload_size = sizeof(struct _sys_rpc) + len;
int null_term = 0;
if (fd == 1) {
null_term = 1;
}
rpc_data->rpc->id = WRITE_SYSCALL_ID;
rpc_data->rpc->sys_call_args.int_field1 = fd;
rpc_data->rpc->sys_call_args.int_field2 = len;
rpc_data->rpc->sys_call_args.data_len = len + null_term;
memcpy(rpc_data->rpc->sys_call_args.data, ptr, len);
if (null_term) {
*(char*) (rpc_data->rpc->sys_call_args.data + len + null_term) = 0;
}
env_lock_mutex(rpc_data->rpc_lock);
send_rpc((void*) rpc_data->rpc, payload_size);
env_unlock_mutex(rpc_data->rpc_lock);
env_acquire_sync_lock(rpc_data->sync_lock);
if (rpc_data->rpc_response->id == WRITE_SYSCALL_ID) {
retval = rpc_data->rpc_response->sys_call_args.int_field1;
}
return retval;
}
/*************************************************************************
*
* FUNCTION
*
* _close
*
* DESCRIPTION
*
* Close a file. Minimal implementation
*
*************************************************************************/
int _close(int fd) {
int payload_size = sizeof(struct _sys_rpc);
int retval = -1;
rpc_data->rpc->id = CLOSE_SYSCALL_ID;
rpc_data->rpc->sys_call_args.int_field1 = fd;
rpc_data->rpc->sys_call_args.int_field2 = 0; /*not used*/
rpc_data->rpc->sys_call_args.data_len = 0; /*not used*/
env_lock_mutex(rpc_data->rpc_lock);
send_rpc((void*) rpc_data->rpc, payload_size);
env_unlock_mutex(rpc_data->rpc_lock);
/* Wait for response from proxy on master */
env_acquire_sync_lock(rpc_data->sync_lock);
if (rpc_data->rpc_response->id == CLOSE_SYSCALL_ID) {
retval = rpc_data->rpc_response->sys_call_args.int_field1;
}
return retval;
}

View file

@ -0,0 +1,52 @@
#include "open_amp.h"
/* RPC response buffer size */
#define RPC_BUFF_SIZE 512
/* System call definitions */
#define OPEN_SYSCALL_ID 1
#define CLOSE_SYSCALL_ID 2
#define WRITE_SYSCALL_ID 3
#define READ_SYSCALL_ID 4
#define ACK_STATUS_ID 5
#define TERM_SYSCALL_ID 6
#define FILE_NAME_LEN 50
/* Proxy device endpoint ID */
#define PROXY_ENDPOINT 127
typedef void (*rpc_shutdown_cb)(struct rpmsg_channel *);
struct _rpc_data
{
struct rpmsg_channel* rpmsg_chnl;
struct rpmsg_endpoint* rp_ept;
void* rpc_lock;
void* sync_lock;
struct _sys_rpc* rpc;
struct _sys_rpc* rpc_response;
rpc_shutdown_cb shutdown_cb;
};
struct _sys_call_args
{
int int_field1;
int int_field2;
unsigned int data_len;
char data[0];
};
/* System call rpc data structure */
struct _sys_rpc
{
unsigned int id;
struct _sys_call_args sys_call_args;
};
void debug_print(char* str, int len);
/* API prototypes */
int rpmsg_retarget_init(struct rpmsg_channel *rp_chnl, rpc_shutdown_cb cb);
int rpmsg_retarget_deinit(struct rpmsg_channel *rp_chnl);

View file

@ -0,0 +1,234 @@
/*
* 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.
*/
#include "rsc_table_parser.h"
/* Resources handler */
rsc_handler rsc_handler_table[] =
{
handle_carve_out_rsc,
handle_trace_rsc,
handle_dev_mem_rsc,
handle_vdev_rsc,
handle_mmu_rsc
};
/**
* handle_rsc_table
*
* This function parses resource table.
*
* @param rproc - pointer to remote remote_proc
* @param rsc_table - resource table to parse
* @param size - size of rsc table
*
* @returns - execution status
*
*/
int handle_rsc_table(struct remote_proc *rproc, struct resource_table *rsc_table,
int size) {
unsigned char *rsc_start;
unsigned int *rsc_offset;
unsigned int rsc_type;
int idx, status = 0;
/* Validate rsc table header fields*/
/* Minimum rsc table size */
if (sizeof(struct resource_table) > size) {
return (RPROC_ERR_RSC_TAB_TRUNC);
}
/* Supported version */
if (rsc_table->ver != RSC_TAB_SUPPORTED_VERSION) {
return (RPROC_ERR_RSC_TAB_VER);
}
/* Offset array */
if (sizeof(struct resource_table)
+ rsc_table->num * sizeof(rsc_table->offset[0]) > size) {
return (RPROC_ERR_RSC_TAB_TRUNC);
}
/* Reserved fields - must be zero */
if ((rsc_table->reserved[0] != 0 || rsc_table->reserved[1]) != 0) {
return RPROC_ERR_RSC_TAB_RSVD;
}
rsc_start = (unsigned char *) rsc_table;
/* Loop through the offset array and parse each resource entry */
for (idx = 0; idx < rsc_table->num; idx++) {
rsc_offset = (unsigned int *) (rsc_start + rsc_table->offset[idx]);
rsc_type = *rsc_offset;
status = rsc_handler_table[rsc_type](rproc, (void *) rsc_offset);
if (status != RPROC_SUCCESS) {
break;
}
}
return status;
}
/**
* handle_carve_out_rsc
*
* Carveout resource handler.
*
* @param rproc - pointer to remote remote_proc
* @param rsc - pointer to carveout resource
*
* @returns - execution status
*
*/
int handle_carve_out_rsc(struct remote_proc *rproc, void *rsc) {
struct fw_rsc_carveout *carve_rsc = (struct fw_rsc_carveout *) rsc;
/* Validate resource fields */
if (!carve_rsc) {
return RPROC_ERR_RSC_TAB_NP;
}
if (carve_rsc->reserved) {
return RPROC_ERR_RSC_TAB_RSVD;
}
if (rproc->role == RPROC_MASTER) {
/* Map memory region for loading the image */
env_map_memory(carve_rsc->da, carve_rsc->da, carve_rsc->len,
(SHARED_MEM | UNCACHED));
}
return RPROC_SUCCESS;
}
/**
* handle_trace_rsc
*
* Trace resource handler.
*
* @param rproc - pointer to remote remote_proc
* @param rsc - pointer to trace resource
*
* @returns - execution status
*
*/
int handle_trace_rsc(struct remote_proc *rproc, void *rsc) {
return RPROC_ERR_RSC_TAB_NS;
}
/**
* handle_dev_mem_rsc
*
* Device memory resource handler.
*
* @param rproc - pointer to remote remote_proc
* @param rsc - pointer to device memory resource
*
* @returns - execution status
*
*/
int handle_dev_mem_rsc(struct remote_proc *rproc, void *rsc) {
return RPROC_ERR_RSC_TAB_NS;
}
/**
* handle_vdev_rsc
*
* Virtio device resource handler
*
* @param rproc - pointer to remote remote_proc
* @param rsc - pointer to virtio device resource
*
* @returns - execution status
*
*/
int handle_vdev_rsc(struct remote_proc *rproc, void *rsc) {
struct fw_rsc_vdev *vdev_rsc = (struct fw_rsc_vdev *) rsc;
struct fw_rsc_vdev_vring *vring;
struct proc_vdev *vdev;
struct proc_vring *vring_table;
int idx;
if (!vdev_rsc) {
return RPROC_ERR_RSC_TAB_NP;
}
/* Maximum supported vrings per Virtio device */
if (vdev_rsc->num_of_vrings > RSC_TAB_MAX_VRINGS) {
return RPROC_ERR_RSC_TAB_VDEV_NRINGS;
}
/* Reserved fields - must be zero */
if (vdev_rsc->reserved[0] || vdev_rsc->reserved[1]) {
return RPROC_ERR_RSC_TAB_RSVD;
}
/* Get the Virtio device from HIL proc */
vdev = hil_get_vdev_info(rproc->proc);
/* Initialize HIL Virtio device resources */
vdev->num_vrings = vdev_rsc->num_of_vrings;
vdev->dfeatures = vdev_rsc->dfeatures;
vdev->gfeatures = vdev_rsc->gfeatures;
vring_table = &vdev->vring_info[0];
for (idx = 0; idx < vdev_rsc->num_of_vrings; idx++) {
vring = &vdev_rsc->vring[idx];
/* Initialize HIL vring resources */
vring_table[idx].phy_addr = (void *) vring->da;
vring_table[idx].num_descs = vring->num;
vring_table[idx].align = vring->align;
/* Enable access to vring memory regions */
env_map_memory(vring->da, vring->da,
vring_size(vring->num, vring->align),
(SHARED_MEM | UNCACHED));
}
return RPROC_SUCCESS;
}
/**
* handle_mmu_rsc
*
* This function parses mmu resource , requested by the peripheral.
*
* @param rproc - pointer to remote remote_proc
* @param rsc - pointer to mmu resource
*
* @returns - execution status
*
*/
int handle_mmu_rsc(struct remote_proc *rproc, void *rsc) {
return RPROC_ERR_RSC_TAB_NS;
}

View file

@ -0,0 +1,53 @@
/*
* 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 RSC_TABLE_PARSER_H
#define RSC_TABLE_PARSER_H
#include "remoteproc.h"
#include "env.h"
#include "hil.h"
#define RSC_TAB_SUPPORTED_VERSION 1
#define RSC_TAB_HEADER_SIZE 12
#define RSC_TAB_MAX_VRINGS 2
/* Standard control request handling. */
typedef int (*rsc_handler)(struct remote_proc *rproc, void * rsc);
/* Function prototypes */
int handle_rsc_table(struct remote_proc *rproc, struct resource_table *rsc_table,
int len);
int handle_carve_out_rsc(struct remote_proc *rproc, void *rsc);
int handle_trace_rsc(struct remote_proc *rproc, void *rsc);
int handle_dev_mem_rsc(struct remote_proc *rproc, void *rsc);
int handle_vdev_rsc(struct remote_proc *rproc, void *rsc);
int handle_mmu_rsc(struct remote_proc *rproc, void *rsc);
#endif /* RSC_TABLE_PARSER_H */

View file

@ -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;
}

View file

@ -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 "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_ */

View file

@ -0,0 +1,95 @@
/*-
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* 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);
}

View file

@ -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_ */

View file

@ -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
/* 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 */

View file

@ -0,0 +1,691 @@
/*-
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* 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;
vq->vq_nentries = ring->num_descs;
vq->vq_free_cnt = vq->vq_nentries;
vq->callback = callback;
vq->notify = 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;
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,
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,
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) {
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);
}
/**
*
* 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);
}

View file

@ -0,0 +1,233 @@
#ifndef VIRTQUEUE_H_
#define VIRTQUEUE_H_
/*-
* Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* 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$
*/
typedef unsigned int uint_t;
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef uint8_t boolean;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
#include "virtio_ring.h"
#include "env.h"
#include "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;
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_ */