From 92c681cc12d912c744b38ab93ce9077b444cdd39 Mon Sep 17 00:00:00 2001 From: luojects Date: Tue, 13 Sep 2022 21:25:53 +0800 Subject: [PATCH] openamp demo: support running on rpi4 * delete boot_address param * solve ko history problem: using smp_processor_id() in preemptible * introduce dts to get mcs info, including smccc method, mcsmem space * combine two ko into one: (cpu_handler, dev_mcsmem) -> mcsdev * mcs/modules/devmem2.c is a userspace memory debug tool, it runs on target * shared memory init only do once * each demo has two zephyr app: zephyr_qemu.bin and zephyr_rpi.bin, source code refer to src-openeuler/zephyr Signed-off-by: luojects --- mcs/CMakeLists.txt | 1 + mcs/README.md | 99 ++++-- mcs/dev_mcsmem/Makefile | 12 - mcs/kernel_cpu_handler/Makefile | 12 - mcs/kernel_cpu_handler/cpu_handler_dev.c | 211 ------------ mcs/mcs_km/Makefile | 6 + .../dev_mcsmem.c => mcs_km/mcs_km.c} | 303 +++++++++++------- mcs/modules/openamp_module.c | 96 +++--- mcs/modules/openamp_module.h | 2 +- mcs/modules/remoteproc_module.c | 47 ++- mcs/modules/remoteproc_module.h | 8 +- mcs/modules/rpmsg_module.c | 15 +- mcs/modules/rpmsg_module.h | 2 - mcs/modules/virtio_module.c | 43 +-- mcs/modules/virtio_module.h | 9 +- mcs/openamp_demo/rpmsg_main.c | 16 +- .../{zephyr.bin => zephyr_qemu.bin} | Bin mcs/openamp_demo/zephyr_rpi.bin | Bin 0 -> 136736 bytes mcs/rpmsg_pty_demo/rpmsg_main.c | 14 +- mcs/rpmsg_pty_demo/rpmsg_pty.c | 3 +- .../{zephyr.bin => zephyr_qemu.bin} | Bin mcs/rpmsg_pty_demo/zephyr_rpi.bin | Bin 0 -> 179672 bytes 22 files changed, 425 insertions(+), 474 deletions(-) delete mode 100644 mcs/dev_mcsmem/Makefile delete mode 100644 mcs/kernel_cpu_handler/Makefile delete mode 100644 mcs/kernel_cpu_handler/cpu_handler_dev.c create mode 100644 mcs/mcs_km/Makefile rename mcs/{dev_mcsmem/dev_mcsmem.c => mcs_km/mcs_km.c} (35%) rename mcs/openamp_demo/{zephyr.bin => zephyr_qemu.bin} (100%) create mode 100644 mcs/openamp_demo/zephyr_rpi.bin rename mcs/rpmsg_pty_demo/{zephyr.bin => zephyr_qemu.bin} (100%) create mode 100644 mcs/rpmsg_pty_demo/zephyr_rpi.bin diff --git a/mcs/CMakeLists.txt b/mcs/CMakeLists.txt index a23538da..bce56863 100644 --- a/mcs/CMakeLists.txt +++ b/mcs/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.19) project(openamp_demo) set(CMAKE_C_COMPILER ${CC}) +set(CMAKE_CXX_COMPILER ${CXX}) message(STATUS "SDKTARGETSYSROOT=$ENV{SDKTARGETSYSROOT}") message(STATUS "DEMO_TARGET=${DEMO_TARGET}") diff --git a/mcs/README.md b/mcs/README.md index 128cbc22..c8feee30 100644 --- a/mcs/README.md +++ b/mcs/README.md @@ -6,11 +6,15 @@ #### 软件架构 -kernel_cpu_handler: 提供OpenAMP所需内核模块,支持Client OS启动、专用中断收发等功能。 +mcs_km: 提供OpenAMP所需内核模块,支持Client OS启动、专用中断收发、管理保留内存等功能。 openamp_demo: 提供OpenAMP用户态程序Linux端样例,支持与指定Client OS进行通信。 -zephyr: 提供样例zephyr.bin镜像文件,该文件需要被加载至0x7a000000的起始地址,并在0x7a000ffc的地址进行启动。启动后会运行OpenAMP Client端的样例程序,并与Linux端进行交互。 +rpmsg_pty_demo: 提供OpenAMP用户态程序Linux端样例,支持通过shell命令行访问Client OS。 + +modules: 提供OpenAMP样例必需的模块remoteproc、virtio、rpmsg、openamp,这些模块静态编译成libopenamp.a。 + +zephyr: 提供样例镜像文件,在每个demo中,zephyr_qemu.bin运行在qemu上,zephyr_rpi.bin运行在树莓派上,该文件需要被加载至设定的0x7a000000起始地址。启动后会运行OpenAMP Client端的样例程序,并与Linux端进行交互。 #### 原理简介 @@ -26,20 +30,20 @@ OpenAMP包括如下三大重要组件: -remoteproc:该组件用于主机上实现对远程处理器及相关软件环境的生命周期管理、及virtio和rpmsg设备的注册等。 -样例Demo通过提供cpu_handler_dev内核KO模块实现Linux内核启动从核的功能,并预留了OpenAMP通信所需的专用中断及其收发机制。用户可在用户态通过dev设备实现Client OS的启动,并通过rpmsg组件实现与Client OS的简单通信。 +样例Demo通过提供mcs_km内核KO模块实现Linux内核启动从核的功能,并预留了OpenAMP通信所需的专用中断及其收发机制。用户可在用户态通过dev设备实现Client OS的启动,并通过rpmsg组件实现与Client OS的简单通信。 #### 安装教程 1. 根据openEuler Embedded使用手册安装SDK并设置SDK环境变量。 -2. 编译内核模块cpu_handler_dev.ko,编译方式如下: +2. 编译内核模块mcs_km.ko,编译方式如下: ```` - cd kernel_cpu_handler + cd mcs_km make ```` -3. 编译用户态程序rpmsg_main,编译方式如下: +3. 编译openamp_demo用户态程序rpmsg_main,编译方式如下: ```` cmake -S . -B build -DDEMO_TARGET=openamp_demo @@ -48,14 +52,15 @@ OpenAMP包括如下三大重要组件: ```` 注意:此处定义OpenAMP通信设备共享内存起始地址为0x70000000,可根据实际内存分配进行修改。 +如果需要查看调试日志,可以给cmake增加-DDEBUG参数。 -4. 将编译好的KO模块、用户态程序,以及zephyr.bin镜像拷贝到openEuler Embedded系统的目录下。如何拷贝可以参考使用手册中共享文件系统场景。 +4. 将编译好的KO模块、用户态程序,以及zephyr_qemu.bin镜像拷贝到openEuler Embedded系统的目录下。如何拷贝可以参考使用手册中共享文件系统场景。 -5. 将OpenAMP的依赖库libmetal.so*,libopen_amp.so*,libsysfs.so*拷贝至文件系统/lib64目录。对应so可在sdk目录中找到,如何拷贝可以参考使用手册中共享文件系统场景。 +5. 将OpenAMP的依赖库libmetal.so*,libopen_amp.so*拷贝至文件系统/usr/lib64目录,libsysfs.so* 拷贝至文件系统/lib64目录。对应so可在sdk目录中找到,如何拷贝可以参考使用手册中共享文件系统场景。 #### 使用说明 -1. 通过QEMU启动openEuler Embedded镜像,如何启动可参考使用手册中QEMU使用与调试章节。 +1. 通过QEMU启动openEuler Embedded镜像,如何启动可参考使用手册中QEMU使用与调试章节。树莓派跳过这一步。 -以上述demo为例,需要预留出地址0x70000000为起始的内存用于OpenAMP demo和Client OS启动。通过QEMU启动时,当指定-m 1G时默认使用0x40000000-0x80000000的系统内存。添加内核启动参数mem=768M,可预留地址为0x70000000-0x80000000的256M内存。 -在样例中在cpu 3启动Client OS,需要预留出3号cpu。 @@ -64,32 +69,86 @@ OpenAMP包括如下三大重要组件: 可参考如下命令进行启动: ```` - qemu-system-aarch64 -M virt,gic-version=3 -m 1G -cpu cortex-a57 -nographic -kernel zImage -initrd initrd -append 'mem=768M maxcpus=3' -smp 4 + qemu-system-aarch64 -M virt,gic-version=3 -m 1G -cpu cortex-a57 -nographic -kernel zImage -initrd *.rootfs.cpio.gz -append 'mem=768M maxcpus=3' -smp 4 ```` 当使用的QEMU版本过老时可能会由于内存分布不一致导致段错误,可升级QEMU版本或手动修改DTB空出对应内存。 -2. 在openEuler Embedded系统上插入内核KO模块cpu_handler_dev.ko。 +2. 在openEuler Embedded系统上插入内核KO模块mcs_km.ko。 ```` - insmod cpu_handler_dev.ko + insmod mcs_km.ko ```` 3. 运行rpmsg_main程序,使用方式如下: ```` - ./rpmsg_main -c [cpu_id] -b [boot_address] -t [target_binfile] -a [target_binaddress] + ./rpmsg_main -c [cpu_id] -t [target_binfile] -a [target_binaddress] eg: - ./rpmsg_main -c 3 -b 0x7a000ffc -t /tmp/zephyr.bin -a 0x7a000000 + ./rpmsg_main -c 3 -t zephyr_qemu.bin -a 0x7a000000 +```` + +此处定义Client OS起始地址为0x7a000000,Client OS镜像名为zephyr_qemu.bin,Client OS从3号cpu启动。树莓派换成zephyr_rpi.bin。 + +#### 用户样例开发 +mcs提供了4个API,供用户做样例开发,用户无需感知OpenAMP实现细节,接口定义在openamp_module.h。 + +1. openamp_init: 初始化保留内存,加载zephyr镜像文件,初始化remoteproc、virtio、rpmsg,建立Linux与Client OS两端配对的endpoint,供消息收发使用。 +```` + int openamp_init(void); +```` + +2. receive_message: Linux端接收消息。 +```` + int receive_message(unsigned char *message, int message_len, int *real_len); +```` + message: 用户传入的消息接收buffer + message_len: 接收buffer的长度 + real_len: 实际接收到的消息长度 + +3. send_message: Linux端发送消息。 +```` + int send_message(unsigned char *message, int len); +```` + message: 用户传入的消息发送buffer + len: 发送buffer的长度 + +4. openamp_deinit: 释放openamp资源。 +```` + void openamp_deinit(void); ```` -此处定义Client OS起始地址为0x7a000000,启动地址为0x7a000ffc,Client OS镜像路径为/tmp/zephyr.bin。 +#### 串口服务样例 +rpmsg_pty_demo包含2个源文件,实现通过Linux shell命令行访问Client OS的功能,样例支持多用户多线程场景。 + +rpmsg_main.c: 初始化OpenAMP,用户创建线程,在线程中运行shell程序。 + +rpmsg_pty.c: 用户创建PTY虚拟串口设备,将PTY的slave端作为shell,PTY的master端与OpenAMP Endpint节点通信。 + +1. 安装教程,其他步骤与openamp_demo相同,把DEMO_TARGET换成rpmsg_pty_demo编译。 +```` + cmake -S . -B build -DDEMO_TARGET=rpmsg_pty_demo +```` -#### 用户开发 -参考demo实现流程,当前提供了多个API,包含在libopenamp.a中,供用户使用和二次开发,用户无需感知openamp实现细节。 +2. 使用说明,为了不影响shell的使用,建议启动qemu时加上console=ttyAMA1内核参数,将内核打印屏蔽。 +```` + qemu-system-aarch64 -M virt,gic-version=3 -m 1G -cpu cortex-a57 -nographic -kernel zImage -initrd *.rootfs.cpio.gz -append 'mem=768M maxcpus=3 console=ttyAMA1' -smp 4 +```` -1. 调用openamp_init,初始化remoteproc、virtio、rpmsg,Linux端与Client OS建立配对的endpoint,供消息收发使用。 +树莓派可以通过设置printk在console上的打印优先级屏蔽内核打印。 +```` + cat /proc/sys/kernel/printk > /tmp/printk_bak + echo 1 4 1 7 > /proc/sys/kernel/printk +```` -2. 调用receive_message、send_message收发消息。 +3. 运行rpmsg_main程序时,把程序放在后台,然后通过screen打开shell。 +```` + ./rpmsg_main -c 3 -t zephyr_qemu.bin -a 0x7a000000 & + screen /dev/pts/0 +```` -3. 调用openamp_deinit,释放openamp资源。 \ No newline at end of file +4. 进入shell,输入help可以查看Client OS支持哪些命令,输入history可以查看历史命令,ctrl+d退出。 +```` + uart:~$ help + uart:~$ history +```` \ No newline at end of file diff --git a/mcs/dev_mcsmem/Makefile b/mcs/dev_mcsmem/Makefile deleted file mode 100644 index fe080afb..00000000 --- a/mcs/dev_mcsmem/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -KERNELDIR := ${KERNEL_SRC_DIR} -CURRENT_PATH := $(shell pwd) - -target := dev_mcsmem -obj-m := $(target).o - -build := kernel_modules - -kernel_modules: - $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules -clean: - $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean diff --git a/mcs/kernel_cpu_handler/Makefile b/mcs/kernel_cpu_handler/Makefile deleted file mode 100644 index 28b37108..00000000 --- a/mcs/kernel_cpu_handler/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -KERNELDIR := ${KERNEL_SRC_DIR} -CURRENT_PATH := $(shell pwd) - -target := cpu_handler_dev -obj-m := $(target).o - -build := kernel_modules - -kernel_modules: - $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules -clean: - $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean diff --git a/mcs/kernel_cpu_handler/cpu_handler_dev.c b/mcs/kernel_cpu_handler/cpu_handler_dev.c deleted file mode 100644 index a646851a..00000000 --- a/mcs/kernel_cpu_handler/cpu_handler_dev.c +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CPU_HANDLER_DEVICE "cpu_handler" -#define CPU_ON_FUNCID 0xC4000003 -#define IRQ_SENDTO_CLIENTOS _IOW('A', 0, int) -#define OPENAMP_INIT_TRIGGER 0x0 -#define OPENAMP_CLIENTOS_TRIGGER 0x1 -#define OPENAMP_IRQ 8 - -static DEFINE_MUTEX(cpu_handler_mutex); -static struct class *cpu_handler_class; -static int cpu_handler_major; -static int cpu_state; - -static ssize_t dev_cpuhandler_write(struct file *file, const char __user *buf, - size_t datalen, loff_t *ppos) -{ - ssize_t result = -EINVAL; - int cpu_id; - phys_addr_t cpu_boot_addr; - char *data = NULL, *startp = NULL, *endp = NULL; - struct arm_smccc_res res; - - mutex_lock(&cpu_handler_mutex); - - if (cpu_state == 1) { - pr_info("cpu_handler: clientos os already boot\n"); - goto out; - } - - data = memdup_user_nul(buf, datalen); - if (!data) { - pr_err("cpu_handler: invalid input data\n"); - goto out; - } - - startp = data; - cpu_id = simple_strtol(startp, &endp, 0); - if (*endp != '@') { - pr_err("cpu_handler: invalid boot cpu format, str = %s\n", data); - goto out; - } - startp = endp + 1; - -#ifdef CONFIG_PHYS_ADDR_T_64BIT - cpu_boot_addr = simple_strtoull(startp, &endp, 0); -#else - cpu_boot_addr = simple_strtoul(startp, &endp, 0); -#endif - - if (startp == endp) { - pr_err("cpu_handler: invalid boot cpu format, str = %s\n", data); - goto out; - } - - if (cpu_state == 0) { - pr_info("cpu_handler: start booting clientos os on cpu(%d)\n", cpu_id); - arm_smccc_hvc(CPU_ON_FUNCID, cpu_id, cpu_boot_addr, 0, 0, 0, 0, 0, &res); - cpu_state = 1; - result = datalen; - } -out: - mutex_unlock(&cpu_handler_mutex); - return result; -} - -static DECLARE_WAIT_QUEUE_HEAD(openamp_trigger_wait); - -static int openamp_trigger; - -static irqreturn_t handle_clientos_ipi(int irq, void *data) -{ - pr_info("cpu_handler: received ipi from client os\n"); - openamp_trigger = OPENAMP_CLIENTOS_TRIGGER; - wake_up_interruptible(&openamp_trigger_wait); - return IRQ_HANDLED; -} - -static struct irq_desc *openamp_ipi_desc; - -void set_openamp_ipi(void) -{ - int err; - - err = request_percpu_irq(OPENAMP_IRQ, handle_clientos_ipi, "IPI", &cpu_number); - if (err) { - pr_err("cpu_handler: request openamp irq failed(%d)\n", err); - return; - } - - openamp_ipi_desc = irq_to_desc(OPENAMP_IRQ); - if (!openamp_ipi_desc) { - pr_err("cpu_handler: generate openamp irq descriptor failed\n"); - return; - } - - enable_percpu_irq(OPENAMP_IRQ, 0); - - return; -} - -static void send_clientos_ipi(const struct cpumask *target) -{ - ipi_send_mask(OPENAMP_IRQ, target); -} - -static ssize_t dev_cpuhandler_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - ssize_t len; - char res[32]; - - len = scnprintf(res, sizeof(res), "%d\n", cpu_state); - return simple_read_from_buffer(buf, count, ppos, res, len); -} - -static unsigned int dev_cpuhandler_poll(struct file *file, poll_table *wait) -{ - unsigned int mask; - - poll_wait(file, &openamp_trigger_wait, wait); - mask = 0; - if (openamp_trigger == OPENAMP_CLIENTOS_TRIGGER) - mask |= POLLIN | POLLRDNORM; - - openamp_trigger = OPENAMP_INIT_TRIGGER; - return mask; -} - -static long dev_cpuhandler_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - int cpu_id = (int)arg; - switch (cmd) { - case IRQ_SENDTO_CLIENTOS: - pr_info("cpu_handler: received ioctl cmd to send ipi to cpu(%d)\n", cpu_id); - send_clientos_ipi(cpumask_of(cpu_id)); - return 0; - default: - return -EINVAL; - } -} - -static const struct file_operations dev_cpuhandler_fops = { - .read = dev_cpuhandler_read, - .write = dev_cpuhandler_write, - .poll = dev_cpuhandler_poll, - .unlocked_ioctl = dev_cpuhandler_ioctl, - .llseek = generic_file_llseek, -}; - -static __init int cpu_handler_dev_init(void) -{ - struct device *class_dev = NULL; - int ret = 0; - - cpu_handler_major = register_chrdev(0, CPU_HANDLER_DEVICE, &dev_cpuhandler_fops); - if (cpu_handler_major < 0) { - pr_err("cpu_handler: unable to get major %d for memory devs.\n", cpu_handler_major); - return -1; - } - - cpu_handler_class = class_create(THIS_MODULE, CPU_HANDLER_DEVICE); - if (IS_ERR(cpu_handler_class)) { - ret = PTR_ERR(cpu_handler_class); - goto error_class_create; - } - - class_dev = device_create(cpu_handler_class, NULL, MKDEV((unsigned int)cpu_handler_major, 1), - NULL, CPU_HANDLER_DEVICE); - if (unlikely(IS_ERR(class_dev))) { - ret = PTR_ERR(class_dev); - goto error_device_create; - } - - set_openamp_ipi(); - pr_info("cpu_handler: create major %d for cpu handler devs\n", cpu_handler_major); - return 0; - -error_device_create: - class_destroy(cpu_handler_class); -error_class_create: - unregister_chrdev(cpu_handler_major, CPU_HANDLER_DEVICE); - return ret; -} -module_init(cpu_handler_dev_init); - -static void __exit cpu_handler_dev_exit(void) -{ - device_destroy(cpu_handler_class, MKDEV((unsigned int)cpu_handler_major, 1)); - class_destroy(cpu_handler_class); - unregister_chrdev(cpu_handler_major, CPU_HANDLER_DEVICE); -} -module_exit(cpu_handler_dev_exit); - -MODULE_AUTHOR("OpenEuler Embedded"); -MODULE_DESCRIPTION("cpu_handler device"); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/mcs/mcs_km/Makefile b/mcs/mcs_km/Makefile new file mode 100644 index 00000000..f9b3396c --- /dev/null +++ b/mcs/mcs_km/Makefile @@ -0,0 +1,6 @@ +obj-m := mcs_km.o + +all: + $(MAKE) -C $(KERNEL_SRC_DIR) M=$(PWD) modules +clean: + $(MAKE) -C $(KERNEL_SRC_DIR) M=$(PWD) clean diff --git a/mcs/dev_mcsmem/dev_mcsmem.c b/mcs/mcs_km/mcs_km.c similarity index 35% rename from mcs/dev_mcsmem/dev_mcsmem.c rename to mcs/mcs_km/mcs_km.c index e3204b1f..de1dd160 100644 --- a/mcs/dev_mcsmem/dev_mcsmem.c +++ b/mcs/mcs_km/mcs_km.c @@ -1,54 +1,139 @@ -/* - * A lite version of linux/drivers/char/mem.c - * Test with MMU for arm64 mcs functions - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include - +#include #include #include #include -#define DEVMEM_MINOR 1 +#define MCS_DEVICE_NAME "mcs" +#define CPU_ON_FUNCID 0xC4000003 + +#define MAGIC_NUMBER 'A' +#define IOC_SENDIPI _IOW(MAGIC_NUMBER, 0, int) +#define IOC_CPUON _IOW(MAGIC_NUMBER, 1, int) +#define IOC_MAXNR 2 + +#define OPENAMP_INIT_TRIGGER 0x0 +#define OPENAMP_CLIENTOS_TRIGGER 0x1 +#define OPENAMP_IRQ 8 + #define START_INDEX 1 -#define SIZE_INDEX 2 +#define SIZE_INDEX 2 + +static struct class *mcs_class; +static int mcs_major; +static int cpu_state; + static u64 valid_start; static u64 valid_end; +static const char *smccc_method; -static ssize_t read_mem(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static DECLARE_WAIT_QUEUE_HEAD(openamp_trigger_wait); +static int openamp_trigger; + +static irqreturn_t handle_clientos_ipi(int irq, void *data) { - return -ENOSYS; + pr_info("mcs_km: received ipi from client os\n"); + openamp_trigger = OPENAMP_CLIENTOS_TRIGGER; + wake_up_interruptible(&openamp_trigger_wait); + return IRQ_HANDLED; } -static ssize_t write_mem(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +void set_openamp_ipi(void) { - return -ENOSYS; + int err; + + err = request_percpu_irq(OPENAMP_IRQ, handle_clientos_ipi, "IPI", &cpu_number); + if (err) { + pr_err("mcs_km: request openamp irq failed(%d)\n", err); + return; + } + + preempt_disable(); /* fix kernel err message: using smp_processor_id() in preemptible */ + enable_percpu_irq(OPENAMP_IRQ, 0); + preempt_enable(); + + return; +} + +static void send_clientos_ipi(const struct cpumask *target) +{ + ipi_send_mask(OPENAMP_IRQ, target); +} + +static ssize_t mcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + ssize_t len; + char res[32]; + + len = scnprintf(res, sizeof(res), "%d\n", cpu_state); + return simple_read_from_buffer(buf, count, ppos, res, len); +} + +static unsigned int mcs_poll(struct file *file, poll_table *wait) +{ + unsigned int mask; + + poll_wait(file, &openamp_trigger_wait, wait); + mask = 0; + if (openamp_trigger == OPENAMP_CLIENTOS_TRIGGER) + mask |= POLLIN | POLLRDNORM; + + openamp_trigger = OPENAMP_INIT_TRIGGER; + return mask; +} + +static long mcs_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int err; + int cpu_id, cpu_boot_addr; + struct arm_smccc_res res; + + if (_IOC_TYPE(cmd) != MAGIC_NUMBER) + return -EINVAL; + if (_IOC_NR(cmd) > IOC_MAXNR) + return -EINVAL; + err = !access_ok((void*)arg, _IOC_SIZE(cmd)); + if (err) + return -EINVAL; + + switch (cmd) { + case IOC_SENDIPI: + cpu_id = (int)arg; + pr_info("mcs_km: received ioctl cmd to send ipi to cpu(%d)\n", cpu_id); + send_clientos_ipi(cpumask_of(cpu_id)); + return 0; + case IOC_CPUON: + cpu_id = *(unsigned int*)arg; + cpu_boot_addr = *((unsigned int*)arg + 1); + + if (cpu_state == 1) { + pr_info("mcs_km: clientos already boot on cpu(%d)\n", cpu_id); + return 0; + } + + if (smccc_method == NULL) + smccc_method = "hvc"; + + if (cpu_state == 0) { + pr_info("mcs_km: start booting clientos on cpu(%d) addr(0x%x) smccc(%s)\n", cpu_id, cpu_boot_addr, smccc_method); + if (strcmp(smccc_method, "smc") == 0) + arm_smccc_smc(CPU_ON_FUNCID, cpu_id, cpu_boot_addr, 0, 0, 0, 0, 0, &res); + else + arm_smccc_hvc(CPU_ON_FUNCID, cpu_id, cpu_boot_addr, 0, 0, 0, 0, 0, &res); + cpu_state = 1; + } + return 0; + default: + return -EINVAL; + } } #ifdef CONFIG_STRICT_DEVMEM @@ -105,7 +190,8 @@ static const struct vm_operations_struct mmap_mem_ops = { #endif }; -static int mmap_mem(struct file *file, struct vm_area_struct *vma) +/* A lite version of linux/drivers/char/mem.c, Test with MMU for arm64 mcs functions */ +static int mcs_mmap(struct file *file, struct vm_area_struct *vma) { size_t size = vma->vm_end - vma->vm_start; phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; @@ -139,67 +225,22 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) return 0; } -static loff_t memory_lseek(struct file *file, loff_t offset, int orig) -{ - return -ENOSYS; -} - -static int open_port(struct inode *inode, struct file *filp) +static int mcs_open(struct inode *inode, struct file *filp) { if (!capable(CAP_SYS_RAWIO)) return -EPERM; return 0; } -#define open_mem open_port -static const struct file_operations __maybe_unused mem_fops = { - .llseek = memory_lseek, - .read = read_mem, - .write = write_mem, - .mmap = mmap_mem, - .open = open_mem, -}; - -static const struct memdev { - const char *name; - umode_t mode; - const struct file_operations *fops; - fmode_t fmode; -} devlist[] = { - [DEVMEM_MINOR] = { "mcsmem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, +static const struct file_operations mcs_fops = { + .open = mcs_open, + .mmap = mcs_mmap, + .read = mcs_read, + .poll = mcs_poll, + .unlocked_ioctl = mcs_ioctl, + .llseek = generic_file_llseek, }; -static int memory_open(struct inode *inode, struct file *filp) -{ - int minor; - const struct memdev *dev; - - minor = iminor(inode); - if (minor >= ARRAY_SIZE(devlist)) - return -ENXIO; - - dev = &devlist[minor]; - if (!dev->fops) - return -ENXIO; - - filp->f_op = dev->fops; - filp->f_mode |= dev->fmode; - - if (dev->fops->open) - return dev->fops->open(inode, filp); - - return 0; -} - -static const struct file_operations memory_fops = { - .open = memory_open, - .llseek = noop_llseek, -}; - -static struct class *mem_class; - -int mcsmem_major; - static int get_mcs_node_info(void) { int ret = 0; @@ -207,15 +248,15 @@ static int get_mcs_node_info(void) u8 datasize; u32 *val = NULL; - nd = of_find_node_by_path("/reserved-memory/mcs@70000000"); - if(nd == NULL) { - printk("invalid reserved-memory mcs node.\n"); + nd = of_find_compatible_node(NULL, NULL, "mcs_mem"); + if (nd == NULL) { + pr_info("no reserved-memory mcs node.\n"); return -EINVAL; } datasize = of_property_count_elems_of_size(nd, "reg", sizeof(u32)); if (datasize != 3) { - printk("invalid reserved-memory mcs reg size.\n"); + pr_err("invalid reserved-memory mcs reg size.\n"); return -EINVAL; } @@ -230,36 +271,72 @@ static int get_mcs_node_info(void) valid_start = (u64)(*(val + START_INDEX)); valid_end = valid_start + (u64)(*(val + SIZE_INDEX)); + ret = of_property_read_string(nd, "smccc", &smccc_method); + if (ret < 0) + goto out; + out: kfree(val); return ret; } -static int __init mcsmem_dev_init(void) +static int __init mcs_dev_init(void) { - int minor = 1; + struct device *class_dev = NULL; + int ret = 0; + + mcs_major = register_chrdev(0, MCS_DEVICE_NAME, &mcs_fops); + if (mcs_major < 0) { + pr_err("mcs_km: unable to get major %d for memory devs.\n", mcs_major); + return -1; + } + + mcs_class = class_create(THIS_MODULE, MCS_DEVICE_NAME); + if (IS_ERR(mcs_class)) { + ret = PTR_ERR(mcs_class); + goto error_class_create; + } + + class_dev = device_create(mcs_class, NULL, MKDEV((unsigned int)mcs_major, 1), + NULL, MCS_DEVICE_NAME); + if (unlikely(IS_ERR(class_dev))) { + ret = PTR_ERR(class_dev); + goto error_device_create; + } + + set_openamp_ipi(); if (get_mcs_node_info() < 0) - printk("cat't get mcsmem dts node info. Allow page isn't ram mmap.\n"); + pr_info("there's no mcsmem dts node info. Allow page isn't ram mmap.\n"); else - printk("valid mcsmem node decated.\n"); + pr_info("valid mcsmem node detected.\n"); - mcsmem_major = register_chrdev(0, "mcsmem", &memory_fops); - if (mcsmem_major < 0) { - printk("unable to get major %d for memory devs\n", mcsmem_major); - return -1; - } + pr_info("mcs_km: create major %d for mcs dev.\n", mcs_major); + return 0; - mem_class = class_create(THIS_MODULE, "mcsmem"); - if (IS_ERR(mem_class)) - return PTR_ERR(mem_class); +error_device_create: + class_destroy(mcs_class); +error_class_create: + unregister_chrdev(mcs_major, MCS_DEVICE_NAME); + return ret; +} +module_init(mcs_dev_init); + +static void __exit mcs_dev_exit(void) +{ + preempt_disable(); + disable_percpu_irq(OPENAMP_IRQ); + preempt_enable(); + free_percpu_irq(OPENAMP_IRQ, &cpu_number); - device_create(mem_class, NULL, MKDEV((unsigned int)mcsmem_major, minor), - NULL, devlist[minor].name); + device_destroy(mcs_class, MKDEV((unsigned int)mcs_major, 1)); + class_destroy(mcs_class); + unregister_chrdev(mcs_major, MCS_DEVICE_NAME); - return 0; + pr_info("mcs_km: remove mcs dev.\n"); } +module_exit(mcs_dev_exit); -module_init(mcsmem_dev_init); -MODULE_DESCRIPTION("dev_mcsmem"); -MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("OpenEuler Embedded"); +MODULE_DESCRIPTION("mcs device"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/mcs/modules/openamp_module.c b/mcs/modules/openamp_module.c index 20ce22ae..be788c19 100644 --- a/mcs/modules/openamp_module.c +++ b/mcs/modules/openamp_module.c @@ -1,37 +1,56 @@ #include #include "openamp_module.h" -#define MAX_BIN_BUFLEN (10 * 1024 * 1024) +#define MCS_DEVICE_NAME "/dev/mcs" +#define STR_TO_HEX 16 +#define PAGE_SIZE 4096 +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGE_ALIGN(addr) ((addr & PAGE_MASK) + PAGE_SIZE) -static int load_bin(void) +static int memfd; +static void *binaddr; +static int binsize; +void *shmaddr; + +static int reserved_mem_init(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); + int binfd; + struct stat buf; + void *file_addr; + + /* open memfd */ + memfd = open(MCS_DEVICE_NAME, O_RDWR | O_SYNC); + if (memfd < 0) { + printf("mcsmem open failed: %d\n", memfd); + return -1; } - bin_buffer = (void *)malloc(MAX_BIN_BUFLEN); - if (!bin_buffer) { - printf("malloc bin_buffer failed\n"); - exit(-1); + /* open clientos bin file */ + binfd = open(target_binfile, O_RDONLY); + if (binfd < 0) { + printf("open %s failed, binfd:%d\n", target_binfile, binfd); + return -1; } - bin_size = read(bin_fd, bin_buffer, MAX_BIN_BUFLEN); - if (bin_size == 0) { - printf("read bin file failed\n"); - exit(-1); + /* shared memory for virtio */ + shmaddr = mmap(NULL, VDEV_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, VDEV_START_ADDR); + memset(shmaddr, 0, VDEV_SIZE); + + /* memory for loading clientos bin file */ + fstat(binfd, &buf); + binsize = PAGE_ALIGN(buf.st_size); + binaddr = mmap(NULL, binsize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, strtol(target_binaddr, NULL, STR_TO_HEX)); + memset(binaddr, 0, binsize); + + if (shmaddr < 0 || binaddr < 0) { + printf("mmap reserved mem failed: shmaddr:%p, binaddr:%p\n", shmaddr, binaddr); + return -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); - free(bin_buffer); + /* load clientos */ + file_addr = mmap(NULL, binsize, PROT_READ, MAP_PRIVATE, binfd, 0); + memcpy(binaddr, file_addr, binsize); + return 0; } @@ -42,25 +61,25 @@ int openamp_init(void) unsigned char message[100]; int len; + ret = reserved_mem_init(); + if (ret) { + printf("failed to init reserved mem\n"); + return ret; + } + rproc = create_remoteproc(); if (!rproc) { printf("create remoteproc 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); /* wait for zephyr booting */ + sleep(5); /* wait for clientos booting */ virtio_init(); (void)receive_message(message, sizeof(message), &len); /* name service: endpoint matching */ @@ -68,14 +87,17 @@ int openamp_init(void) return 0; } -int openamp_deinit(void) +void openamp_deinit(void) { printf("\nOpenAMP demo ended.\n"); - if (io) - free(io); - remoteproc_stop(&rproc_inst); - rproc_inst.state = RPROC_OFFLINE; - remoteproc_remove(&rproc_inst); - return 0; + virtio_deinit(); + destory_remoteproc(); + + if (shmaddr) + munmap(shmaddr, VDEV_SIZE); + if (binaddr) + munmap(binaddr, binsize); + if (memfd) + close(memfd); } diff --git a/mcs/modules/openamp_module.h b/mcs/modules/openamp_module.h index 69abb2c9..90261620 100644 --- a/mcs/modules/openamp_module.h +++ b/mcs/modules/openamp_module.h @@ -13,7 +13,7 @@ extern char *target_binaddr; int openamp_init(void); /* release openamp resource */ -int openamp_deinit(void); +void openamp_deinit(void); /* message standard receive interface */ int receive_message(unsigned char *message, int message_len, int *real_len); diff --git a/mcs/modules/remoteproc_module.c b/mcs/modules/remoteproc_module.c index f6efca0b..7cb1f65e 100644 --- a/mcs/modules/remoteproc_module.c +++ b/mcs/modules/remoteproc_module.c @@ -1,9 +1,13 @@ #include #include #include +#include #include "remoteproc_module.h" -#define BOOTCMD_MAXSIZE 100 +#define MCS_DEVICE_NAME "/dev/mcs" +#define IOC_CPUON _IOW('A', 1, int) +#define STR_TO_HEX 16 +#define STR_TO_DEC 10 struct rproc_priv { struct remoteproc *rproc; /* pass a remoteproc instance pointer */ @@ -12,7 +16,7 @@ struct rproc_priv { unsigned int boot_address; /* related arg: boot address(in hex format) */ }; -struct remoteproc rproc_inst; +static struct remoteproc rproc_inst; static struct remoteproc *rproc_init(struct remoteproc *rproc, const struct remoteproc_ops *ops, void *args) @@ -42,30 +46,31 @@ static void rproc_remove(struct remoteproc *rproc) static int rproc_start(struct remoteproc *rproc) { - int cpu_handler_fd; int ret; - char on[BOOTCMD_MAXSIZE]; + unsigned int boot_args[2]; struct rproc_priv *args = (struct rproc_priv *)rproc->priv; - (void)snprintf(on, sizeof(on), "%d%s%d", args->cpu_id, "@", args->boot_address); + int fd = open(MCS_DEVICE_NAME, O_RDWR); + if (fd < 0) { + printf("failed to open %s device.\n", MCS_DEVICE_NAME); + return fd; + } - 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; + boot_args[0] = args->cpu_id; + boot_args[1] = args->boot_address; + ret = ioctl(fd, IOC_CPUON, boot_args); + if (ret) { + printf("boot clientos failed\n"); + return ret; } - ret = write(cpu_handler_fd, on, sizeof(on)); + close(fd); return 0; } static int rproc_stop(struct remoteproc *rproc) { -#if 0 - /* send message to zephyr, zephyr shut itself down by PSCI */ - int ret = send_message("shutdown\r\n", 10); - sleep(3); -#endif + /* TODO: send message to clientos, clientos shut itself down by PSCI */ return 0; } @@ -83,11 +88,19 @@ struct remoteproc *create_remoteproc(void) args.rproc = &rproc_inst; args.idx = 1; - args.cpu_id = strtol(cpu_id, NULL, 10); - args.boot_address = strtol(boot_address, NULL, 16); + args.cpu_id = strtol(cpu_id, NULL, STR_TO_DEC); + args.boot_address = strtol(target_binaddr, NULL, STR_TO_HEX); rproc = remoteproc_init(&rproc_inst, &rproc_ops, &args); if (!rproc) return NULL; return rproc; } + +void destory_remoteproc(void) +{ + remoteproc_stop(&rproc_inst); + rproc_inst.state = RPROC_OFFLINE; + if (rproc_inst.priv) + remoteproc_remove(&rproc_inst); +} diff --git a/mcs/modules/remoteproc_module.h b/mcs/modules/remoteproc_module.h index 7a54f471..7b1378ba 100644 --- a/mcs/modules/remoteproc_module.h +++ b/mcs/modules/remoteproc_module.h @@ -3,8 +3,6 @@ #include -#define DEV_CLIENT_OS_AGENT "/dev/cpu_handler" - /* create remoteproc */ struct remoteproc *create_remoteproc(void); @@ -23,8 +21,10 @@ struct remoteproc *create_remoteproc(void); int remoteproc_remove(struct remoteproc *rproc); */ +/* destory remoteproc */ +void destory_remoteproc(void); + extern char *cpu_id; -extern char *boot_address; -extern struct remoteproc rproc_inst; +extern char *target_binaddr; #endif \ No newline at end of file diff --git a/mcs/modules/rpmsg_module.c b/mcs/modules/rpmsg_module.c index a97c34bb..56ffb52f 100644 --- a/mcs/modules/rpmsg_module.c +++ b/mcs/modules/rpmsg_module.c @@ -4,6 +4,8 @@ #include "virtio_module.h" #include "rpmsg_module.h" +#define MCS_DEVICE_NAME "/dev/mcs" + static unsigned char received_data[2048] = {0}; static unsigned int received_len = 0; struct rpmsg_endpoint ept_inst; @@ -37,9 +39,9 @@ int receive_message(unsigned char *message, int message_len, int *real_len) int cpu_handler_fd; struct pollfd fds; - cpu_handler_fd = open(DEV_CLIENT_OS_AGENT, O_RDWR); + cpu_handler_fd = open(MCS_DEVICE_NAME, O_RDWR); if (cpu_handler_fd < 0) { - printf("receive_message: open %s failed.\n", DEV_CLIENT_OS_AGENT); + printf("receive_message: open %s device failed.\n", MCS_DEVICE_NAME); return cpu_handler_fd; } @@ -51,6 +53,9 @@ int receive_message(unsigned char *message, int message_len, int *real_len) received_len = 0; while (1) { +#ifdef DEBUG + printf("master waiting for message....\n"); +#endif ret = poll(&fds, 1, 100); /* 100ms timeout */ if (ret < 0) { printf("receive_message: poll failed.\n"); @@ -59,10 +64,16 @@ int receive_message(unsigned char *message, int message_len, int *real_len) } if (ret == 0) { +#ifdef DEBUG + printf("master waiting for message timeout....\n"); +#endif break; } if (fds.revents & POLLIN) { +#ifdef DEBUG + printf("master receiving message....\n"); +#endif virtqueue_notification(vq[0]); /* will call endpoint_cb */ } } diff --git a/mcs/modules/rpmsg_module.h b/mcs/modules/rpmsg_module.h index cb700df2..9cba4780 100644 --- a/mcs/modules/rpmsg_module.h +++ b/mcs/modules/rpmsg_module.h @@ -3,8 +3,6 @@ #include -#define DEV_CLIENT_OS_AGENT "/dev/cpu_handler" - /* callback of ns */ void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest); diff --git a/mcs/modules/virtio_module.c b/mcs/modules/virtio_module.c index 418aaedb..892bd046 100644 --- a/mcs/modules/virtio_module.c +++ b/mcs/modules/virtio_module.c @@ -5,6 +5,10 @@ #include "rpmsg_module.h" #include "virtio_module.h" +#define IOC_SENDIPI _IOW('A', 0, int) +#define MCS_DEVICE_NAME "/dev/mcs" +#define STR_TO_DEC 10 + static struct virtio_vring_info rvrings[2] = { [0] = { .info.align = VRING_ALIGNMENT, @@ -14,12 +18,10 @@ static struct virtio_vring_info rvrings[2] = { }, }; -static int g_memfd; static struct virtio_device vdev; static struct rpmsg_virtio_device rvdev; -struct metal_io_region *io; +static struct metal_io_region *io; struct virtqueue *vq[2]; -static void *tx_addr, *rx_addr, *shm_start_addr; static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR }; static unsigned char virtio_get_status(struct virtio_device *vdev) @@ -29,9 +31,7 @@ static unsigned char virtio_get_status(struct virtio_device *vdev) 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; + *(volatile unsigned int *)VDEVADDR(shmaddr) = (unsigned int)status; } static uint32_t virtio_get_features(struct virtio_device *vdev) @@ -44,15 +44,14 @@ static void virtio_notify(struct virtqueue *vq) (void)vq; int cpu_handler_fd; int ret; - int cpu_num = strtol(cpu_id, NULL, 0); - cpu_handler_fd = open(DEV_CLIENT_OS_AGENT, O_RDWR); + cpu_handler_fd = open(MCS_DEVICE_NAME, O_RDWR); if (cpu_handler_fd < 0) { - printf("open %s failed\n", DEV_CLIENT_OS_AGENT); + printf("open %s device failed\n", MCS_DEVICE_NAME); return; } - ret = ioctl(cpu_handler_fd, IRQ_SENDTO_CLIENTOS, cpu_num); + ret = ioctl(cpu_handler_fd, IOC_SENDIPI, strtol(cpu_id, NULL, STR_TO_DEC)); if (ret) { printf("send ipi tp second os failed\n"); } @@ -76,17 +75,12 @@ void virtio_init(void) printf("\nInitialize the virtio, virtqueue and rpmsg device\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); + metal_io_init(io, SHMEMADDR(shmaddr), shm_physmap, SHM_SIZE, -1, 0, NULL); /* setup vdev */ vq[0] = virtqueue_allocate(VRING_SIZE); @@ -106,13 +100,13 @@ void virtio_init(void) vdev.vrings_num = VRING_COUNT; vdev.func = &dispatch; rvrings[0].io = io; - rvrings[0].info.vaddr = tx_addr; + rvrings[0].info.vaddr = TXADDR(shmaddr); 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.vaddr = RXADDR(shmaddr); rvrings[1].info.num_descs = VRING_SIZE; rvrings[1].info.align = VRING_ALIGNMENT; rvrings[1].vq = vq[1]; @@ -120,7 +114,7 @@ void virtio_init(void) vdev.vrings_info = &rvrings[0]; /* setup rvdev */ - rpmsg_virtio_init_shm_pool(&shpool, shm_start_addr, SHM_SIZE); + rpmsg_virtio_init_shm_pool(&shpool, SHMEMADDR(shmaddr), SHM_SIZE); status = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool); if (status != 0) { printf("rpmsg_init_vdev failed %d\n", status); @@ -128,3 +122,14 @@ void virtio_init(void) return; } } + +void virtio_deinit(void) +{ + if (io) + free(io); + if (vq[0]) + virtqueue_free(vq[0]); + if (vq[1]) + virtqueue_free(vq[1]); + rpmsg_deinit_vdev(&rvdev); +} diff --git a/mcs/modules/virtio_module.h b/mcs/modules/virtio_module.h index 89145344..c73cdcb3 100644 --- a/mcs/modules/virtio_module.h +++ b/mcs/modules/virtio_module.h @@ -19,13 +19,16 @@ #define VRING_ALIGNMENT 4 #define VRING_SIZE 16 -#define IRQ_SENDTO_CLIENTOS _IOW('A', 0, int) -#define DEV_CLIENT_OS_AGENT "/dev/cpu_handler" +#define TXADDR(SHMADDR) (SHMADDR + VRING_TX_ADDRESS - VDEV_START_ADDR) +#define RXADDR(SHMADDR) (SHMADDR + VRING_RX_ADDRESS - VDEV_START_ADDR) +#define VDEVADDR(SHMADDR) (SHMADDR + VDEV_STATUS_ADDR - VDEV_START_ADDR) +#define SHMEMADDR(SHMADDR) (SHMADDR + SHM_START_ADDR - VDEV_START_ADDR) void virtio_init(void); +void virtio_deinit(void); extern char *cpu_id; -extern struct metal_io_region *io; extern struct virtqueue *vq[2]; +extern void *shmaddr; #endif \ No newline at end of file diff --git a/mcs/openamp_demo/rpmsg_main.c b/mcs/openamp_demo/rpmsg_main.c index cd467e42..44389404 100644 --- a/mcs/openamp_demo/rpmsg_main.c +++ b/mcs/openamp_demo/rpmsg_main.c @@ -3,13 +3,14 @@ #include "openamp_module.h" char *cpu_id; -char *boot_address; char *target_binfile; char *target_binaddr; int rpmsg_app_master(void) { int ret; + int message = 0; + int len; printf("start processing OpenAMP demo...\n"); @@ -39,14 +40,11 @@ int main(int argc, char **argv) int ret; int opt; - while ((opt = getopt(argc, argv, "c:b:t:a:")) != -1) { + while ((opt = getopt(argc, argv, "c:t:a:")) != -1) { switch (opt) { case 'c': cpu_id = optarg; break; - case 'b': - boot_address = optarg; - break; case 't': target_binfile = optarg; break; @@ -61,20 +59,18 @@ int main(int argc, char **argv) ret = openamp_init(); if (ret) { printf("openamp init failed: %d\n", ret); + openamp_deinit(); return ret; } ret = rpmsg_app_master(); if (ret) { printf("rpmsg app master failed: %d\n", ret); + openamp_deinit(); return ret; } - ret = openamp_deinit(); - if (ret) { - printf("openamp deinit failed: %d\n", ret); - return ret; - } + openamp_deinit(); return 0; } diff --git a/mcs/openamp_demo/zephyr.bin b/mcs/openamp_demo/zephyr_qemu.bin similarity index 100% rename from mcs/openamp_demo/zephyr.bin rename to mcs/openamp_demo/zephyr_qemu.bin diff --git a/mcs/openamp_demo/zephyr_rpi.bin b/mcs/openamp_demo/zephyr_rpi.bin new file mode 100644 index 0000000000000000000000000000000000000000..5240ed73c05a25fdb5c07a30c220c7ac3748e8f6 GIT binary patch literal 136736 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 bi|PM=N=G`+r+2-ypO`<7L+Pb&a?k%8=z^9? literal 0 HcmV?d00001 diff --git a/mcs/rpmsg_pty_demo/rpmsg_main.c b/mcs/rpmsg_pty_demo/rpmsg_main.c index 3bf642ed..0e63e859 100644 --- a/mcs/rpmsg_pty_demo/rpmsg_main.c +++ b/mcs/rpmsg_pty_demo/rpmsg_main.c @@ -5,7 +5,6 @@ #include "openamp_module.h" char *cpu_id; -char *boot_address; char *target_binfile; char *target_binaddr; @@ -41,14 +40,11 @@ int main(int argc, char **argv) int ret; int opt; - while ((opt = getopt(argc, argv, "c:b:t:a:")) != -1) { + while ((opt = getopt(argc, argv, "c:t:a:")) != -1) { switch (opt) { case 'c': cpu_id = optarg; break; - case 'b': - boot_address = optarg; - break; case 't': target_binfile = optarg; break; @@ -63,20 +59,18 @@ int main(int argc, char **argv) ret = openamp_init(); if (ret) { printf("openamp init failed: %d\n", ret); + openamp_deinit(); return ret; } ret = rpmsg_app_master(); if (ret) { printf("rpmsg app master failed: %d\n", ret); + openamp_deinit(); return ret; } - ret = openamp_deinit(); - if (ret) { - printf("openamp deinit failed: %d\n", ret); - return ret; - } + openamp_deinit(); return 0; } diff --git a/mcs/rpmsg_pty_demo/rpmsg_pty.c b/mcs/rpmsg_pty_demo/rpmsg_pty.c index 900e6342..c7a21c58 100644 --- a/mcs/rpmsg_pty_demo/rpmsg_pty.c +++ b/mcs/rpmsg_pty_demo/rpmsg_pty.c @@ -10,6 +10,7 @@ /* define the keys according to your terminfo */ #define KEY_CTRL_D 4 +#define FILE_MODE 0644 pthread_mutex_t mutex; @@ -126,7 +127,7 @@ void *log_user(void *arg) } /* write log into file */ - log_fd = open(log_file, O_RDWR | O_CREAT | O_APPEND, 0644); + log_fd = open(log_file, O_RDWR | O_CREAT | O_APPEND, FILE_MODE); if (log_fd < 0) { printf("log_user: open (%s) failed: %d\n", log_file, log_fd); return (void*)-1; diff --git a/mcs/rpmsg_pty_demo/zephyr.bin b/mcs/rpmsg_pty_demo/zephyr_qemu.bin similarity index 100% rename from mcs/rpmsg_pty_demo/zephyr.bin rename to mcs/rpmsg_pty_demo/zephyr_qemu.bin diff --git a/mcs/rpmsg_pty_demo/zephyr_rpi.bin b/mcs/rpmsg_pty_demo/zephyr_rpi.bin new file mode 100644 index 0000000000000000000000000000000000000000..981f9b4fe71377a2c6d642b181fd40da449a40b7 GIT binary patch literal 179672 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 m9o7Qb5nV0g(@Y$rt-*)LQ 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