From 04f650a4d0b2d1722e3153c4ecadb2a7f0ae135b Mon Sep 17 00:00:00 2001 From: guyiwen Date: Sat, 26 Mar 2022 14:22:35 +0800 Subject: [PATCH] Add OpenAMP userspace demo Add OpenAMP userspace demo: 1. Load Client OS to specific physical address and activate it. 2. Start processing messages from Linux and Client OS through rpmsg architecture. Add OpenAMP sample README file. Signed-off-by: guyiwen --- mcs/README.md | 46 ++++++ mcs/kernel_cpu_handler/Makefile | 2 +- mcs/openamp_demo/Makefile | 11 ++ mcs/openamp_demo/rpmsg-internal.h | 27 ++++ mcs/openamp_demo/rpmsg_main.c | 172 ++++++++++++++++++++++ mcs/openamp_demo/rpmsg_ping.c | 228 ++++++++++++++++++++++++++++++ 6 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 mcs/README.md create mode 100644 mcs/openamp_demo/Makefile create mode 100644 mcs/openamp_demo/rpmsg-internal.h create mode 100644 mcs/openamp_demo/rpmsg_main.c create mode 100644 mcs/openamp_demo/rpmsg_ping.c diff --git a/mcs/README.md b/mcs/README.md new file mode 100644 index 00000000..281ba153 --- /dev/null +++ b/mcs/README.md @@ -0,0 +1,46 @@ +# mcs + +#### 介绍 + +该模块用于提供OpenAMP样例的内核态与用户态支持。 + +#### 软件架构 + +kernel_cpu_handler: 提供OpenAMP所需内核模块,支持Client OS启动、专用中断收发等功能。 + +openamp_demo: 提供OpenAMP用户态程序样例,支持与指定Client OS进行通信。 + + +#### 安装教程 + +1. 根据openEuler Embedded使用手册安装SDK并设置SDK环境变量。 + +2. 编译内核模块cpu_handler_dev.ko,编译方式如下: + +```` + cd kernel_cpu_handler + make +```` + +3. 编译用户态程序rpmsg_main,编译方式如下: + +```` + cd openamp_demo + make +```` + +4. 将编译好的KO模块、用户态程序,以及zephyr.bin镜像拷贝到OpenEuler Embedded 系统的目录下。如何拷贝可以参考使用手册中共享文件系统场景。 + + +#### 使用说明 + +1. 在openEuler Embedded系统上插入内核KO模块cpu_handler_dev.ko。 + +2. 运行rpmsg_main程序,使用方式如下: + +```` + ./rpmsg_main -c [cpu_id] -b [boot_address] -t [target_binfile] -a [target_binaddress] + eg: + ./rpmsg_main -c 3 -b 0xc0000ffc -t /tmp/zephyr.bin -a 0xc0000000 +```` + diff --git a/mcs/kernel_cpu_handler/Makefile b/mcs/kernel_cpu_handler/Makefile index f0b7393e..28b37108 100644 --- a/mcs/kernel_cpu_handler/Makefile +++ b/mcs/kernel_cpu_handler/Makefile @@ -1,4 +1,4 @@ -KERNELDIR := ${DL_SDK_KERNELDIR} +KERNELDIR := ${KERNEL_SRC_DIR} CURRENT_PATH := $(shell pwd) target := cpu_handler_dev diff --git a/mcs/openamp_demo/Makefile b/mcs/openamp_demo/Makefile new file mode 100644 index 00000000..c293881e --- /dev/null +++ b/mcs/openamp_demo/Makefile @@ -0,0 +1,11 @@ +rpmsg_main: rpmsg_main.o rpmsg_ping.o + $(CC) rpmsg_main.o rpmsg_ping.o $(SDKTARGETSYSROOT)/usr/lib64/libmetal.so $(SDKTARGETSYSROOT)/usr/lib64/libopen_amp.so $(SDKTARGETSYSROOT)/lib64/libsysfs.so -o rpmsg_main + +rpmsg_main.o: rpmsg_main.c rpmsg-internal.h + $(CC) -I$(SDKTARGETSYSROOT)/usr/include -c rpmsg_main.c -o rpmsg_main.o + +rpmsg_ping.o: rpmsg_ping.c rpmsg-internal.h + $(CC) -I$(SDKTARGETSYSROOT)/usr/include -c rpmsg_ping.c -o rpmsg_ping.o + +clean: + rm -rf *.o rpmsg_main diff --git a/mcs/openamp_demo/rpmsg-internal.h b/mcs/openamp_demo/rpmsg-internal.h new file mode 100644 index 00000000..44d80c14 --- /dev/null +++ b/mcs/openamp_demo/rpmsg-internal.h @@ -0,0 +1,27 @@ +#ifndef RPMSG_INTERNAL_H_ +#define RPMSG_INTERNAL_H_ + +#include +#include +#include + +#define VDEV_START_ADDR 0xD0000000 +#define VDEV_SIZE 0x30000 + +#define VDEV_STATUS_ADDR VDEV_START_ADDR +#define VDEV_STATUS_SIZE 0x4000 + +#define SHM_START_ADDR (VDEV_START_ADDR + VDEV_STATUS_SIZE) +#define SHM_SIZE (VDEV_SIZE - VDEV_STATUS_SIZE) + +#define VRING_COUNT 2 +#define VRING_RX_ADDRESS (VDEV_START_ADDR + SHM_SIZE - VDEV_STATUS_SIZE) +#define VRING_TX_ADDRESS (VDEV_START_ADDR + SHM_SIZE) +#define VRING_ALIGNMENT 4 +#define VRING_SIZE 16 + +#define IRQ_SENDTO_CLIENTOS _IOW('A', 0, int) +#define DEV_CLIENT_OS_AGENT "/dev/cpu_handler" + +void rpmsg_app_master(void); +#endif diff --git a/mcs/openamp_demo/rpmsg_main.c b/mcs/openamp_demo/rpmsg_main.c new file mode 100644 index 00000000..2ac2726f --- /dev/null +++ b/mcs/openamp_demo/rpmsg_main.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "rpmsg-internal.h" + +#define MAX_BIN_BUFLEN (10 * 1024 * 1024) +#define BOOTCMD_MAXSIZE 100 + +static struct remoteproc rproc_inst; +static struct remoteproc_ops ops; + +static char *cpu_id; +static char *boot_address; +static char *target_binfile; +static char *target_binaddr; + +struct rproc_priv { + struct remoteproc *rproc; + unsigned int id; +}; + +static int load_bin(void) +{ + int memfd = open("/dev/mem", O_RDWR); + int bin_fd = open(target_binfile, O_RDONLY); + void *access_address = NULL, *bin_buffer = NULL; + long bin_size; + long long int bin_addr = strtoll(target_binaddr, NULL, 0); + + if (bin_fd < 0 || memfd < 0) { + printf("invalid bin file fd\n"); + exit(-1); + } + + bin_buffer = (void *)malloc(MAX_BIN_BUFLEN); + if (!bin_buffer) { + printf("malloc bin_buffer failed\n"); + exit(-1); + } + + bin_size = read(bin_fd, bin_buffer, MAX_BIN_BUFLEN); + if (bin_size == 0) { + printf("read bin file failed\n"); + exit(-1); + } + + access_address = mmap((void *)bin_addr, MAX_BIN_BUFLEN, PROT_READ | PROT_WRITE, + MAP_SHARED, memfd, bin_addr); + memcpy(access_address, bin_buffer, bin_size); + return 0; +} + +static struct remoteproc *rproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct rproc_priv *priv; + unsigned int id = *((unsigned int *)arg); + + priv = metal_allocate_memory(sizeof(*priv)); + if (!priv) + return NULL; + + memset(priv, 0, sizeof(*priv)); + priv->rproc = rproc; + priv->id = id; + priv->rproc->ops = ops; + metal_list_init(&priv->rproc->mems); + priv->rproc->priv = priv; + rproc->state = RPROC_READY; + return priv->rproc; +} + +static int rproc_start(struct remoteproc *rproc) +{ + int cpu_handler_fd; + int ret; + char on[BOOTCMD_MAXSIZE]; + + (void)snprintf(on, sizeof(on), "%s%s%s", cpu_id, "@", boot_address); + + cpu_handler_fd = open(DEV_CLIENT_OS_AGENT, O_RDWR); + if (cpu_handler_fd < 0) { + printf("failed to open %s\n", DEV_CLIENT_OS_AGENT); + return cpu_handler_fd; + } + + ret = write(cpu_handler_fd, on, sizeof(on)); + return 0; +} + +static void rproc_remove(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + + priv = (struct rproc_priv *)rproc->priv; + metal_free_memory(priv); +} + +struct remoteproc_ops rproc_ops = { + .init = rproc_init, + .remove = rproc_remove, + .start = rproc_start, +}; + +static struct remoteproc *platform_create_proc(unsigned int id) +{ + struct remoteproc *rproc; + + ops = rproc_ops; + rproc = remoteproc_init(&rproc_inst, &ops, &id); + if (!rproc) + return NULL; + + return &rproc_inst; +} + +int main(int argc, char **argv) +{ + struct remoteproc *rproc = NULL; + unsigned int id = 1; + int ret; + int opt; + + while ((opt = getopt(argc, argv, "c:b:t:a:")) != -1) { + switch (opt) { + case 'c': + cpu_id = optarg; + break; + case 'b': + boot_address = optarg; + break; + case 't': + target_binfile = optarg; + break; + case 'a': + target_binaddr = optarg; + break; + default: + break; + } + } + + rproc = platform_create_proc(id); + if (!rproc) { + printf("create rproc failed\n"); + return -1; + } + + ret = load_bin(); + if (ret) { + printf("failed to load client os\n"); + return ret; + } + + ret = remoteproc_start(rproc); + if (ret) { + printf("start processor failed\n"); + return ret; + } + + sleep(5); + printf("start processing OpenAMP demo...\n"); + rpmsg_app_master(); + + remoteproc_remove(rproc); + return ret; +} diff --git a/mcs/openamp_demo/rpmsg_ping.c b/mcs/openamp_demo/rpmsg_ping.c new file mode 100644 index 00000000..0cb75168 --- /dev/null +++ b/mcs/openamp_demo/rpmsg_ping.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "rpmsg-internal.h" + +static struct virtio_vring_info rvrings[2] = { + [0] = { + .info.align = VRING_ALIGNMENT, + }, + [1] = { + .info.align = VRING_ALIGNMENT, + }, +}; + +static int g_memfd; +static volatile unsigned int received_data; +static struct virtio_device vdev; +static struct rpmsg_virtio_device rvdev; +static struct metal_io_region *io; +static struct virtqueue *vq[2]; + +static unsigned char virtio_get_status(struct virtio_device *vdev) +{ + return VIRTIO_CONFIG_STATUS_DRIVER_OK; +} + +static void virtio_set_status(struct virtio_device *vdev, unsigned char status) +{ + void *stat_addr = NULL; + stat_addr = mmap((void *)VDEV_STATUS_ADDR, VDEV_STATUS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_memfd, VDEV_STATUS_ADDR); + *(volatile unsigned int *)stat_addr = (unsigned int)status; +} + +static uint32_t virtio_get_features(struct virtio_device *vdev) +{ + return 1 << VIRTIO_RPMSG_F_NS; +} + +static void virtio_notify(struct virtqueue *vq) +{ + (void)vq; + int cpu_handler_fd; + int ret; + + cpu_handler_fd = open(DEV_CLIENT_OS_AGENT, O_RDWR); + if (cpu_handler_fd < 0) { + printf("open %s failed\n", DEV_CLIENT_OS_AGENT); + return; + } + + ret = ioctl(cpu_handler_fd, IRQ_SENDTO_CLIENTOS, 3); + if (ret) { + printf("send ipi tp second os failed\n"); + } + + close(cpu_handler_fd); + return; +} + +struct virtio_dispatch dispatch = { + .get_status = virtio_get_status, + .set_status = virtio_set_status, + .get_features = virtio_get_features, + .notify = virtio_notify, +}; + +int endpoint_cb(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) +{ + received_data = *((unsigned int *) data); + return RPMSG_SUCCESS; +} + +struct rpmsg_endpoint my_ept; +struct rpmsg_endpoint *ep = &my_ept; + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(ep); +} + +void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest) +{ + (void)rpmsg_create_ept(ep, rdev, name, + RPMSG_ADDR_ANY, dest, + endpoint_cb, + rpmsg_service_unbind); +} + +static unsigned int receive_message(void) +{ + int cpu_handler_fd; + struct pollfd fds; + int ret; + + cpu_handler_fd = open(DEV_CLIENT_OS_AGENT, O_RDWR); + if (cpu_handler_fd < 0) { + printf("open %s failed.\n", DEV_CLIENT_OS_AGENT); + return cpu_handler_fd; + } + + fds.fd = cpu_handler_fd; + fds.events = POLLIN; + + printf("Master core waiting for messages.... \n"); + while (1) { + ret = poll(&fds, 1, -1); + if (ret < 0) { + printf("poll error.\n"); + close(cpu_handler_fd); + return ret; + } + + if (ret == 0) { + printf("poll time out.\n"); + close(cpu_handler_fd); + return ret; + } + + if (fds.revents & POLLIN) { + printf("Master core receiving messages....\n"); + virtqueue_notification(vq[0]); + break; + } + } + close(cpu_handler_fd); + return received_data; +} + +static int send_message(unsigned int message) +{ + return rpmsg_send(ep, &message, sizeof(message)); +} + +static struct rpmsg_virtio_shm_pool shpool; + +void rpmsg_app_master(void) +{ + int status = 0; + unsigned int message = 0U; + void *tx_addr, *rx_addr, *shm_start_addr; + metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR }; + + printf("\r\nOpenAMP[master] demo started\r\n"); + + g_memfd = open("/dev/mem", O_RDWR); + tx_addr = mmap((void *)VRING_TX_ADDRESS, VDEV_STATUS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_memfd, VRING_TX_ADDRESS); + rx_addr = mmap((void *)VRING_RX_ADDRESS, VDEV_STATUS_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_memfd, VRING_RX_ADDRESS); + shm_start_addr = mmap((void *)SHM_START_ADDR, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_memfd, SHM_START_ADDR); + + io = malloc(sizeof(struct metal_io_region)); + if (!io) { + printf("malloc io failed\n"); + return; + } + metal_io_init(io, shm_start_addr, shm_physmap, SHM_SIZE, -1, 0, NULL); + + /* setup vdev */ + vq[0] = virtqueue_allocate(VRING_SIZE); + if (vq[0] == NULL) { + printf("virtqueue_allocate failed to alloc vq[0]\n"); + free(io); + return; + } + vq[1] = virtqueue_allocate(VRING_SIZE); + if (vq[1] == NULL) { + printf("virtqueue_allocate failed to alloc vq[1]\n"); + free(io); + return; + } + + vdev.role = RPMSG_MASTER; + vdev.vrings_num = VRING_COUNT; + vdev.func = &dispatch; + rvrings[0].io = io; + rvrings[0].info.vaddr = tx_addr; + rvrings[0].info.num_descs = VRING_SIZE; + rvrings[0].info.align = VRING_ALIGNMENT; + rvrings[0].vq = vq[0]; + + rvrings[1].io = io; + rvrings[1].info.vaddr = rx_addr; + rvrings[1].info.num_descs = VRING_SIZE; + rvrings[1].info.align = VRING_ALIGNMENT; + rvrings[1].vq = vq[1]; + + vdev.vrings_info = &rvrings[0]; + + /* setup rvdev */ + rpmsg_virtio_init_shm_pool(&shpool, shm_start_addr, SHM_SIZE); + status = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool); + if (status != 0) { + printf("rpmsg_init_vdev failed %d\n", status); + free(io); + return; + } + + /* Since we are using name service, we need to wait for a response + * from NS setup and than we need to process it + */ + receive_message(); + + while (message < 99) { + status = send_message(message); + if (status < 0) { + printf("send_message(%u) failed with status %d\n", + message, status); + goto _cleanup; + } + + sleep(1); + message = receive_message(); + printf("Master core received a message: %u\n", message); + + message++; + sleep(1); + } + +_cleanup: + free(io); + printf("OpenAMP demo ended.\n"); +} -- Gitee