diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a204bb5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required (VERSION 2.6) +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() + +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() + +# The version number +set (OPENAMP_VERSION_MAJOR 1) +set (OPENAMP_VERSION_MINOR 0) + +list (APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/platforms") + +include (syscheck) +project (open_amp C) + +include (CheckIncludeFiles) +include (CheckCSourceCompiles) +include (collect) +include (options) +include (depends) +enable_testing () + +set (OPENAMP_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set (OPENAMP_BIN_ROOT "${CMAKE_CURRENT_BINARY_DIR}") + +if (WITH_OBSOLETE) + add_subdirectory (obsolete) +endif (WITH_OBSOLETE) + +add_subdirectory (lib) + +if (WITH_APPS) + add_subdirectory (apps) +endif (WITH_APPS) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..41a8b8a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,69 @@ +Software License Agreement (BSD 3-Clause License) +======================================== + +Copyright (c) 2014, Mentor Graphics Corporation. All rights reserved. +Copyright (c) 2015 - 2016 Xilinx, Inc. All rights reserved. +Copyright (c) 2016 Freescale Semiconductor, 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 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. + +BSD 2-Clause License +------------------------- + +Copyright (c) . 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. + +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. + +Notes +========================================= +Use the following tag instead of the full license text in the individual files: + + SPDX-License-Identifier: BSD-3-Clause + SPDX-License-Identifier: BSD-2-Clause + +This enables machine processing of license information based on the SPDX +License Identifiers that are here available: http://spdx.org/licenses/ + diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000..cb6709b --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,23 @@ +# OpenAMP Maintainers + +OpenAMP project is maintained by the OpenAMP open source community. Everyone +is encouraged to submit issues and changes to improve OpenAMP. + +The intention of this file is to provide a set of names that developers can +consult when they have a question about OpenAMP and to provide a a set of +names to be CC'd when submitting a patch. + + +## Project Administration +Ed Mooring +Arnaud Pouliquen + +### All patches CC here +openamp-rp@lists.openampproject.org + +## Machines +### Xilinx Platform - Zynq-7000 +Ed Mooring + +### Xilinx Platform - Zynq UltraScale+ MPSoC +Ed Mooring diff --git a/README.md b/README.md new file mode 100644 index 0000000..90b5851 --- /dev/null +++ b/README.md @@ -0,0 +1,305 @@ +# open-amp +This repository is the home for the Open Asymmetric Multi Processing (OpenAMP) +framework project. The OpenAMP framework provides software components that +enable development of software applications for Asymmetric Multiprocessing +(AMP) systems. The framework provides the following key capabilities. + +1. Provides Life Cycle Management, and Inter Processor Communication + capabilities for management of remote compute resources and their associated + software contexts. +2. Provides a stand alone library usable with RTOS and Baremetal software + environments +3. Compatibility with upstream Linux remoteproc and rpmsg components +4. Following AMP configurations supported + a. Linux master/Generic(Baremetal) remote + b. Generic(Baremetal) master/Linux remote +5. Proxy infrastructure and supplied demos showcase ability of proxy on master + to handle printf, scanf, open, close, read, write calls from Bare metal + based remote contexts. + +## OpenAMP Source Structure +``` +|- lib/ +| |- virtio/ # virtio implementation +| |- rpmsg/ # rpmsg implementation +| |- remoteproc/ # remoteproc implementation +| |- proxy/ # implement one processor access device on the +| | # other processor with file operations +|- apps/ # demonstration/testing applications +| |- examples/ # Application samples using the OpenAMP framework. +| |- machine/ # common files for machine can be shared by applications +| | # It is up to each app to decide whether to use these files. +| |- system/ # common files for system can be shared by applications +| # It is up to each app to decide whether to use these files. +|- cmake # CMake files +|- script # helper scripts (such as checkpatch) for contributors. +``` + +OpenAMP library libopen_amp is composed of the following directories in `lib/`: +* `virtio/` +* `rpmsg/` +* `remoteproc/` +* `proxy/` + +OpenAMP system/machine support has been moved to libmetal, the system/machine +layer in the `apps/` directory is for system application initialization, and +resource table definition. + +### libmetal APIs used in OpenAMP +Here are the libmetal APIs used by OpenAMP, if you want to port OpenAMP for your +system, you will need to implement the following libmetal APIs in the libmetal's +`lib/system/` directory: +* alloc, for memory allocation and memory free +* cache, for flushing cache and invalidating cache +* io, for memory mapping. OpenAMP required memory mapping in order to access + vrings and carved out memory. +* irq, for IRQ handler registration, IRQ disable/enable and global IRQ handling. +* mutex +* shmem (For RTOS, you can usually use the implementation from + `lib/system/generic/`) +* sleep, at the moment, OpenAMP only requires microseconds sleep as when OpenAMP + fails to get a buffer to send messages, it will call this function to sleep and + then try again. +* time, for timestamp +* init, for libmetal initialization. +* atomic + +Please refer to `lib/system/generic` when you port libmetal for your system. + +If you a different compiler to GNU gcc, please refer to `lib/compiler/gcc/` to +port libmetal for your compiler. At the moment, OpenAMP needs the atomic +operations defined in `lib/compiler/gcc/atomic.h`. + +## OpenAMP Compilation +OpenAMP uses CMake for library and demonstration application compilation. +OpenAMP requires libmetal library. For now, you will need to download and +compile libmetal library separately before you compiling OpenAMP library. +In future, we will try to make libmetal as a submodule to OpenAMP to make this +flow easier. + +Some Cmake options are available to allow user to customize to the OpenAMP +library for it project: +* **WITH_PROXY** (default OFF): Include proxy support in the library. +* **WITH APPS** (default OFF): Build with sample applications. +* **WITH_PROXY_APPS** (default OFF):Build with proxy sample applications. +* **WITH_VIRTIO_MASTER** (default ON): Build with virtio master enabled. + This option can be set to OFF if the only the remote mode is implemented. +* **WITH_VIRTIO_SLAVE** (default ON): Build with virtio slave enabled. + This option can be set to OFF if the only the master mode is implemented. +* **WITH_STATIC_LIB** (default ON): Build with a static library. +* **WITH_SHARED_LIB** (default ON): Build with a shared library. +* **WITH_ZEPHYR** (default OFF): Build open-amp as a zephyr library. This option + is mandatory in a Zephyr environment. +* **RPMSG_BUFFER_SIZE** (default 512): adjust the size of the RPMsg buffers. + The default value of the RPMsg size is compatible with the Linux Kernel hard + coded value. If you AMP configuration is Linux kernel master/ OpenAMP remote, + this option must not be used. + +### Example to compile OpenAMP for Zephyr +The [Zephyr open-amp repo](https://github.com/zephyrproject-rtos/open-amp) +implements the open-amp library for the Zephyr project. It is mainly a fork of +this repository, with some add-ons for integration in the Zephyr project. +The standard way to compile OpenAMP for a Zephyr project is to use Zephyr build +environment. Please refer to [Zephyr OpenAMP samples](https://github.com/zephyrproject-rtos/zephyr/tree/master/samples/subsys/ipc) for examples. + +Nevertheless you can compile the OpenAMP project for Zephyr. +As OpenAMP uses libmetal, please refer to libmetal README to build libmetal +for Zephyr before building OpenAMP library for Zephyr. +As Zephyr uses CMake, we build OpenAMP library as a target of Zephyr CMake +project. Here is how to build libmetal for Zephyr: +``` + $ export ZEPHRY_GCC_VARIANT=zephyr + $ export ZEPHRY_SDK_INSTALL_DIR= + $ source /zephyr-env.sh + + $ cmake \ + -DWITH_ZEPHYR=on -DBOARD=qemu_cortex_m3 \ + -DCMAKE_INCLUDE_PATH="/lib/include" \ + -DCMAKE_LIBRARY_PATH="/lib" \ + $ make VERBOSE=1 all +``` + +### Example to compile OpenAMP for communication between Linux processes: +* Install libsysfs devel and libhugetlbfs devel packages on your Linux host. +* build libmetal library on your host as follows: + + ``` + $ mkdir -p build-libmetal + $ cd build-libmetal + $ cmake + $ make VERBOSE=1 DESTDIR= install + ``` + +* build OpenAMP library on your host as follows: + + $ mkdir -p build-openamp + $ cd build-openamp + $ cmake -DCMAKE_INCLUDE_PATH= \ + -DCMAKE_LIBRARY_PATH= [-DWITH_APPS=ON] + $ make VERBOSE=1 DESTDIR=$(pwd) install + +The OpenAMP library will be generated to `build/usr/local/lib` directory, +headers will be generated to `build/usr/local/include` directory, and the +applications executable will be generated to `build/usr/local/bin` +directory. + +* cmake option `-DWITH_APPS=ON` is to build the demonstration applications. +* If you have used `-DWITH_APPS=ON` to build the demos, you can try them on + your Linux host as follows: + + ``` + # Start echo test server to wait for message to echo + $ sudo LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib \ + build/usr/local/bin/rpmsg-echo-shared + # Run echo test to send message to echo test server + $ sudo LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib \ + build/usr/local/bin/rpmsg-echo-ping-shared 1 + ``` + +### Example to compile Zynq UltraScale+ MPSoC R5 generic(baremetal) remote: +* build libmetal library on your host as follows: + * Create your on cmake toolchain file to compile libmetal for your generic + (baremetal) platform. Here is the example of the toolchain file: + + ``` + set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "") + set (MACHINE "zynqmp_r5" CACHE STRING "") + + set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "") + set (CMAKE_C_FLAGS "-mfloat-abi=soft -mcpu=cortex-r5 -Wall -Werror -Wextra \ + -flto -Os -I/ws/xsdk/r5_0_bsp/psu_cortexr5_0/include" CACHE STRING "") + + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") + SET(CMAKE_AR "gcc-ar" CACHE STRING "") + SET(CMAKE_C_ARCHIVE_CREATE " qcs ") + SET(CMAKE_C_ARCHIVE_FINISH true) + + include (cross-generic-gcc) + ``` + + * Compile libmetal library: + + ``` + $ mkdir -p build-libmetal + $ cd build-libmetal + $ cmake -DCMAKE_TOOLCHAIN_FILE= + $ make VERBOSE=1 DESTDIR= install + ``` + +* build OpenAMP library on your host as follows: + * Create your on cmake toolchain file to compile openamp for your generic + (baremetal) platform. Here is the example of the toolchain file: + ``` + set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "") + set (MACHINE "zynqmp_r5" CACHE STRING "") + set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "") + set (CMAKE_C_FLAGS "-mfloat-abi=soft -mcpu=cortex-r5 -Os -flto \ + -I/ws/libmetal-r5-generic/usr/local/include \ + -I/ws/xsdk/r5_0_bsp/psu_cortexr5_0/include" CACHE STRING "") + set (CMAKE_ASM_FLAGS "-mfloat-abi=soft -mcpu=cortex-r5" CACHE STRING "") + set (PLATFORM_LIB_DEPS "-lxil -lc -lm" CACHE STRING "") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") + SET(CMAKE_AR "gcc-ar" CACHE STRING "") + SET(CMAKE_C_ARCHIVE_CREATE " qcs ") + SET(CMAKE_C_ARCHIVE_FINISH true) + set (CMAKE_FIND_ROOT_PATH /ws/libmetal-r5-generic/usr/local/lib \ + /ws/xsdk/r5_bsp/psu_cortexr5_0/lib ) + + include (cross_generic_gcc) + ``` + + * We use cmake `find_path` and `find_library` to check if libmetal includes + and libmetal library is in the includes and library search paths. However, + for non-linux system, it doesn't work with `CMAKE_INCLUDE_PATH` and + `CMAKE_LIBRARY_PATH` variables, and thus, we need to specify those paths + in the toolchain file with `CMAKE_C_FLAGS` and `CMAKE_FIND_ROOT_PATH`. +* Compile the OpenAMP library: + + ``` + $ mkdir -p build-openamp + $ cd build-openamp + $ cmake -DCMAKE_TOOLCHAIN_FILE= + $ make VERBOSE=1 DESTDIR=$(pwd) install + ``` + +The OpenAMP library will be generated to `build/usr/local/lib` directory, +headers will be generated to `build/usr/local/include` directory, and the +applications executable will be generated to `build/usr/local/bin` +directory. + + +### Example to compile OpenAMP Linux Userspace for Zynq UltraScale+ MPSoC +We can use yocto to build the OpenAMP Linux userspace library and application. +open-amp and libmetal recipes are in this yocto layer: +https://github.com/OpenAMP/meta-openamp +* Add the `meta-openamp` layer to your layers in your yocto build project's `bblayers.conf` file. +* Add `libmetal` and `open-amp` to your packages list. E.g. add `libmetal` and `open-amp` to the + `IMAGE_INSTALL_append` in the `local.conf` file. +* You can also add OpenAMP demos Linux applications packages to your yocto packages list. OpenAMP + demo examples recipes are also in `meta-openamp`: + https://github.com/OpenAMP/meta-openamp/tree/master/recipes-openamp/openamp-examples + +In order to user OpenAMP(RPMsg) in Linux userspace, you will need to have put the IPI device, + vring memory and shared buffer memory to your Linux kernel device tree. The device tree example + can be found here: + https://github.com/OpenAMP/open-amp/blob/master/apps/machine/zynqmp/openamp-linux-userspace.dtsi + +## Supported System and Machines +For now, it supports: +* Zynq generic slave +* Zynq UltraScale+ MPSoC R5 generic slave +* Linux host OpenAMP between Linux userspace processes +* Linux userspace OpenAMP RPMsg master +* Linux userspace OpenAMP RPMsg slave + +## Known Limitations: +1. In case of OpenAMP on Linux userspace for inter processors communication, + it only supports static vrings and shared buffers. +2. `sudo` is required to run the OpenAMP demos between Linux processes, as + it doesn't work on some systems if you are normal users. + +## How to contribute: +As an open-source project, we welcome and encourage the community to submit patches directly to the project. As a contributor you should be familiar with common developer tools such as Git and CMake, and platforms such as GitHub. +Then following points should be rescpected to facilitate the review process. + +### Licencing +Code is contributed to the Linux kernel under a number of licenses, but all code must be compatible with version the [BSD License](https://github.com/OpenAMP/open-amp/blob/master/LICENSE.md), which is the license covering the OpenAMP distribution as a whole. In practice, use the following tag instead of the full license text in the individual files: + + ``` + SPDX-License-Identifier: BSD-3-Clause + SPDX-License-Identifier: BSD-2-Clause + ``` +### Signed-off-by +Commit message must contain Signed-off-by: line and your email must match the change authorship information. Make sure your .gitconfig is set up correctly: + + ``` + git config --global user.name "first-name Last-Namer" + git config --global user.email "yourmail@company.com" + ``` +### gitlint +Before you submit a pull request to the project, verify your commit messages meet the requirements. The check can be performed locally using the the gitlint command. + +Run gitlint locally in your tree and branch where your patches have been committed: + + ```gitlint``` +Note, gitlint only checks HEAD (the most recent commit), so you should run it after each commit, or use the --commits option to specify a commit range covering all the development patches to be submitted. + +### Code style +In general, follow the Linux kernel coding style, with the following exceptions: + +* Use /** */ for doxygen comments that need to appear in the documentation. + +The Linux kernel GPL-licensed tool checkpatch is used to check coding style conformity.Checkpatch is available in the scripts directory. + +To check your \ commits in your git branch: + ``` + ./scripts/checkpatch.pl --strict -g HEAD- + + ``` +### Send a pull request +We use standard github mechanism for pull request. Please refer to github documentation for help. + +## Communication and Collaboration +[Subscribe](https://lists.openampproject.org/mailman/listinfo/openamp-rp) to the OpenAMP mailing list(openamp-rp@lists.openampproject.org). + +For more details on the framework please refer to the the [OpenAMP wiki](https://github.com/OpenAMP/open-amp/wiki). diff --git a/SConscript b/SConscript new file mode 100644 index 0000000..c06bb03 --- /dev/null +++ b/SConscript @@ -0,0 +1,38 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) +include_path = [cwd + '/apps/system/generic/machine/rv64_virt'] +include_path += [cwd + '/lib/rpmsg'] +include_path += [cwd + '/lib/include/openamp'] +include_path += [cwd + '/lib/include'] + + +src = [cwd + '/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c'] +src += [cwd + '/apps/tests/msg/rpmsg-update.c'] +src += [cwd + '/apps/examples/matrix_multiply/matrix_multiplyd.c'] +src += [cwd + '/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.c'] +src += [cwd + '/apps/system/generic/machine/rv64_virt/helper.c'] +src += [cwd + '/apps/system/generic/machine/rv64_virt/platform_info.c'] +src += [cwd + '/apps/system/generic/machine/rv64_virt/rsc_table.c'] +src += [cwd + '/lib/rpmsg/rpmsg.c'] +src += [cwd + '/lib/rpmsg/rpmsg_virtio.c'] +src += [cwd + '/lib/proxy/rpmsg_retarget.c'] +src += [cwd + '/lib/remoteproc/rsc_table_parser.c'] +src += [cwd + '/lib/remoteproc/remoteproc_virtio.c'] +src += [cwd + '/lib/remoteproc/elf_loader.c'] +src += [cwd + '/lib/remoteproc/remoteproc.c'] +src += [cwd + '/lib/virtio/virtqueue.c'] +src += [cwd + '/lib/virtio/virtio.c'] + +CPPDEFINES = [] +CPPDEFINES += ['DEFAULT_LOGGER_ON'] +CPPDEFINES += ['RPMSG_NO_IPI'] + +objs = DefineGroup('openamp', src, depend = ['PKG_USING_OPENAMP'], CPPPATH = include_path, CPPDEFINES = CPPDEFINES) + +Return('objs') diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt new file mode 100644 index 0000000..95b48e0 --- /dev/null +++ b/apps/CMakeLists.txt @@ -0,0 +1,14 @@ +collector_create (APP_COMMON_SOURCES "") +collector_create (APP_LIB_DIRS "") +collector_create (APP_INC_DIRS "") +collector_create (APP_LIB_DEPS "") + +collector_create (APP_EXTRA_C_FLAGS "") +set (APPS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +set (APPS_SHARE_DIR "${CMAKE_CURRENT_BINARY_DIR}/share") + +add_subdirectory (machine) +add_subdirectory (system) +add_subdirectory (tests) +add_subdirectory (examples) diff --git a/apps/examples/CMakeLists.txt b/apps/examples/CMakeLists.txt new file mode 100644 index 0000000..a351e3e --- /dev/null +++ b/apps/examples/CMakeLists.txt @@ -0,0 +1,10 @@ + +option (WITH_LOAD_FW "Include loading firmware example" OFF) + +add_subdirectory (echo) +add_subdirectory (rpmsg_sample_echo) +add_subdirectory (matrix_multiply) +add_subdirectory (load_fw) +if (WITH_PROXY_APPS) +add_subdirectory (rpc_demo) +endif (WITH_PROXY_APPS) diff --git a/apps/examples/echo/CMakeLists.txt b/apps/examples/echo/CMakeLists.txt new file mode 100644 index 0000000..f01de6e --- /dev/null +++ b/apps/examples/echo/CMakeLists.txt @@ -0,0 +1,46 @@ + +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS} -fdata-sections -ffunction-sections") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +foreach (_app rpmsg-echo-ping rpmsg-echo) + collector_list (_sources APP_COMMON_SOURCES) + if (${_app} STREQUAL "rpmsg-echo-ping") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/rpmsg-ping.c") + elseif (${_app} STREQUAL "rpmsg-echo") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/rpmsg-echo.c") + endif (${_app} STREQUAL "rpmsg-echo-ping") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) diff --git a/apps/examples/echo/rpmsg-echo.c b/apps/examples/echo/rpmsg-echo.c new file mode 100644 index 0000000..91c7ee4 --- /dev/null +++ b/apps/examples/echo/rpmsg-echo.c @@ -0,0 +1,116 @@ +/* This is a sample demonstration application that showcases usage of rpmsg +This application is meant to run on the remote CPU running baremetal code. +This application echoes back data that was sent to it by the master core. */ + +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-echo.h" + +#define SHUTDOWN_MSG 0xEF56A55A + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + (void)priv; + (void)src; + + /* On reception of a shutdown we signal the application to terminate */ + if ((*(unsigned int *)data) == SHUTDOWN_MSG) { + LPRINTF("shutdown message is received.\r\n"); + shutdown_req = 1; + return RPMSG_SUCCESS; + } + + /* Send data back to master */ + if (rpmsg_send(ept, data, len) < 0) { + LPERROR("rpmsg_send failed\r\n"); + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPRINTF("unexpected Remote endpoint destroy\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while(1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/echo/rpmsg-echo.h b/apps/examples/echo/rpmsg-echo.h new file mode 100644 index 0000000..d15eb56 --- /dev/null +++ b/apps/examples/echo/rpmsg-echo.h @@ -0,0 +1,6 @@ +#ifndef RPMSG_ECHO_H +#define RPMSG_ECHO_H + +#define RPMSG_SERVICE_NAME "rpmsg-openamp-demo-channel" + +#endif /* RPMSG_ECHO_H */ diff --git a/apps/examples/echo/rpmsg-ping.c b/apps/examples/echo/rpmsg-ping.c new file mode 100644 index 0000000..854e03c --- /dev/null +++ b/apps/examples/echo/rpmsg-ping.c @@ -0,0 +1,201 @@ +/* This is a sample demonstration application that showcases usage of rpmsg +This application is meant to run on the remote CPU running baremetal code. +This application echoes back data that was sent to it by the master core. */ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-echo.h" + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +struct _payload { + unsigned long num; + unsigned long size; + unsigned char data[]; +}; + +static int err_cnt; + +#define PAYLOAD_MIN_SIZE 1 + +/* Globals */ +static struct rpmsg_endpoint lept; +static struct _payload *i_payload; +static int rnum = 0; +static int err_cnt = 0; +static int ept_deleted = 0; + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + int i; + struct _payload *r_payload = (struct _payload *)data; + + (void)ept; + (void)src; + (void)priv; + LPRINTF(" received payload number %lu of size %lu \r\n", + r_payload->num, (unsigned long)len); + + if (r_payload->size == 0) { + LPERROR(" Invalid size of package is received.\r\n"); + err_cnt++; + return RPMSG_SUCCESS; + } + /* Validate data buffer integrity. */ + for (i = 0; i < (int)r_payload->size; i++) { + if (r_payload->data[i] != 0xA5) { + LPRINTF("Data corruption at index %d\r\n", i); + err_cnt++; + break; + } + } + rnum = r_payload->num + 1; + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&lept); + LPRINTF("echo test: service is destroyed\r\n"); + ept_deleted = 1; +} + +static void rpmsg_name_service_bind_cb(struct rpmsg_device *rdev, + const char *name, uint32_t dest) +{ + LPRINTF("new endpoint notification is received.\r\n"); + if (strcmp(name, RPMSG_SERVICE_NAME)) + LPERROR("Unexpected name service %s.\r\n", name); + else + (void)rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, dest, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app (struct rpmsg_device *rdev, void *priv) +{ + int ret; + int i; + int size, max_size, num_payloads; + int expect_rnum = 0; + + LPRINTF(" 1 - Send data to remote core, retrieve the echo"); + LPRINTF(" and validate its integrity ..\r\n"); + + max_size = rpmsg_virtio_get_buffer_size(rdev); + if (max_size < 0) { + LPERROR("No avaiable buffer size.\r\n"); + return -1; + } + max_size -= sizeof(struct _payload); + num_payloads = max_size - PAYLOAD_MIN_SIZE + 1; + i_payload = + (struct _payload *)metal_allocate_memory(2 * sizeof(unsigned long) + + max_size); + + if (!i_payload) { + LPERROR("memory allocation failed.\r\n"); + return -1; + } + + /* Create RPMsg endpoint */ + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + + if (ret) { + LPERROR("Failed to create RPMsg endpoint.\r\n"); + return ret; + } + + while (!is_rpmsg_ept_ready(&lept)) + platform_poll(priv); + + LPRINTF("RPMSG endpoint is binded with remote.\r\n"); + for (i = 0, size = PAYLOAD_MIN_SIZE; i < num_payloads; i++, size++) { + i_payload->num = i; + i_payload->size = size; + + /* Mark the data buffer. */ + memset(&(i_payload->data[0]), 0xA5, size); + + LPRINTF("sending payload number %lu of size %lu\r\n", + i_payload->num, + (unsigned long)(2 * sizeof(unsigned long)) + size); + + ret = rpmsg_send(&lept, i_payload, + (2 * sizeof(unsigned long)) + size); + + if (ret < 0) { + LPERROR("Failed to send data...\r\n"); + break; + } + LPRINTF("echo test: sent : %lu\r\n", + (unsigned long)(2 * sizeof(unsigned long)) + size); + + expect_rnum++; + do { + platform_poll(priv); + } while ((rnum < expect_rnum) && !err_cnt && !ept_deleted); + + } + + LPRINTF("**********************************\r\n"); + LPRINTF(" Test Results: Error count = %d \r\n", err_cnt); + LPRINTF("**********************************\r\n"); + /* Destroy the RPMsg endpoint */ + rpmsg_destroy_ept(&lept); + LPRINTF("Quitting application .. Echo test end\r\n"); + + metal_free_memory(i_payload); + return 0; +} + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, + rpmsg_name_service_bind_cb); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/examples/load_fw/CMakeLists.txt b/apps/examples/load_fw/CMakeLists.txt new file mode 100644 index 0000000..6f12f9b --- /dev/null +++ b/apps/examples/load_fw/CMakeLists.txt @@ -0,0 +1,52 @@ +collector_list (_app_extra_c_flags APP_EXTRA_C_FLAGS) +set (_cflags "${CMAKE_C_FLAGS} ${_app_extra_c_flags}") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +if (WITH_ZYNQMP) + collect (PROJECT_LIB_DEPS xilpm) +endif (WITH_ZYNQMP) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +if (WITH_WFI) + add_definitions(-DWITH_WFI) +endif (WITH_WFI) + +foreach (_app load_fw) + collector_list (_sources APP_COMMON_SOURCES) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/mem_image_store.c") + if (WITH_ZYNQMP) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/zynqmp_apu_lcm_rproc_example.c") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/zynqmp_rpu_lcm_rproc_example.c") + add_definitions(-DWITH_ZYNQMP) + endif (WITH_ZYNQMP) + if (WITH_RV64) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/virt_rv64_rproc_example.c") + add_definitions(-DWITH_RV64) + endif (WITH_RV64) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/platform_info.c") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + if (WITH_STATIC_LIB) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections -T"${CMAKE_CURRENT_SOURCE_DIR}/lscript.ld" -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_STATIC_LIB) + +endforeach(_app) diff --git a/apps/examples/load_fw/common.h b/apps/examples/load_fw/common.h new file mode 100644 index 0000000..6e4a8d0 --- /dev/null +++ b/apps/examples/load_fw/common.h @@ -0,0 +1,122 @@ +/* + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef COMMON_H_ +#define COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_ZYNQMP +/* Xilinx headers */ +#include +#include +#include +#include + +#ifdef versal +#include +#include + +#ifndef NODE_APU_0 +#define NODE_APU_0 PM_DEV_ACPU_0 +#endif +#ifndef NODE_APU_1 +#define NODE_APU_1 PM_DEV_ACPU_1 +#endif +#ifndef NODE_RPU_0 +#define NODE_RPU_0 PM_DEV_RPU0_0 +#endif +#ifndef NODE_RPU +#define NODE_RPU PM_DEV_RPU0_0 +#endif + +#ifndef NODE_RPU_1 +#define NODE_RPU_1 PM_DEV_RPU0_1 +#endif + +#ifndef NODE_TCM_0_A +#define NODE_TCM_0_A PM_DEV_TCM_0_A +#endif +#ifndef NODE_TCM_0_B +#define NODE_TCM_0_B PM_DEV_TCM_0_B +#endif +#ifndef NODE_TCM_1_A +#define NODE_TCM_1_A PM_DEV_TCM_1_A +#endif +#ifndef NODE_TCM_1_B +#define NODE_TCM_1_B PM_DEV_TCM_1_B +#endif + +#ifndef NODE_DDR +#define NODE_DDR PM_DEV_DDR_0 +#endif + +#ifndef NODE_OCM_BANK_0 +#define NODE_OCM_BANK_0 PM_DEV_OCM_0 +#endif +#ifndef NODE_OCM_BANK_1 +#define NODE_OCM_BANK_1 PM_DEV_OCM_1 +#endif +#ifndef NODE_OCM_BANK_2 +#define NODE_OCM_BANK_2 PM_DEV_OCM_2 +#endif +#ifndef NODE_OCM_BANK_3 +#define NODE_OCM_BANK_3 PM_DEV_OCM_3 +#endif + +/* Requirement limits */ +#define XPM_MAX_CAPABILITY (PM_CAP_ACCESS | PM_CAP_CONTEXT | PM_CAP_WAKEUP) +#define XPM_MAX_LATENCY (0xFFFFU) +#define XPM_MAX_QOS (100) +#define XPM_MIN_CAPABILITY (0) +#define XPM_MIN_LATENCY (0) +#define XPM_MIN_QOS (0) +#define XPM_DEF_CAPABILITY XPM_MAX_CAPABILITY +#define XPM_DEF_LATENCY XPM_MAX_LATENCY +#define XPM_DEF_QOS XPM_MAX_QOS + +enum XPmRequestAck { + REQUEST_ACK_NO = 1, + REQUEST_ACK_BLOCKING, + REQUEST_ACK_NON_BLOCKING, + REQUEST_ACK_CB_CERROR, +}; + +#elif zynqmp +#include +#define XPM_MAX_QOS MAX_QOS +#define XPM_MIN_QOS (0) +#endif /* versal */ + +#define LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) + +#endif /* #ifdef WITH_ZYNQMP */ + +#ifdef WITH_RV64 + +#define RV_NODE_APU_0 (0) +#define RV_NODE_APU_N (1) + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) + +#endif /* #ifdef WITH_RV64 */ + +struct rproc_priv { + struct remoteproc *rproc; + int cpu_id; +}; +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +#endif /* COMMON_H_ */ diff --git a/apps/examples/load_fw/common.h.orig b/apps/examples/load_fw/common.h.orig new file mode 100644 index 0000000..e4ed373 --- /dev/null +++ b/apps/examples/load_fw/common.h.orig @@ -0,0 +1,109 @@ +/* + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef COMMON_H_ +#define COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +/* Xilinx headers */ +#include +#include +#include +#include + +#ifdef versal +#include +#include + +#ifndef NODE_APU_0 +#define NODE_APU_0 PM_DEV_ACPU_0 +#endif +#ifndef NODE_APU_1 +#define NODE_APU_1 PM_DEV_ACPU_1 +#endif +#ifndef NODE_RPU_0 +#define NODE_RPU_0 PM_DEV_RPU0_0 +#endif +#ifndef NODE_RPU +#define NODE_RPU PM_DEV_RPU0_0 +#endif + +#ifndef NODE_RPU_1 +#define NODE_RPU_1 PM_DEV_RPU0_1 +#endif + +#ifndef NODE_TCM_0_A +#define NODE_TCM_0_A PM_DEV_TCM_0_A +#endif +#ifndef NODE_TCM_0_B +#define NODE_TCM_0_B PM_DEV_TCM_0_B +#endif +#ifndef NODE_TCM_1_A +#define NODE_TCM_1_A PM_DEV_TCM_1_A +#endif +#ifndef NODE_TCM_1_B +#define NODE_TCM_1_B PM_DEV_TCM_1_B +#endif + +#ifndef NODE_DDR +#define NODE_DDR PM_DEV_DDR_0 +#endif + +#ifndef NODE_OCM_BANK_0 +#define NODE_OCM_BANK_0 PM_DEV_OCM_0 +#endif +#ifndef NODE_OCM_BANK_1 +#define NODE_OCM_BANK_1 PM_DEV_OCM_1 +#endif +#ifndef NODE_OCM_BANK_2 +#define NODE_OCM_BANK_2 PM_DEV_OCM_2 +#endif +#ifndef NODE_OCM_BANK_3 +#define NODE_OCM_BANK_3 PM_DEV_OCM_3 +#endif + +/* Requirement limits */ +#define XPM_MAX_CAPABILITY (PM_CAP_ACCESS | PM_CAP_CONTEXT | PM_CAP_WAKEUP) +#define XPM_MAX_LATENCY (0xFFFFU) +#define XPM_MAX_QOS (100) +#define XPM_MIN_CAPABILITY (0) +#define XPM_MIN_LATENCY (0) +#define XPM_MIN_QOS (0) +#define XPM_DEF_CAPABILITY XPM_MAX_CAPABILITY +#define XPM_DEF_LATENCY XPM_MAX_LATENCY +#define XPM_DEF_QOS XPM_MAX_QOS + +enum XPmRequestAck { + REQUEST_ACK_NO = 1, + REQUEST_ACK_BLOCKING, + REQUEST_ACK_NON_BLOCKING, + REQUEST_ACK_CB_CERROR, +}; + +#else /* zynqmp */ +#include +#define XPM_MAX_QOS MAX_QOS +#define XPM_MIN_QOS (0) +#endif /* versal */ + +#define LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +struct rproc_priv { + struct remoteproc *rproc; + int cpu_id; +}; + +#endif /* COMMON_H_ */ diff --git a/apps/examples/load_fw/load_fw.c b/apps/examples/load_fw/load_fw.c new file mode 100644 index 0000000..eeae647 --- /dev/null +++ b/apps/examples/load_fw/load_fw.c @@ -0,0 +1,197 @@ +/* + * Load firmware example + * + * Copyright(c) 2018 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#ifdef WITH_RV64 +#include +#endif /* #ifdef WITH_RV64 */ + +extern struct image_store_ops mem_image_store_ops; + +struct mem_file { + const void *base; +}; + +static struct mem_file image = { + .base = (void *)0x3ED00000, +}; + +int load_exectuable_block(struct remoteproc *rproc, + struct image_store_ops *store_ops, void *store, + const char *img_path) +{ + int ret; + + if (rproc == NULL) + return -EINVAL; + /* Configure remoteproc to get ready to load executable */ + remoteproc_config(rproc, NULL); + + /* Load remoteproc executable */ + LPRINTF("Start to load executable with remoteproc_load() \r\n"); + ret = remoteproc_load(rproc, img_path, store, store_ops, NULL); + if (ret) { + LPRINTF("failed to load firmware\r\n"); + return ret; + } + /* Start the processor */ + ret = remoteproc_start(rproc); + if (ret) { + LPRINTF("failed to start processor\r\n"); + return ret; + } + LPRINTF("successfully started the processor\r\n"); +#ifndef RPU_BOOT_LINUX +#ifdef WITH_WFI + /* ... */ + asm volatile("wfi"); +#endif /* `#ifdef WITH_WFI */ + LPRINTF("going to stop the processor\r\n"); + remoteproc_stop(rproc); + /* application may want to do some cleanup before shutdown */ + LPRINTF("going to shutdown the processor\r\n"); + remoteproc_shutdown(rproc); +#endif /* RPU_BOOT_LINUX */ + return 0; +} + +#ifndef RPU_BOOT_LINUX +int load_exectuable_noblock(struct remoteproc *rproc, + struct image_store_ops *store_ops, void *store, + const char *img_path) +{ + int ret; + const void *img_data; + void *img_info = NULL; + metal_phys_addr_t pa; + struct metal_io_region *io; + size_t offset, noffset; + size_t len, nlen, nmlen; + unsigned char padding; + + if (rproc == NULL) + return -EINVAL; + /* Configure remoteproc to get ready to load executable */ + remoteproc_config(rproc, NULL); + /* Load remoteproc executable */ + LPRINTF("Start to load executable with remoteproc_load() \r\n"); + ret = store_ops->open(store, img_path, &img_data); + if (ret <= 0) + return -EINVAL; + offset = 0; + len = (size_t)ret; + do { + nlen = 0; + pa = METAL_BAD_PHYS; + io = NULL; + nmlen = 0; + LPRINTF("%s, loading 0x%lx,0x%lx\r\n", + __func__, offset, len); + ret = remoteproc_load_noblock(rproc, img_data, offset, len, + &img_info, &pa, &io, &noffset, + &nlen, &nmlen, &padding); + if (ret) { + LPERROR("failed to load executable, 0x%lx,0x%lx\r\n", + offset, len); + return ret; + } + if (nlen == 0) + break; + offset = noffset; + len = nlen; + ret = store_ops->load(store, noffset, nlen, &img_data, pa, + io, 1); + if (ret != (int)nlen) { + LPERROR("failed to load data to memory, 0x%lx,0x%lx\r\n", + noffset, nlen); + return ret; + } + if (nmlen > nlen && io != NULL) { + /* pad the rest of the memory with 0 */ + size_t tmpoffset; + + tmpoffset = metal_io_phys_to_offset(io, pa + nlen); + metal_io_block_set(io, tmpoffset, padding, + (nmlen - nlen)); + + } + } while(1); + + /* Start the processor */ + ret = remoteproc_start(rproc); + if (ret) { + LPRINTF("failed to start processor\r\n"); + return ret; + } + LPRINTF("successfully started the processor\r\n"); +#ifdef WITH_WFI + /* ... */ + asm volatile("wfi"); +#endif /* `#ifdef WITH_WFI */ + LPRINTF("going to stop the processor\r\n"); + remoteproc_stop(rproc); + /* application may want to do some cleanup before shutdown */ + LPRINTF("going to shutdown the processor\r\n"); + remoteproc_shutdown(rproc); + return 0; +} +#endif /* RPU_BOOT_LINUX */ + +int main(int argc, char *argv[]) +{ + struct remoteproc *rproc = NULL; + void *store = ℑ + unsigned int cpu_id; + int ret; + char *path = NULL; + int i = 0; + +#ifdef WITH_ZYNQMP + (void)argc; + (void)argv; + cpu_id = LOAD_FW_TARGET; +#elif defined(WITH_RV64) + if (argc > 2) { + i++; + path = argv[i]; + } + if (argc > 1) { + i++; + cpu_id = atoi(argv[i]); + } else { + return -1; + } +#endif /* #ifdef WITH_ZYNQMP */ + LPRINTF("Loading Exectuable Demo\n"); + rproc = app_init(cpu_id); + if (!rproc) { + LPERROR("app_init failed\r\n"); + return -1; + } + ret = load_exectuable_block(rproc, &mem_image_store_ops, store, path); + if (ret < 0) { + LPERROR("load_exectuable_block failed\r\n"); + /* Make sure the remote is shut down */ + remoteproc_shutdown(rproc); + return -1; + } +#ifndef RPU_BOOT_LINUX + ret = load_exectuable_noblock(rproc, &mem_image_store_ops, store, + NULL); + if (ret < 0) { + LPERROR("load_exectuable_noblock failed\r\n"); + /* Make sure the remote is shut down */ + remoteproc_shutdown(rproc); + return -1; + } +#endif /* RPU_BOOT_LINUX */ + remoteproc_remove(rproc); + return ret; +} diff --git a/apps/examples/load_fw/lscript.ld b/apps/examples/load_fw/lscript.ld new file mode 100644 index 0000000..d241a2a --- /dev/null +++ b/apps/examples/load_fw/lscript.ld @@ -0,0 +1,315 @@ +/****************************************************************************** +* +* 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. +* +* 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 +* XILINX 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. +* +******************************************************************************/ + +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000; + +_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; +_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; +_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024; +_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; +_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; + +/* Define Memories in the system */ +/* TCM size is set to 2*0x20000 for R5 in lockstep mode */ +MEMORY +{ + psu_ocm_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000 + psu_r5_tcm_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00010000 + psu_r5_tcm_ram_1_S_AXI_BASEADDR : ORIGIN = 0x00020000, LENGTH = 0x00010000 +} + +/* Specify the default entry point to the program */ + +/* ENTRY(_boot) */ + +ENTRY(_vector_table) + +/* Define the sections, and where they are mapped in memory */ + +SECTIONS +{ +.vectors : { + KEEP (*(.vectors)) + *(.boot) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.gnu_warning) + *(.gcc_execpt_table) + *(.glue_7) + *(.glue_7t) + *(.vfp11_veneer) + *(.ARM.extab) + *(.gnu.linkonce.armextab.*) +} > psu_r5_tcm_ram_1_S_AXI_BASEADDR + +.init : { + KEEP (*(.init)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini : { + KEEP (*(.fini)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.interp : { + KEEP (*(.interp)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.note-ABI-tag : { + KEEP (*(.note-ABI-tag)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata : { + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata1 : { + __rodata1_start = .; + *(.rodata1) + *(.rodata1.*) + __rodata1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata2 : { + __sdata2_start = .; + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + __sdata2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss2 : { + __sbss2_start = .; + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + __sbss2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data : { + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.jcr) + *(.got) + *(.got.plt) + __data_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data1 : { + __data1_start = .; + *(.data1) + *(.data1.*) + __data1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.got : { + *(.got) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ctors : { + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.dtors : { + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + __DTOR_END__ = .; + ___DTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fixup : { + __fixup_start = .; + *(.fixup) + __fixup_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_frame : { + *(.eh_frame) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_framehdr : { + __eh_framehdr_start = .; + *(.eh_framehdr) + __eh_framehdr_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.gcc_except_table : { + *(.gcc_except_table) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.mmu_tbl (ALIGN(16384)) : { + __mmu_tbl_start = .; + *(.mmu_tbl) + __mmu_tbl_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + *(.gnu.linkonce.armexidix.*.*) + __exidx_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.preinit_array : { + __preinit_array_start = .; + KEEP (*(SORT(.preinit_array.*))) + KEEP (*(.preinit_array)) + __preinit_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini_array : { + __fini_array_start = .; + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + __fini_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.attributes : { + __ARM.attributes_start = .; + *(.ARM.attributes) + __ARM.attributes_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata : { + __sdata_start = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + __sdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss (NOLOAD) : { + __sbss_start = .; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + __sbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tdata : { + __tdata_start = .; + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + __tdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tbss : { + __tbss_start = .; + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + __tbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.bss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); + +_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(16); + _heap = .; + HeapBase = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; + HeapLimit = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.stack (NOLOAD) : { + . = ALIGN(16); + _stack_end = .; + . += _STACK_SIZE; + _stack = .; + __stack = _stack; + . = ALIGN(16); + _irq_stack_end = .; + . += _IRQ_STACK_SIZE; + __irq_stack = .; + _supervisor_stack_end = .; + . += _SUPERVISOR_STACK_SIZE; + . = ALIGN(16); + __supervisor_stack = .; + _abort_stack_end = .; + . += _ABORT_STACK_SIZE; + . = ALIGN(16); + __abort_stack = .; + _fiq_stack_end = .; + . += _FIQ_STACK_SIZE; + . = ALIGN(16); + __fiq_stack = .; + _undef_stack_end = .; + . += _UNDEF_STACK_SIZE; + . = ALIGN(16); + __undef_stack = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +_end = .; +} diff --git a/apps/examples/load_fw/mem_image_store.c b/apps/examples/load_fw/mem_image_store.c new file mode 100644 index 0000000..c4ba498 --- /dev/null +++ b/apps/examples/load_fw/mem_image_store.c @@ -0,0 +1,110 @@ +/* + * Memory based image store Operation + * + * Copyright(c) 2018 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct mem_file { + const void *base; +}; + +int mem_image_open(void *store, const char *path, const void **image_data) +{ + struct mem_file *image = store; + int fd = -1; + struct stat file_stat; + char *buf = NULL; + int ret = -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + memset(&file_stat, 0, sizeof(file_stat)); + if (fstat(fd, &file_stat)) + return -1; + + LPRINTF("file size %d\n", file_stat.st_size); + buf = malloc(file_stat.st_size); + if (!buf) + return -1; + + ret = read(fd, buf, file_stat.st_size); + if (ret > 0) { + LPRINTF("%d read\n", ret); + } else if (ret < 0) { + LPRINTF("%s\n", strerror(errno)); + return ret; + } + *image_data = buf; + image->base = buf; + return file_stat.st_size; +} + +void mem_image_close(void *store) +{ + //TODO + /* The image is in memory, does nothing */ + (void)store; +} + +int mem_image_load(void *store, size_t offset, size_t size, + const void **data, metal_phys_addr_t pa, + struct metal_io_region *io, + char is_blocking) +{ + struct mem_file *image = store; + const void *fw_base = image->base; + + (void)is_blocking; + + LPRINTF("%s: offset=0x%x, size=0x%x\n\r", + __func__, offset, size); + if (pa == METAL_BAD_PHYS) { + if (data == NULL) { + LPERROR("%s: data is NULL while pa is ANY\r\n", + __func__); + return -EINVAL; + } + *data = (const void *)((const char *)fw_base + offset); + } else { + void *va; + + if (io == NULL) { + LPERROR("%s, io is NULL while pa is not ANY\r\n", + __func__); + return -EINVAL; + } + va = metal_io_phys_to_virt(io, pa); + if (va == NULL) { + LPERROR("%s: no va is found\r\n", __func__); + return -EINVAL; + } + LPRINTF("Copy to %p (pa: %lx) with size 0x%lx\n", va, pa, size); + memcpy(va, (const void *)((const char *)fw_base + offset), size); + } + + return (int)size; +} + +struct image_store_ops mem_image_store_ops = { + .open = mem_image_open, + .close = mem_image_close, + .load = mem_image_load, + .features = SUPPORT_SEEK, +}; + diff --git a/apps/examples/load_fw/mem_image_store.c.orig b/apps/examples/load_fw/mem_image_store.c.orig new file mode 100644 index 0000000..10bd4a4 --- /dev/null +++ b/apps/examples/load_fw/mem_image_store.c.orig @@ -0,0 +1,107 @@ +/* + * Memory based image store Operation + * + * Copyright(c) 2018 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct mem_file { + const void *base; +}; + +int mem_image_open(void *store, const char *path, const void **image_data) +{ + int fd = -1; + struct stat file_stat; + char *buf = NULL; + int ret = -1; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + memset(&file_stat, 0, sizeof(file_stat)); + if (fstat(fd, &file_stat)) + return -1; + + LPRINTF("file size %d\n", file_stat.st_size); + buf = malloc(file_stat.st_size); + if (!buf) + return -1; + + ret = read(fd, buf, file_stat.st_size); + if (ret > 0) { + LPRINTF("%d read\n", ret); + } else if (ret < 0) { + LPRINTF("%s\n", strerror(errno)); + return ret; + } + *image_data = buf; + return file_stat.st_size; +} + +void mem_image_close(void *store) +{ + //TODO + /* The image is in memory, does nothing */ + (void)store; +} + +int mem_image_load(void *store, size_t offset, size_t size, + const void **data, metal_phys_addr_t pa, + struct metal_io_region *io, + char is_blocking) +{ + struct mem_file *image = store; + const void *fw_base = image->base; + + (void)is_blocking; + + LPRINTF("%s: offset=0x%x, size=0x%x\n\r", + __func__, offset, size); + if (pa == METAL_BAD_PHYS) { + if (data == NULL) { + LPERROR("%s: data is NULL while pa is ANY\r\n", + __func__); + return -EINVAL; + } + *data = (const void *)((const char *)fw_base + offset); + } else { + void *va; + + if (io == NULL) { + LPERROR("%s, io is NULL while pa is not ANY\r\n", + __func__); + return -EINVAL; + } + va = metal_io_phys_to_virt(io, pa); + if (va == NULL) { + LPERROR("%s: no va is found\r\n", __func__); + return -EINVAL; + } + memcpy(va, (const void *)((const char *)fw_base + offset), size); + } + + return (int)size; +} + +struct image_store_ops mem_image_store_ops = { + .open = mem_image_open, + .close = mem_image_close, + .load = mem_image_load, + .features = SUPPORT_SEEK, +}; + diff --git a/apps/examples/load_fw/platform_info.c b/apps/examples/load_fw/platform_info.c new file mode 100644 index 0000000..4fed796 --- /dev/null +++ b/apps/examples/load_fw/platform_info.c @@ -0,0 +1,116 @@ +/* + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#ifdef WITH_ZYNQMP +extern struct remoteproc_ops zynqmp_apu_rproc_ops; +extern struct remoteproc_ops zynqmp_rpu_rproc_ops; +#endif /* #ifdef WITH_ZYNQMP */ +#ifdef WITH_RV64 +extern struct remoteproc_ops virt_rv64_rproc_ops; +#endif /* #ifdef WITH_RV64 */ + +static struct remoteproc rproc_inst; +static struct remoteproc_ops ops; + +static struct remoteproc * platform_create_proc(unsigned int cpu_id) +{ + struct remoteproc * rproc; + +#ifdef WITH_RV64 + ops = virt_rv64_rproc_ops; +#elif defined(WITH_ZYNQMP) + if (NODE_RPU_0 <= LOAD_FW_TARGET && LOAD_FW_TARGET <= NODE_RPU_1) + ops = zynqmp_rpu_rproc_ops; + else if (NODE_APU_0 <= LOAD_FW_TARGET && LOAD_FW_TARGET <= NODE_APU_1) + ops = zynqmp_apu_rproc_ops; +#else + return NULL; +#endif /* #ifdef WITH_ZYNQMP */ + + rproc = remoteproc_init(&rproc_inst, &ops, &cpu_id); + if (!rproc) + return NULL; + return &rproc_inst; +} + +static void app_log_handler(enum metal_log_level level, + const char *format, ...) +{ + char msg[1024]; + va_list args; + static const char *level_strs[] = { + "metal: emergency: ", + "metal: alert: ", + "metal: critical: ", + "metal: error: ", + "metal: warning: ", + "metal: notice: ", + "metal: info: ", + "metal: debug: ", + }; + + va_start(args, format); + vsnprintf(msg, sizeof(msg), format, args); + va_end(args); + + if (level <= METAL_LOG_EMERGENCY || level > METAL_LOG_DEBUG) + level = METAL_LOG_EMERGENCY; + + LPRINTF("%s%s", level_strs[level], msg); +} + +#ifdef WITH_ZYNQMP +static XIpiPsu IpiInst; + +static XStatus IpiConfigure(XIpiPsu *const IpiInstPtr) +{ + XStatus Status; + XIpiPsu_Config *IpiCfgPtr; + + /* Look Up the config data */ + IpiCfgPtr = XIpiPsu_LookupConfig(XPAR_XIPIPSU_0_DEVICE_ID); + if (NULL == IpiCfgPtr) { + Status = XST_FAILURE; + LPERROR("%s ERROR in getting CfgPtr\n", __func__); + return Status; + } + + /* Init with the Cfg Data */ + Status = XIpiPsu_CfgInitialize(IpiInstPtr, IpiCfgPtr, + IpiCfgPtr->BaseAddress); + if (XST_SUCCESS != Status) { + LPERROR("%s ERROR #%d in configuring IPI\n", __func__, Status); + return Status; + } + return Status; +} +#endif /* #ifdef WITH_ZYNQMP */ + +struct remoteproc * app_init(unsigned int cpu_id){ + struct metal_init_params metal_param = { + .log_handler = app_log_handler, + .log_level = METAL_LOG_DEBUG, + }; + metal_init(&metal_param); + +#ifdef WITH_ZYNQMP + if (XST_SUCCESS != IpiConfigure(&IpiInst)) { + LPERROR("Failed to config IPI instance\r\n"); + return NULL; + } + + if (XST_SUCCESS != XPm_InitXilpm(&IpiInst)) { + LPERROR("Failed to initialize PM\r\n"); + return NULL; + } +#endif /* #ifdef WITH_ZYNQMP */ + + return platform_create_proc(cpu_id); +} diff --git a/apps/examples/load_fw/platform_info.h b/apps/examples/load_fw/platform_info.h new file mode 100644 index 0000000..bcf6441 --- /dev/null +++ b/apps/examples/load_fw/platform_info.h @@ -0,0 +1,11 @@ +/* + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +struct remoteproc * app_init(unsigned int cpu_id); +#endif /* PLATFORM_INFO_H_ */ diff --git a/apps/examples/load_fw/virt_rv64_rproc_example.c b/apps/examples/load_fw/virt_rv64_rproc_example.c new file mode 100644 index 0000000..424836a --- /dev/null +++ b/apps/examples/load_fw/virt_rv64_rproc_example.c @@ -0,0 +1,185 @@ +/* + * RV64 Virt APU life cycle management remoteproc example implementation + * + * Copyright(c) 2020 Bamvor Jian ZHANG + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#define DEV_BUS_NAME "platform" +#define RPROC_DEV_NAME "90200000.rproc" +static struct metal_io_region *rproc_io; /**< pointer to rproc i/o region */ +static struct metal_device *rproc_dev; /**< pointer to rproc device */ +struct remoteproc_mem rproc_mem; /**< rproc memory */ + +static struct remoteproc *apu_rproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct rproc_priv *priv; + unsigned int cpu_id = *((unsigned int *)arg); + struct metal_device *dev; + metal_phys_addr_t rproc_pa; + int ret = -1; + + if(cpu_id < RV_NODE_APU_0 || cpu_id > RV_NODE_APU_N) { + LPERROR("%s: invalid node id: %d \n\r",__func__, cpu_id); + return NULL; + } + + LPRINTF("%s: node id: %d\n\r", __func__, cpu_id); + priv = metal_allocate_memory(sizeof(*priv)); + if (!priv) + return NULL; + + memset(priv, 0, sizeof(*priv)); + priv->rproc = rproc; + priv->cpu_id = cpu_id; + priv->rproc->ops = ops; + metal_list_init(&priv->rproc->mems); + priv->rproc->priv = priv; + rproc->state = RPROC_READY; + /* Get remoteproc device */ + ret = metal_device_open(DEV_BUS_NAME, RPROC_DEV_NAME, + &dev); + if (ret) { + LPERROR("ERROR: failed to open shm device: %d.\r\n", + ret); + goto err_open; + } + LPRINTF("Successfully open rproc device.\r\n"); + rproc_dev = dev; + rproc_io = metal_device_io_region(dev, 0); + if (!rproc_io) + goto err_get_io; + + rproc_pa = metal_io_phys(rproc_io, 0); + remoteproc_init_mem(&rproc_mem, "rproc", rproc_pa, rproc_pa, + metal_io_region_size(rproc_io), + rproc_io); + remoteproc_add_mem(rproc, &rproc_mem); + LPRINTF("Successfully added rproc shared memory\r\n"); + return priv->rproc; + +err_get_io: + metal_device_close(rproc_dev); +err_open: + return NULL; +} + +static void apu_rproc_remove(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + + priv = (struct rproc_priv *)rproc->priv; + metal_free_memory(priv); +} + +static void *apu_rproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io) +{ + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + (void)attribute; + (void)size; + (void)rproc; + + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + tmpio = rproc_io; + if (!tmpio) + return NULL; + + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, lpa); +} + +#if 0 +static int apu_rproc_start(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + + priv = rproc->priv; + ret = XPm_RequestWakeUp(priv->cpu_id, true,rproc->bootaddr, + REQUEST_ACK_NO); + if (ret != XST_SUCCESS) { + LPRINTF("%s: Failed to start APU 0x%x, ret=0x%x\n\r", + __func__, priv->cpu_id, ret); + return -1; + } + return 0; +} + +static int apu_rproc_stop(struct remoteproc *rproc) +{ + /* It is lacking a stop operation in the libPM */ + (void)rproc; + return 0; +} + +static int apu_rproc_shutdown(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + struct remoteproc_mem *mem; + struct metal_list *node; + + priv = rproc->priv; + /* Delete all the registered remoteproc memories */ + metal_list_for_each(&rproc->mems, node) { + struct metal_list *tmpnode; + metal_phys_addr_t pa, pa_end; + + mem = metal_container_of(node, struct remoteproc_mem, node); + tmpnode = node; + /* Release TCM resource */ + pa = mem->pa; + pa_end = metal_io_phys(mem->io, metal_io_region_size(mem->io)); + if (pa_end <= 0x7FFFFFFF) + XPm_ReleaseNode(NODE_DDR); + if (pa >= 0xFFFC0000 && pa < 0xFFFD0000) + XPm_ReleaseNode(NODE_OCM_BANK_0); + if (pa <= 0xFFFDFFFF && pa_end >= 0xFFFD0000) + XPm_ReleaseNode(NODE_OCM_BANK_1); + if (pa <= 0xFFFEFFFF && pa_end >= 0xFFFE0000) + XPm_ReleaseNode(NODE_OCM_BANK_2); + if (pa_end >= 0xFFFF0000) + XPm_ReleaseNode(NODE_OCM_BANK_3); + + node = tmpnode->prev; + metal_list_del(tmpnode); + metal_free_memory(mem->io); + metal_free_memory(mem); + } + ret = XPm_ForcePowerDown(priv->cpu_id, REQUEST_ACK_NO); + if (ret != XST_SUCCESS) + return -1; + return 0; +} +#endif + +struct remoteproc_ops virt_rv64_rproc_ops = { + .init = apu_rproc_init, + .remove = apu_rproc_remove, +// .start = apu_rproc_start, +// .stop = apu_rproc_stop, +// .shutdown = apu_rproc_shutdown, + .mmap = apu_rproc_mmap, +}; diff --git a/apps/examples/load_fw/zynqmp_apu_lcm_rproc_example.c b/apps/examples/load_fw/zynqmp_apu_lcm_rproc_example.c new file mode 100644 index 0000000..2caec27 --- /dev/null +++ b/apps/examples/load_fw/zynqmp_apu_lcm_rproc_example.c @@ -0,0 +1,176 @@ +/* + * ZynqMP APU life cycle management remoteproc example implementation + * + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +static struct remoteproc *apu_rproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct rproc_priv *priv; + unsigned int cpu_id = *((unsigned int *)arg); + if(cpu_id < NODE_APU_0 || cpu_id > NODE_APU_1) { + LPERROR("%s: invalid node id: %d \n\r",__func__, cpu_id); + return NULL; + } + + LPRINTF("%s: node id: %d\n\r", __func__, cpu_id); + priv = metal_allocate_memory(sizeof(*priv)); + if (!priv) + return NULL; + memset(priv, 0, sizeof(*priv)); + priv->rproc = rproc; + priv->cpu_id = cpu_id; + priv->rproc->ops = ops; + metal_list_init(&priv->rproc->mems); + priv->rproc->priv = priv; + rproc->state = RPROC_READY; + return priv->rproc; +} + +static void apu_rproc_remove(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + + priv = (struct rproc_priv *)rproc->priv; + metal_free_memory(priv); +} + +static void *apu_rproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + metal_phys_addr_t lpa, lda, lda_end; + + if ((!da || !pa) || (*da == METAL_BAD_PHYS && *pa == METAL_BAD_PHYS)) + return NULL; + + LPRINTF("%s: pa=0x%x, da=0x%x, size=0x%x, atrribute=0x%x\n\r", + __func__, *pa, *da, size, attribute); + lda = *da; + lpa = *pa; + if (!attribute) + attribute = NORM_SHARED_NCACHE | PRIV_RW_USER_RW; + + + lda_end = lda + size; + if (lda_end <= 0x80000000) + XPm_RequestNode(NODE_DDR,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda >= 0xFFFC0000 && lda < 0xFFFD0000) + XPm_RequestNode(NODE_OCM_BANK_0,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0xFFFDFFFF && lda_end >= 0xFFFD0000) + XPm_RequestNode(NODE_OCM_BANK_1,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0xFFFEFFFF && lda_end >= 0xFFFE0000) + XPm_RequestNode(NODE_OCM_BANK_2,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda_end >= 0xFFFF0000) + XPm_RequestNode(NODE_OCM_BANK_3,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + mem->pa = lpa; + mem->da = lda; + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + /* This code only runs on the R5, which has a flat memory + * space. Therefore, we use the same value for the physical + * and virtual addresses we pass in to metal_io_init(). + */ + metal_io_init(*io, (void *)mem->pa, &mem->pa, size, + sizeof(metal_phys_addr_t)<<3, attribute, NULL); + mem->io = *io; + metal_list_add_tail(&rproc->mems, &mem->node); + *pa = lpa; + *da = lda; + mem->size = size; + return metal_io_phys_to_virt(*io, mem->pa); +} + +static int apu_rproc_start(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + + priv = rproc->priv; + ret = XPm_RequestWakeUp(priv->cpu_id, true,rproc->bootaddr, + REQUEST_ACK_NO); + if (ret != XST_SUCCESS) { + LPRINTF("%s: Failed to start APU 0x%x, ret=0x%x\n\r", + __func__, priv->cpu_id, ret); + return -1; + } + return 0; +} + +static int apu_rproc_stop(struct remoteproc *rproc) +{ + /* It is lacking a stop operation in the libPM */ + (void)rproc; + return 0; +} + +static int apu_rproc_shutdown(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + struct remoteproc_mem *mem; + struct metal_list *node; + + priv = rproc->priv; + /* Delete all the registered remoteproc memories */ + metal_list_for_each(&rproc->mems, node) { + struct metal_list *tmpnode; + metal_phys_addr_t pa, pa_end; + + mem = metal_container_of(node, struct remoteproc_mem, node); + tmpnode = node; + /* Release TCM resource */ + pa = mem->pa; + pa_end = metal_io_phys(mem->io, metal_io_region_size(mem->io)); + if (pa_end <= 0x7FFFFFFF) + XPm_ReleaseNode(NODE_DDR); + if (pa >= 0xFFFC0000 && pa < 0xFFFD0000) + XPm_ReleaseNode(NODE_OCM_BANK_0); + if (pa <= 0xFFFDFFFF && pa_end >= 0xFFFD0000) + XPm_ReleaseNode(NODE_OCM_BANK_1); + if (pa <= 0xFFFEFFFF && pa_end >= 0xFFFE0000) + XPm_ReleaseNode(NODE_OCM_BANK_2); + if (pa_end >= 0xFFFF0000) + XPm_ReleaseNode(NODE_OCM_BANK_3); + + node = tmpnode->prev; + metal_list_del(tmpnode); + metal_free_memory(mem->io); + metal_free_memory(mem); + } + ret = XPm_ForcePowerDown(priv->cpu_id, REQUEST_ACK_NO); + if (ret != XST_SUCCESS) + return -1; + return 0; +} + +struct remoteproc_ops zynqmp_apu_rproc_ops = { + .init = apu_rproc_init, + .remove = apu_rproc_remove, + .start = apu_rproc_start, + .stop = apu_rproc_stop, + .shutdown = apu_rproc_shutdown, + .mmap = apu_rproc_mmap, +}; diff --git a/apps/examples/load_fw/zynqmp_r5_lcm_rproc_example.c b/apps/examples/load_fw/zynqmp_r5_lcm_rproc_example.c new file mode 100644 index 0000000..26a09e5 --- /dev/null +++ b/apps/examples/load_fw/zynqmp_r5_lcm_rproc_example.c @@ -0,0 +1,313 @@ +/* + * ZynqMP RPU life cycle management remoteproc example implementation + * + * Copyright(c) 2018 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +/* Xilinx headers */ +#include +#include +#include +#include +#include + +#define LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +#define RPU_BASE_ADDR 0xFF9A0000 +#define RPU_GLBL_CNTL_OFFSET 0 +#define RPU0_CFG_BASE_OFFSET 0x100 +#define RPU1_CFG_BASE_OFFSET 0x200 +#define RPU_CFG_BASE_OFFSET(n) (RPU0_CFG_BASE_OFFSET << (n)) +/* Boot memory bit. high for OCM, low for TCM */ +#define VINITHI_BIT metal_bit(2) +/* CPU halt bit, high: processor is running. low: processor is halt */ +#define nCPUHALT_BIT metal_bit(0) +/* RPU mode, high: split mode. low: lock step mode */ +#define SLSPLIT_BIT metal_bit(3) +/* Clamp mode. high: split mode. low: lock step mode */ +#define SLCLAMP_BIT metal_bit(4) +/* TCM mode. high: combine RPU TCMs. low: split TCM for RPU1 and RPU0 */ +#define TCM_COMB_BIT metal_bit(6) + +struct r5_rproc_priv { + struct remoteproc *rproc; + int cpu_id; + metal_phys_addr_t rpu_base; + struct metal_io_region rpu_io; +}; + +/** + * r5_rproc_boot_addr_config - configure the boot address of R5 + * @pdata: platform data + * + * This function will set the boot address based on if the + * boot memory in the ELF file is TCM or OCM + */ +static uint32_t r5_rproc_boot_addr_config(struct r5_rproc_priv *priv, + metal_phys_addr_t bootaddr) +{ + uint32_t rpu_resetaddr; + + LPRINTF("%s: R5 ID: %d, boot_addr 0x%x\r\n", + __func__, priv->cpu_id, bootaddr); + + if (bootaddr < 0x40000) + rpu_resetaddr = 0; + else + rpu_resetaddr = 1; + return rpu_resetaddr; +} + +/** + * r5_rproc_mode_config - configure R5 operation mode + * @pdata: platform data + * + * configure R5 to split mode or lockstep mode + * based on the platform data. + */ +static void r5_rproc_mode_config(struct r5_rproc_priv *priv) +{ + uint32_t tmp; + + LPRINTF("%s: mode: %d\r\n", __func__, priv->cpu_id); + tmp = metal_io_read32(&priv->rpu_io, RPU_GLBL_CNTL_OFFSET); + if (priv->cpu_id == NODE_RPU) { + /* RPU lock step mode */ + tmp &= ~SLSPLIT_BIT; + tmp |= TCM_COMB_BIT; + tmp |= SLCLAMP_BIT; + } else { + /* RPU split mode */ + tmp |= SLSPLIT_BIT; + tmp &= ~TCM_COMB_BIT; + tmp &= ~SLCLAMP_BIT; + } + metal_io_write32(&priv->rpu_io, RPU_GLBL_CNTL_OFFSET, tmp); +} + +struct remoteproc *r5_rproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct r5_rproc_priv *priv; + unsigned int cpu_id = *((unsigned int *)arg); + + if (cpu_id < NODE_RPU_0 || cpu_id > NODE_RPU_1) { + xil_printf("rproc init: invalide node id: %d\r\n", cpu_id); + return NULL; + } + + xil_printf("rproc init: node id: %d\r\n", cpu_id); + priv = metal_allocate_memory(sizeof(*priv)); + if (!priv) + return NULL; + memset(priv, 0, sizeof(*priv)); + priv->rproc = rproc; + priv->cpu_id = cpu_id; + priv->rproc->ops = ops; + metal_list_init(&priv->rproc->mems); + priv->rproc->priv = priv; + priv->rpu_base = RPU_BASE_ADDR; + metal_io_init(&priv->rpu_io, (void *)RPU_BASE_ADDR, &priv->rpu_base, + 0x1000, (metal_phys_addr_t)(-1), + DEVICE_NONSHARED | PRIV_RW_USER_RW, NULL); + + r5_rproc_mode_config(priv); + rproc->state = RPROC_READY; + return priv->rproc; +} + +void r5_rproc_remove(struct remoteproc *rproc) +{ + if (rproc) { + struct r5_rproc_priv *priv; + + priv = (struct r5_rproc_priv *)rproc->priv; + metal_free_memory(priv); + } +} + +void *r5_rproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + struct r5_rproc_priv *priv; + metal_phys_addr_t lpa, lda; + + priv = rproc->priv; + + if (!da || !pa) + return NULL; + LPRINTF("%s: pa=0x%x, da=0x%x, size=0x%x, atrribute=0x%x\r\n", + __func__, *pa, *da, size, attribute); + lda = *da; + lpa = *pa; + if (!attribute) + attribute = NORM_SHARED_NCACHE | PRIV_RW_USER_RW; + if (lda <= 0x40000) { + metal_phys_addr_t lda_end; + + lda_end = lda + size; + if (priv->cpu_id == NODE_RPU_0 || priv->cpu_id == NODE_RPU) { + lpa = 0xFFE00000 + lda; + if (lda < 0x10000) + XPm_RequestNode(NODE_TCM_0_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (lda <= 0x20000 && lda_end >= 0x10000) + XPm_RequestNode(NODE_TCM_1_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (lda <= 0x30000 && lda_end >= 0x20000) + XPm_RequestNode(NODE_TCM_0_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (lda <= 0x40000 && lda_end >= 0x30000) + XPm_RequestNode(NODE_TCM_1_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + } else if (priv->cpu_id == NODE_RPU_1) { + lpa = 0xFFE90000 + lda; + if (lda < 0x10000) + XPm_RequestNode(NODE_TCM_1_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (lda <= 0x30000 && lda_end >= 0x20000) + XPm_RequestNode(NODE_TCM_1_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + } else { + LPERROR("mmap failed: invalid cpu node: %d\r\n", + priv->cpu_id); + return NULL; + } + } + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lpa == METAL_BAD_PHYS) + return NULL; + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + mem->pa = lpa; + mem->da = lda; + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + metal_io_init(*io, (void *)mem->pa, &mem->pa, size, + sizeof(metal_phys_addr_t)<<3, attribute, NULL); + mem->io = *io; + metal_list_add_tail(&rproc->mems, &mem->node); + *pa = lpa; + *da = lda; + mem->size = size; + return metal_io_phys_to_virt(*io, mem->pa); +} + +int r5_rproc_start(struct remoteproc *rproc) +{ + struct r5_rproc_priv *priv; + int ret; + uint32_t resetaddr; + + priv = rproc->priv; + resetaddr = r5_rproc_boot_addr_config(priv, rproc->bootaddr); + ret = XPm_RequestWakeUp(priv->cpu_id, true, resetaddr, + REQUEST_ACK_BLOCKING); + if (ret != XST_SUCCESS) { + LPRINTF("%s: Failed to start RPU 0x%x, ret=0x%x\r\n", + __func__, priv->cpu_id, ret); + return -1; + } else { + return 0; + } +} + +int r5_rproc_stop(struct remoteproc *rproc) +{ + /* It is lacking a stop operation in the libPM */ + (void)rproc; + return 0; +} + +int r5_rproc_shutdown(struct remoteproc *rproc) +{ + struct r5_rproc_priv *priv; + int ret; + struct remoteproc_mem *mem; + struct metal_list *node; + + priv = rproc->priv; + /* Delete all the registered remoteproc memories */ + metal_list_for_each(&rproc->mems, node) { + struct metal_list *tmpnode; + metal_phys_addr_t pa, pa_end; + + mem = metal_container_of(node, struct remoteproc_mem, node); + tmpnode = node; + /* Release TCM resource */ + pa = mem->pa; + pa_end = metal_io_phys(mem->io, metal_io_region_size(mem->io)); + if (priv->cpu_id == NODE_RPU_0 || priv->cpu_id == NODE_RPU) { + if (pa < 0xFFE10000) + XPm_RequestNode(NODE_TCM_0_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (pa <= 0xFFE20000 && pa_end >= 0xFFE10000) + XPm_RequestNode(NODE_TCM_1_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (pa <= 0xFFE30000 && pa_end >= 0xFFE20000) + XPm_RequestNode(NODE_TCM_0_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (pa <= 0xFFE40000 && pa_end >= 0xFFE30000) + XPm_RequestNode(NODE_TCM_1_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + } else if (priv->cpu_id == NODE_RPU_1) { + if (pa < 0xFFEA0000) + XPm_RequestNode(NODE_TCM_1_A, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + if (pa <= 0xFFC0000 && pa_end >= 0xFFEB0000) + XPm_RequestNode(NODE_TCM_1_B, + PM_CAP_ACCESS, 0, + REQUEST_ACK_BLOCKING); + } + node = tmpnode->prev; + metal_list_del(tmpnode); + metal_free_memory(mem->io); + metal_free_memory(mem); + } + + ret = XPm_ForcePowerDown(priv->cpu_id, REQUEST_ACK_BLOCKING); + if (ret != XST_SUCCESS) + return -1; + else + return 0; +} + +struct remoteproc_ops r5_rproc_ops = { + .init = r5_rproc_init, + .remove = r5_rproc_remove, + .start = r5_rproc_start, + .stop = r5_rproc_stop, + .shutdown = r5_rproc_shutdown, + .mmap = r5_rproc_mmap, +}; diff --git a/apps/examples/load_fw/zynqmp_rpu_lcm_rproc_example.c b/apps/examples/load_fw/zynqmp_rpu_lcm_rproc_example.c new file mode 100644 index 0000000..34f9b6b --- /dev/null +++ b/apps/examples/load_fw/zynqmp_rpu_lcm_rproc_example.c @@ -0,0 +1,203 @@ +/* + * ZynqMP RPU life cycle management remoteproc example implementation + * + * Copyright(c) 2019 Xilinx Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +static struct remoteproc *rpu_rproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct rproc_priv *priv; + unsigned int cpu_id = *((unsigned int *)arg); + + if (cpu_id < NODE_RPU_0 || cpu_id > NODE_RPU_1) { + LPERROR("%s: invalid node id: %d\n\r", __func__, cpu_id); + return NULL; + } + + LPRINTF("%s: node id: %d\n\r", __func__, cpu_id); + priv = metal_allocate_memory(sizeof(*priv)); + if (!priv) + return NULL; + memset(priv, 0, sizeof(*priv)); + priv->rproc = rproc; + priv->cpu_id = cpu_id; + priv->rproc->ops = ops; + metal_list_init(&priv->rproc->mems); + priv->rproc->priv = priv; + rproc->state = RPROC_READY; + return priv->rproc; +} + +static void rpu_rproc_remove(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + + priv = (struct rproc_priv *)rproc->priv; + metal_free_memory(priv); +} + +static void *rpu_rproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + struct rproc_priv *priv; + metal_phys_addr_t lpa, lda, lda_end; + + priv = rproc->priv; + + if ((!da || !pa) || (*da == METAL_BAD_PHYS && *pa == METAL_BAD_PHYS)) + return NULL; + LPRINTF("%s: pa=0x%x, da=0x%x, size=0x%x, atrribute=0x%x\n\r", + __func__, *pa, *da, size, attribute); + lda = *da; + lpa = *pa; + if (!attribute) + attribute = NORM_SHARED_NCACHE | PRIV_RW_USER_RW; + + lda_end = lda + size; + /* call xilpm request node for relevant memory */ + if (lda <= 0x40000) { + if (priv->cpu_id == NODE_RPU_0 || priv->cpu_id == NODE_RPU) { + lpa = 0xFFE00000 + lda; + if (lda < 0x10000) + XPm_RequestNode(NODE_TCM_0_A,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0x20000 && lda_end >= 0x10000) + XPm_RequestNode(NODE_TCM_1_A,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0x30000 && lda_end >= 0x20000) + XPm_RequestNode(NODE_TCM_0_B,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0x40000 && lda_end >= 0x30000) + XPm_RequestNode(NODE_TCM_1_B,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + } else if (priv->cpu_id == NODE_RPU_1) { + lpa = 0xFFE90000 + lda; + if (lda < 0x10000) + XPm_RequestNode(NODE_TCM_1_A,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + if (lda <= 0x30000 && lda_end >= 0x20000) + XPm_RequestNode(NODE_TCM_1_B,PM_CAP_ACCESS, + XPM_MIN_QOS,REQUEST_ACK_NO); + } else { + LPERROR("mmap failed: invalid cpu node: %d\r\n", priv->cpu_id); + return NULL; + } + } + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + mem->pa = lpa; + mem->da = lda; + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + /* This code only runs on the R5, which has a flat memory + * space. Therefore, we use the same value for the physical + * and virtual addresses we pass in to metal_io_init(). + */ + metal_io_init(*io, (void *)mem->pa, &mem->pa, size, + sizeof(metal_phys_addr_t)<<3, attribute, NULL); + mem->io = *io; + metal_list_add_tail(&rproc->mems, &mem->node); + *pa = lpa; + *da = lda; + mem->size = size; + return metal_io_phys_to_virt(*io, mem->pa); +} + +static int rpu_rproc_start(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + + priv = rproc->priv; + ret = XPm_RequestWakeUp(priv->cpu_id, true, rproc->bootaddr, + REQUEST_ACK_NO); + if (ret != XST_SUCCESS) { + LPRINTF("%s: Failed to start RPU 0x%x, ret=0x%x\n\r", + __func__, priv->cpu_id, ret); + return -1; + } + return 0; +} + +static int rpu_rproc_stop(struct remoteproc *rproc) +{ + /* It is lacking a stop operation in the libPM */ + (void)rproc; + return 0; +} + +static int rpu_rproc_shutdown(struct remoteproc *rproc) +{ + struct rproc_priv *priv; + int ret; + struct remoteproc_mem *mem; + struct metal_list *node; + + priv = rproc->priv; + /* Delete all the registered remoteproc memories */ + metal_list_for_each(&rproc->mems, node) { + struct metal_list *tmpnode; + metal_phys_addr_t pa, pa_end; + + mem = metal_container_of(node, struct remoteproc_mem, node); + tmpnode = node; + /* Release TCM resource */ + pa = mem->pa; + pa_end = metal_io_phys(mem->io, metal_io_region_size(mem->io)); + node = tmpnode->prev; + metal_list_del(tmpnode); + metal_free_memory(mem->io); + metal_free_memory(mem); + /* call xilpm release node for relevant memory */ + if (pa <= 0x40000) { + if (priv->cpu_id == NODE_RPU_0 || priv->cpu_id == NODE_RPU) { + if (pa < 0x10000) + XPm_ReleaseNode(NODE_TCM_0_A); + if (pa <= 0x20000 && pa_end >= 0x10000) + XPm_ReleaseNode(NODE_TCM_1_A); + if (pa <= 0x30000 && pa_end >= 0x20000) + XPm_ReleaseNode(NODE_TCM_0_B); + if (pa <= 0x40000 && pa_end >= 0x30000) + XPm_ReleaseNode(NODE_TCM_1_B); + } else if (priv->cpu_id == NODE_RPU_1) { + if (pa < 0x10000) + XPm_ReleaseNode(NODE_TCM_1_A); + if (pa <= 0x30000 && pa_end >= 0x20000) + XPm_ReleaseNode(NODE_TCM_1_B); + } else { + LPERROR("unmap failed: invalid cpu node: %d\r\n", priv->cpu_id); + return NULL; + } + } + + } + + ret = XPm_ForcePowerDown(priv->cpu_id, REQUEST_ACK_NO); + if (ret != XST_SUCCESS) + return -1; + return 0; +} + +struct remoteproc_ops zynqmp_rpu_rproc_ops = { + .init = rpu_rproc_init, + .remove = rpu_rproc_remove, + .start = rpu_rproc_start, + .stop = rpu_rproc_stop, + .shutdown = rpu_rproc_shutdown, + .mmap = rpu_rproc_mmap, +}; diff --git a/apps/examples/matrix_multiply/CMakeLists.txt b/apps/examples/matrix_multiply/CMakeLists.txt new file mode 100644 index 0000000..8914e17 --- /dev/null +++ b/apps/examples/matrix_multiply/CMakeLists.txt @@ -0,0 +1,41 @@ +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS} -fdata-sections -ffunction-sections") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) +foreach (_app matrix_multiply matrix_multiplyd) + collector_list (_sources APP_COMMON_SOURCES) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) + diff --git a/apps/examples/matrix_multiply/matrix_multiply.c b/apps/examples/matrix_multiply/matrix_multiply.c new file mode 100644 index 0000000..1fbaf6b --- /dev/null +++ b/apps/examples/matrix_multiply/matrix_multiply.c @@ -0,0 +1,257 @@ +/* This is a sample demonstration application that showcases usage of remoteproc +and rpmsg APIs on the remote core. This application is meant to run on the remote CPU +running baremetal code. This applicationr receives two matrices from the master, +multiplies them and returns the result to the master core. */ + +#include +#include +#include +#include +#include +#include +#include "matrix_multiply.h" +#include "platform_info.h" + +#define MAX_SIZE 6 +#define NUM_MATRIX 2 + +#define raw_printf(format, ...) printf(format, ##__VA_ARGS__) +#define LPRINTF(format, ...) raw_printf("CLIENT> " format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +typedef struct _matrix { + unsigned int size; + unsigned int elements[MAX_SIZE][MAX_SIZE]; +} matrix; + +/* Globals */ +static struct rpmsg_endpoint lept; +static struct _matrix i_matrix[2]; +static struct _matrix e_matrix; +static unsigned int result_returned = 0; +static int err_cnt = 0; +static int ept_deleted = 0; + +/** + * _gettimeofday() is called from time() which is used by srand() to generate + * random number. It is defined here in case this function is not defined in + * library. + */ +int __attribute__((weak)) _gettimeofday(struct timeval *tv, void *tz) +{ + (void)tv; + (void)tz; + return 0; +} + +static void matrix_print(struct _matrix *m) +{ + unsigned int i, j; + + /* Generate two random matrices */ + LPRINTF("Printing matrix... \r\n"); + + for (i = 0; i < m->size; ++i) { + for (j = 0; j < m->size; ++j) + raw_printf(" %u ", m->elements[i][j]); + raw_printf("\r\n"); + } +} + +static void generate_matrices(int num_matrices, + unsigned int matrix_size, void *p_data) +{ + unsigned int i, j, k; + struct _matrix *p_matrix = p_data; + unsigned long value; + + + for (i = 0; i < (unsigned int)num_matrices; i++) { + /* Initialize workload */ + p_matrix[i].size = matrix_size; + + LPRINTF("Input matrix %d \r\n", i); + for (j = 0; j < matrix_size; j++) { + raw_printf("\r\n"); + for (k = 0; k < matrix_size; k++) { + + value = (rand() & 0x7F); + value = value % 10; + p_matrix[i].elements[j][k] = value; + raw_printf(" %u ", p_matrix[i].elements[j][k]); + } + } + raw_printf("\r\n"); + } + +} + +static void matrix_multiply(const matrix * m, const matrix * n, matrix * r) +{ + unsigned int i, j, k; + + memset(r, 0x0, sizeof(matrix)); + r->size = m->size; + + for (i = 0; i < m->size; ++i) { + for (j = 0; j < n->size; ++j) { + for (k = 0; k < r->size; ++k) { + r->elements[i][j] += + m->elements[i][k] * n->elements[k][j]; + } + } + } +} + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) +{ + struct _matrix *r_matrix = (struct _matrix *)data; + int i, j; + + (void)ept; + (void)priv; + (void)src; + if (len != sizeof(struct _matrix)) { + LPERROR("Received matrix is of invalid len: %d:%lu\r\n", + (int)sizeof(struct _matrix), (unsigned long)len); + err_cnt++; + return RPMSG_SUCCESS; + } + for (i = 0; i < MAX_SIZE; i++) { + for (j = 0; j < MAX_SIZE; j++) { + if (r_matrix->elements[i][j] != + e_matrix.elements[i][j]) { + err_cnt++; + break; + } + } + } + if (err_cnt) { + LPERROR("Result mismatched...\r\n"); + LPERROR("Expected matrix:\r\n"); + matrix_print(&e_matrix); + LPERROR("Actual matrix:\r\n"); + matrix_print(r_matrix); + } else { + result_returned = 1; + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&lept); + LPRINTF("echo test: service is destroyed\r\n"); + ept_deleted = 1; +} + +static void rpmsg_name_service_bind_cb(struct rpmsg_device *rdev, + const char *name, uint32_t dest) +{ + if (strcmp(name, RPMSG_SERVICE_NAME)) + LPERROR("Unexpected name service %s.\r\n", name); + else + (void)rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, dest, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app (struct rpmsg_device *rdev, void *priv) +{ + int c; + int ret; + + LPRINTF("Compute thread unblocked ..\r\n"); + LPRINTF("It will generate two random matrices.\r\n"); + LPRINTF("Send to the remote and get the computation result back.\r\n"); + LPRINTF("It will then check if the result is expected.\r\n"); + + /* Create RPMsg endpoint */ + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create RPMsg endpoint.\r\n"); + return ret; + } + + while (!is_rpmsg_ept_ready(&lept)) + platform_poll(priv); + + LPRINTF("RPMSG endpoint is binded with remote.\r\n"); + err_cnt = 0; + srand(time(NULL)); + for (c = 0; c < 200; c++) { + generate_matrices(2, MAX_SIZE, i_matrix); + matrix_multiply(&i_matrix[0], &i_matrix[1], + &e_matrix); + result_returned = 0; + ret = rpmsg_send(&lept, i_matrix, sizeof(i_matrix)); + + if (ret < 0) { + LPRINTF("Error sending data...\r\n"); + break; + } + LPRINTF("Matrix multiply: sent : %lu\r\n", + (unsigned long)sizeof(i_matrix)); + do { + platform_poll(priv); + } while (!result_returned && !err_cnt && !ept_deleted); + + if (err_cnt) + break; + } + + LPRINTF("**********************************\r\n"); + LPRINTF(" Test Results: Error count = %d \r\n", err_cnt); + LPRINTF("**********************************\r\n"); + + /* Detroy RPMsg endpoint */ + rpmsg_destroy_ept(&lept); + LPRINTF("Quitting application .. Matrix multiplication end\r\n"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, + rpmsg_name_service_bind_cb); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/examples/matrix_multiply/matrix_multiply.h b/apps/examples/matrix_multiply/matrix_multiply.h new file mode 100644 index 0000000..3257558 --- /dev/null +++ b/apps/examples/matrix_multiply/matrix_multiply.h @@ -0,0 +1,6 @@ +#ifndef MATRIX_MULTIPLY_H +#define MATRIX_MULTIPLY_H + +#define RPMSG_SERVICE_NAME "rpmsg-openamp-demo-channel" + +#endif /* MATRIX_MULTIPLY_H */ diff --git a/apps/examples/matrix_multiply/matrix_multiplyd.c b/apps/examples/matrix_multiply/matrix_multiplyd.c new file mode 100644 index 0000000..9fbc8b6 --- /dev/null +++ b/apps/examples/matrix_multiply/matrix_multiplyd.c @@ -0,0 +1,172 @@ +/* This is a sample demonstration application that showcases usage of remoteproc +and rpmsg APIs on the remote core. This application is meant to run on the remote CPU +running baremetal code. This applicationr receives two matrices from the master, +multiplies them and returns the result to the master core. */ + +#include +#include +#include +#include +#include "matrix_multiply.h" +#include "platform_info.h" + +#define MAX_SIZE 6 +#define NUM_MATRIX 2 + +#define SHUTDOWN_MSG 0xEF56A55A + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +typedef struct _matrix { + unsigned int size; + unsigned int elements[MAX_SIZE][MAX_SIZE]; +} matrix; + +/* Local variables */ +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/*-----------------------------------------------------------------------------* + * Calculate the Matrix + *-----------------------------------------------------------------------------*/ +static void Matrix_Multiply(const matrix *m, const matrix *n, matrix *r) +{ + unsigned int i, j, k; + + memset(r, 0x0, sizeof(matrix)); + r->size = m->size; + + for (i = 0; i < m->size; ++i) { + for (j = 0; j < n->size; ++j) { + for (k = 0; k < r->size; ++k) { + r->elements[i][j] += + m->elements[i][k] * n->elements[k][j]; + } + } + } +} + +/*-----------------------------------------------------------------------------* + * RPMSG callbacks setup by remoteproc_resource_init() + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + matrix matrix_array[NUM_MATRIX]; + matrix matrix_result; + + (void)priv; + (void)src; + + if ((*(unsigned int *)data) == SHUTDOWN_MSG) { + LPRINTF("shutdown message is received.\r\n"); + shutdown_req = 1; + return RPMSG_SUCCESS; + } + + memcpy(matrix_array, data, len); + /* Process received data and multiple matrices. */ + Matrix_Multiply(&matrix_array[0], &matrix_array[1], &matrix_result); + + /* Send the result of matrix multiplication back to master. */ + if (rpmsg_send(ept, &matrix_result, sizeof(matrix)) < 0) { + LPERROR("rpmsg_send failed\r\n"); + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPERROR("Endpoint is destroyed\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int matrix_multiplyd_app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Waiting for events...\r\n"); + while(1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int __matrix_multiplyd(int argc, char *argv[]); + +int matrix_multiplyd() +{ + __matrix_multiplyd(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(matrix_multiplyd, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(matrix_multiplyd, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int __matrix_multiplyd(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); +#ifdef METAL_SYSTEM_RTTHREAD + //Make rt-thread happy + shutdown_req = 0; + memset(&lept, 0, sizeof(lept)); +#endif /* #ifdef FINSH_USING_MSH */ + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev,0); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/matrix_multiply/matrix_multiplyd.c.orig b/apps/examples/matrix_multiply/matrix_multiplyd.c.orig new file mode 100644 index 0000000..f26ec99 --- /dev/null +++ b/apps/examples/matrix_multiply/matrix_multiplyd.c.orig @@ -0,0 +1,167 @@ +/* This is a sample demonstration application that showcases usage of remoteproc +and rpmsg APIs on the remote core. This application is meant to run on the remote CPU +running baremetal code. This applicationr receives two matrices from the master, +multiplies them and returns the result to the master core. */ + +#include +#include +#include +#include +#include "matrix_multiply.h" +#include "platform_info.h" + +#define MAX_SIZE 6 +#define NUM_MATRIX 2 + +#define SHUTDOWN_MSG 0xEF56A55A + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +typedef struct _matrix { + unsigned int size; + unsigned int elements[MAX_SIZE][MAX_SIZE]; +} matrix; + +/* Local variables */ +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/*-----------------------------------------------------------------------------* + * Calculate the Matrix + *-----------------------------------------------------------------------------*/ +static void Matrix_Multiply(const matrix *m, const matrix *n, matrix *r) +{ + unsigned int i, j, k; + + memset(r, 0x0, sizeof(matrix)); + r->size = m->size; + + for (i = 0; i < m->size; ++i) { + for (j = 0; j < n->size; ++j) { + for (k = 0; k < r->size; ++k) { + r->elements[i][j] += + m->elements[i][k] * n->elements[k][j]; + } + } + } +} + +/*-----------------------------------------------------------------------------* + * RPMSG callbacks setup by remoteproc_resource_init() + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + matrix matrix_array[NUM_MATRIX]; + matrix matrix_result; + + (void)priv; + (void)src; + + if ((*(unsigned int *)data) == SHUTDOWN_MSG) { + LPRINTF("shutdown message is received.\r\n"); + shutdown_req = 1; + return RPMSG_SUCCESS; + } + + memcpy(matrix_array, data, len); + /* Process received data and multiple matrices. */ + Matrix_Multiply(&matrix_array[0], &matrix_array[1], &matrix_result); + + /* Send the result of matrix multiplication back to master. */ + if (rpmsg_send(ept, &matrix_result, sizeof(matrix)) < 0) { + LPERROR("rpmsg_send failed\r\n"); + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPERROR("Endpoint is destroyed\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int matrix_multiplyd_app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Waiting for events...\r\n"); + while(1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int __matrix_multiplyd(int argc, char *argv[]); + +int matrix_multiplyd() +{ + __matrix_multiplyd(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(matrix_multiplyd, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(matrix_multiplyd, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int __matrix_multiplyd(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/matrix_multiply/matrix_multiplyd.c.rej b/apps/examples/matrix_multiply/matrix_multiplyd.c.rej new file mode 100644 index 0000000..0a9a543 --- /dev/null +++ b/apps/examples/matrix_multiply/matrix_multiplyd.c.rej @@ -0,0 +1,11 @@ +--- apps/examples/matrix_multiply/matrix_multiplyd.c ++++ apps/examples/matrix_multiply/matrix_multiplyd.c +@@ -158,7 +175,7 @@ int main(int argc, char *argv[]) + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { +- app(rpdev, platform); ++ matrix_multiplyd_app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev, platform); + ret = 0; + } diff --git a/apps/examples/matrix_multiply/matrix_multiplyd.o b/apps/examples/matrix_multiply/matrix_multiplyd.o new file mode 100644 index 0000000..abd0350 Binary files /dev/null and b/apps/examples/matrix_multiply/matrix_multiplyd.o differ diff --git a/apps/examples/rpc_demo/CMakeLists.txt b/apps/examples/rpc_demo/CMakeLists.txt new file mode 100644 index 0000000..5d5655d --- /dev/null +++ b/apps/examples/rpc_demo/CMakeLists.txt @@ -0,0 +1,49 @@ +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS}") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_LARGE_TEXT_OPT) +if (NOT _linker_opt) + get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +endif (NOT _linker_opt) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +if (${PROJECT_SYSTEM} STREQUAL "linux") + set (app_list rpc_demod) +else (${PROJECT_SYSTEM}) + set (app_list rpc_demo) +endif (${PROJECT_SYSTEM} STREQUAL "linux") + +foreach (_app ${app_list}) + collector_list (_sources APP_COMMON_SOURCES) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) diff --git a/apps/examples/rpc_demo/rpc_demo.c b/apps/examples/rpc_demo/rpc_demo.c new file mode 100644 index 0000000..fda5c8c --- /dev/null +++ b/apps/examples/rpc_demo/rpc_demo.c @@ -0,0 +1,169 @@ +/* This is a sample demonstration application that showcases usage of proxy from the remote core. + This application is meant to run on the remote CPU running baremetal. + This applicationr can print to to master console and perform file I/O using proxy mechanism. */ + +#include +#include +#include +#include +#include +#include +#include +#include "rsc_table.h" +#include "platform_info.h" +#include "rpmsg-rpc-demo.h" + +#define REDEF_O_CREAT 0000100 +#define REDEF_O_EXCL 0000200 +#define REDEF_O_RDONLY 0000000 +#define REDEF_O_WRONLY 0000001 +#define REDEF_O_RDWR 0000002 +#define REDEF_O_APPEND 0002000 +#define REDEF_O_ACCMODE 0000003 + +#define LPRINTF(format, ...) xil_printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static void rpmsg_rpc_shutdown(struct rpmsg_rpc_data *rpc) +{ + (void)rpc; + LPRINTF("RPMSG RPC is shutting down.\r\n"); +} + +/*-----------------------------------------------------------------------------* + * Application specific + *-----------------------------------------------------------------------------*/ +int app(struct rpmsg_device *rdev, void *priv) +{ + struct rpmsg_rpc_data rpc; + struct rpmsg_rpc_syscall rpccall; + int fd, bytes_written, bytes_read; + char fname[] = "remote.file"; + char wbuff[50]; + char rbuff[1024]; + char ubuff[50]; + float fdata; + int idata; + int ret; + + /* redirect I/Os */ + LPRINTF("Initializating I/Os redirection...\r\n"); + ret = rpmsg_rpc_init(&rpc, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + priv, platform_poll, rpmsg_rpc_shutdown); + rpmsg_set_default_rpc(&rpc); + if (ret) { + LPRINTF("Failed to intialize rpmsg rpc\r\n"); + return -1; + } + + printf("\nRemote>Baremetal Remote Procedure Call (RPC) Demonstration\r\n"); + printf("\nRemote>***************************************************\r\n"); + + printf("\nRemote>Rpmsg based retargetting to proxy initialized..\r\n"); + + /* Remote performing file IO on Master */ + printf("\nRemote>FileIO demo ..\r\n"); + + printf("\nRemote>Creating a file on master and writing to it..\r\n"); + fd = open(fname, REDEF_O_CREAT | REDEF_O_WRONLY | REDEF_O_APPEND, + S_IRUSR | S_IWUSR); + printf("\nRemote>Opened file '%s' with fd = %d\r\n", fname, fd); + + sprintf(wbuff, "This is a test string being written to file.."); + bytes_written = write(fd, wbuff, strlen(wbuff)); + printf("\nRemote>Wrote to fd = %d, size = %d, content = %s\r\n", fd, + bytes_written, wbuff); + close(fd); + printf("\nRemote>Closed fd = %d\r\n", fd); + + /* Remote performing file IO on Master */ + printf("\nRemote>Reading a file on master and displaying its contents..\r\n"); + fd = open(fname, REDEF_O_RDONLY, S_IRUSR | S_IWUSR); + printf("\nRemote>Opened file '%s' with fd = %d\r\n", fname, fd); + bytes_read = read(fd, rbuff, 1024); + *(char *)(&rbuff[0] + bytes_read + 1) = 0; + printf("\nRemote>Read from fd = %d, size = %d, printing contents below .. %s\r\n", + fd, bytes_read, rbuff); + close(fd); + printf("\nRemote>Closed fd = %d\r\n", fd); + + while (1) { + /* Remote performing STDIO on Master */ + printf("\nRemote>Remote firmware using scanf and printf ..\r\n"); + printf("\nRemote>Scanning user input from master..\r\n"); + printf("\nRemote>Enter name\r\n"); + ret = scanf("%s", ubuff); + if (ret) { + printf("\nRemote>Enter age\r\n"); + ret = scanf("%d", &idata); + if (ret) { + printf("\nRemote>Enter value for pi\r\n"); + ret = scanf("%f", &fdata); + if (ret) { + printf("\nRemote>User name = '%s'\r\n", ubuff); + printf("\nRemote>User age = '%d'\r\n", idata); + printf("\nRemote>User entered value of pi = '%f'\r\n", fdata); + } + } + } + if (!ret) { + scanf("%s", ubuff); + printf("Remote> Invalid value. Starting again...."); + } else { + printf("\nRemote>Repeat demo ? (enter yes or no) \r\n"); + scanf("%s", ubuff); + if ((strcmp(ubuff, "no")) && (strcmp(ubuff, "yes"))) { + printf("\nRemote>Invalid option. Starting again....\r\n"); + } else if ((!strcmp(ubuff, "no"))) { + printf("\nRemote>RPC retargetting quitting ...\r\n"); + break; + } + } + } + + printf("\nRemote> Firmware's rpmsg-rpc-channel going down! \r\n"); + rpccall.id = TERM_SYSCALL_ID; + (void)rpmsg_rpc_send(&rpc, &rpccall, sizeof(rpccall), NULL, 0); + + LPRINTF("Release remoteproc procedure call\r\n"); + rpmsg_rpc_release(&rpc); + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/rpc_demo/rpc_demod.c b/apps/examples/rpc_demo/rpc_demod.c new file mode 100644 index 0000000..004b533 --- /dev/null +++ b/apps/examples/rpc_demo/rpc_demod.c @@ -0,0 +1,372 @@ +/* This is a sample demonstration application that showcases usage of proxy from the remote core. + This application is meant to run on the remote CPU running baremetal. + This applicationr can print to to master console and perform file I/O using proxy mechanism. */ + +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-rpc-demo.h" + +#define RPC_BUFF_SIZE 496 +#define REDEF_O_CREAT 100 +#define REDEF_O_EXCL 200 +#define REDEF_O_RDONLY 0 +#define REDEF_O_WRONLY 1 +#define REDEF_O_RDWR 2 +#define REDEF_O_APPEND 2000 +#define REDEF_O_ACCMODE 3 + +#define raw_printf(format, ...) printf(format, ##__VA_ARGS__) +#define LPRINTF(format, ...) raw_printf("Master> " format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static void *platform; +static struct rpmsg_device *rpdev; +static struct rpmsg_endpoint app_ept; +static int request_termination = 0; +static int ept_deleted = 0; +static int err_cnt = 0; + +static void *copy_from_shbuf(void *dst, void *shbuf, int len) +{ + void *retdst = dst; + + while (len && ((uintptr_t)shbuf % sizeof(int))) { + *(uint8_t *)dst = *(uint8_t *)shbuf; + dst++; + shbuf++; + len--; + } + while (len >= (int)sizeof(int)) { + *(unsigned int *)dst = *(unsigned int *)shbuf; + dst += sizeof(int); + shbuf += sizeof(int); + len -= sizeof(int); + } + while (len > 0) { + *(uint8_t *)dst = *(uint8_t *)shbuf; + dst++; + shbuf++; + len--; + } + return retdst; +} + +static int handle_open(struct rpmsg_rpc_syscall *syscall, + struct rpmsg_endpoint *ept) +{ + char *buf; + struct rpmsg_rpc_syscall resp; + int fd, ret; + + if (!syscall || !ept) + return -EINVAL; + buf = (char *)syscall; + buf += sizeof(*syscall); + + /* Open remote fd */ + fd = open(buf, syscall->args.int_field1, syscall->args.int_field2); + + /* Construct rpc response */ + resp.id = OPEN_SYSCALL_ID; + resp.args.int_field1 = fd; + resp.args.int_field2 = 0; /*not used */ + resp.args.data_len = 0; /*not used */ + + /* Transmit rpc response */ + ret = rpmsg_send(ept, (void *)&resp, sizeof(resp)); + + return ret > 0 ? 0 : ret; +} + +static int handle_close(struct rpmsg_rpc_syscall *syscall, + struct rpmsg_endpoint *ept) +{ + struct rpmsg_rpc_syscall resp; + int ret; + + if (!syscall || !ept) + return -EINVAL; + /* Close remote fd */ + ret = close(syscall->args.int_field1); + + /* Construct rpc response */ + resp.id = CLOSE_SYSCALL_ID; + resp.args.int_field1 = ret; + resp.args.int_field2 = 0; /*not used */ + resp.args.data_len = 0; /*not used */ + + /* Transmit rpc response */ + ret = rpmsg_send(ept, &resp, sizeof(resp)); + + return ret > 0 ? 0 : ret; +} + +static int handle_read(struct rpmsg_rpc_syscall *syscall, + struct rpmsg_endpoint *ept) +{ + struct rpmsg_rpc_syscall *resp; + unsigned char buf[RPC_BUFF_SIZE]; + unsigned char *payload; + int bytes_read, payload_size; + int ret; + + if (!syscall || !ept) + return -EINVAL; + payload = buf + sizeof(*resp); + if (syscall->args.int_field1 == 0) { + bytes_read = sizeof(buf) - sizeof(*resp); + /* Perform read from fd for large size since this is a + STD/I request */ + bytes_read = read(syscall->args.int_field1, payload, + bytes_read); + } else { + /* Perform read from fd */ + bytes_read = read(syscall->args.int_field1, payload, + syscall->args.int_field2); + } + + /* Construct rpc response */ + resp = (struct rpmsg_rpc_syscall *)buf; + resp->id = READ_SYSCALL_ID; + resp->args.int_field1 = bytes_read; + resp->args.int_field2 = 0; /* not used */ + resp->args.data_len = bytes_read; + + payload_size = sizeof(*resp) + + ((bytes_read > 0) ? bytes_read : 0); + + /* Transmit rpc response */ + ret = rpmsg_send(ept, buf, payload_size); + + return ret > 0 ? 0 : ret; +} + +static int handle_write(struct rpmsg_rpc_syscall *syscall, + struct rpmsg_endpoint *ept) +{ + struct rpmsg_rpc_syscall resp; + unsigned char *buf; + int bytes_written; + int ret; + + if (!syscall || !ept) + return -EINVAL; + buf = (unsigned char *)syscall; + buf += sizeof(*syscall); + /* Write to remote fd */ + bytes_written = write(syscall->args.int_field1, buf, + syscall->args.int_field2); + + /* Construct rpc response */ + resp.id = WRITE_SYSCALL_ID; + resp.args.int_field1 = bytes_written; + resp.args.int_field2 = 0; /*not used */ + resp.args.data_len = 0; /*not used */ + + /* Transmit rpc response */ + ret = rpmsg_send(ept, (void *)&resp, sizeof(resp)); + + return ret > 0 ? 0 : ret; +} + +static int handle_rpc(struct rpmsg_rpc_syscall *syscall, + struct rpmsg_endpoint *ept) +{ + int retval; + + /* Handle RPC */ + switch (syscall->id) { + case OPEN_SYSCALL_ID: + { + retval = handle_open(syscall, ept); + break; + } + case CLOSE_SYSCALL_ID: + { + retval = handle_close(syscall, ept); + break; + } + case READ_SYSCALL_ID: + { + retval = handle_read(syscall, ept); + break; + } + case WRITE_SYSCALL_ID: + { + retval = handle_write(syscall, ept); + break; + } + case TERM_SYSCALL_ID: + { + LPRINTF("Received termination request\r\n"); + request_termination = 1; + retval = 0; + break; + } + default: + { + LPERROR + ("Invalid RPC sys call ID: %d:%d!\r\n", + (int)syscall->id, (int)WRITE_SYSCALL_ID); + retval = -1; + break; + } + } + + return retval; +} +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&app_ept); + LPRINTF("Endpoint is destroyed\r\n"); + ept_deleted = 1; +} + +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + unsigned char buf[RPC_BUFF_SIZE]; + struct rpmsg_rpc_syscall *syscall; + + (void)priv; + (void)src; + + if (len < (int)sizeof(*syscall)) { + LPERROR("Received data is less than the rpc structure: %d\r\n", + len); + err_cnt++; + return RPMSG_SUCCESS; + } + + /* In case the shared memory is device memory + * E.g. For now, we only use UIO device memory in Linux. + */ + if (len > RPC_BUFF_SIZE) + len = RPC_BUFF_SIZE; + copy_from_shbuf(buf, data, len); + syscall = (struct rpmsg_rpc_syscall *)buf; + if (handle_rpc(syscall, ept)) { + LPRINTF("\nHandling remote procedure call errors:\r\n"); + raw_printf("rpc id %d\r\n", syscall->id); + raw_printf("rpc int field1 %d\r\n", + syscall->args.int_field1); + raw_printf("\nrpc int field2 %d\r\n", + syscall->args.int_field2); + err_cnt++; + } + return RPMSG_SUCCESS; +} + + +void terminate_rpc_app() +{ + LPRINTF("Destroying endpoint.\r\n"); + if (!ept_deleted) + rpmsg_destroy_ept(&app_ept); +} + +void exit_action_handler(int signum) +{ + (void)signum; + terminate_rpc_app(); +} + +void kill_action_handler(int signum) +{ + (void)signum; + LPRINTF("RPC service killed !!\r\n"); + + terminate_rpc_app(); + + if (rpdev) + platform_release_rpmsg_vdev(rpdev); + if (platform) + platform_cleanup(platform); +} + +/* Application entry point */ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret = 0; + struct sigaction exit_action; + struct sigaction kill_action; + + /* Initialize signalling infrastructure */ + memset(&exit_action, 0, sizeof(struct sigaction)); + memset(&kill_action, 0, sizeof(struct sigaction)); + exit_action.sa_handler = exit_action_handler; + kill_action.sa_handler = kill_action_handler; + sigaction(SIGTERM, &exit_action, NULL); + sigaction(SIGINT, &exit_action, NULL); + sigaction(SIGKILL, &kill_action, NULL); + sigaction(SIGHUP, &kill_action, NULL); + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&app_ept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -EINVAL; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while(1) { + platform_poll(priv); + if (err_cnt) { + LPERROR("Got error!\r\n"); + ret = -EINVAL; + break; + } + /* we got a shutdown request, exit */ + if (ept_deleted || request_termination) { + break; + } + } + LPRINTF("\nRPC service exiting !!\r\n"); + + terminate_rpc_app(); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/examples/rpc_demo/rpmsg-rpc-demo.h b/apps/examples/rpc_demo/rpmsg-rpc-demo.h new file mode 100644 index 0000000..8a26272 --- /dev/null +++ b/apps/examples/rpc_demo/rpmsg-rpc-demo.h @@ -0,0 +1,6 @@ +#ifndef RPMSG_RPC_DEMO_H +#define RPMSG_RPC_DEMO_H + +#define RPMSG_SERVICE_NAME "rpmsg-openamp-demo-channel" + +#endif /* RPMSG_RPC_DEMO_H */ diff --git a/apps/examples/rpmsg_sample_echo/CMakeLists.txt b/apps/examples/rpmsg_sample_echo/CMakeLists.txt new file mode 100644 index 0000000..eda1111 --- /dev/null +++ b/apps/examples/rpmsg_sample_echo/CMakeLists.txt @@ -0,0 +1,42 @@ + +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS} -fdata-sections -ffunction-sections") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +foreach (_app rpmsg-sample-echo rpmsg-sample-ping) + collector_list (_sources APP_COMMON_SOURCES) + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/${_app}.c") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) diff --git a/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c new file mode 100644 index 0000000..c27e39f --- /dev/null +++ b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c @@ -0,0 +1,153 @@ +/* + * This is a sample demonstration application that showcases usage of rpmsg + * This application is meant to run on the remote CPU running baremetal code. + * This application allows to check the compatibility with linux OS running on + * the master CPU. For this it echo MSG_LIMIT time message sent by the rpmsg + * sample client available in linux kernel distribution. + */ + +#include +#include +#include +#include +#include "platform_info.h" + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + + +#define RPMSG_SERV_NAME "rpmsg-client-sample" +#define MSG_LIMIT 100 + +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + (void)priv; + (void)src; + static uint32_t count = 0; + char payload[RPMSG_BUFFER_SIZE]; + + /* Send data back MSG_LIMIT time to master */ + memset(payload, 0, RPMSG_BUFFER_SIZE); + memcpy(payload, data, len); + if (++count <= MSG_LIMIT) { + LPRINTF("echo message number %u: %s\r\n", + (unsigned int)count, payload); + if (rpmsg_send(ept, (char *)data, len) < 0) { + LPERROR("rpmsg_send failed\r\n"); + goto destroy_ept; + } + + if (count == MSG_LIMIT) { + LPRINTF("reach message limit, exit\r\n"); + goto destroy_ept; + } + } + return RPMSG_SUCCESS; + +destroy_ept: + shutdown_req = 1; + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPRINTF("unexpected Remote endpoint destroy\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERV_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while (1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int rpmsg_sample_echo(int argc, char *argv[]); + +int rpmsg_echo() +{ + rpmsg_sample_echo(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(rpmsg_echo, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(rpmsg_echo, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int rpmsg_sample_echo(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); +#ifdef METAL_SYSTEM_RTTHREAD + //Make rt-thread happy + shutdown_req = 0; + memset(&lept, 0, sizeof(lept)); +#endif /* #ifdef FINSH_USING_MSH */ + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev,0); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c.orig b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c.orig new file mode 100644 index 0000000..9ddeef0 --- /dev/null +++ b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.c.orig @@ -0,0 +1,147 @@ +/* + * This is a sample demonstration application that showcases usage of rpmsg + * This application is meant to run on the remote CPU running baremetal code. + * This application allows to check the compatibility with linux OS running on + * the master CPU. For this it echo MSG_LIMIT time message sent by the rpmsg + * sample client available in linux kernel distribution. + */ + +#include +#include +#include +#include +#include "platform_info.h" + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + + +#define RPMSG_SERV_NAME "rpmsg-client-sample" +#define MSG_LIMIT 100 + +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + (void)priv; + (void)src; + static uint32_t count = 0; + char payload[RPMSG_BUFFER_SIZE]; + + /* Send data back MSG_LIMIT time to master */ + memset(payload, 0, RPMSG_BUFFER_SIZE); + memcpy(payload, data, len); + if (++count <= MSG_LIMIT) { + LPRINTF("echo message number %u: %s\r\n", + (unsigned int)count, payload); + if (rpmsg_send(ept, (char *)data, len) < 0) { + LPERROR("rpmsg_send failed\r\n"); + goto destroy_ept; + } + + if (count == MSG_LIMIT) { + goto destroy_ept; + } + } + return RPMSG_SUCCESS; + +destroy_ept: + shutdown_req = 1; + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPRINTF("unexpected Remote endpoint destroy\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERV_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while (1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int rpmsg_sample_echo(int argc, char *argv[]); + +int rpmsg_echo() +{ + rpmsg_sample_echo(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(rpmsg_echo, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(rpmsg_echo, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int rpmsg_sample_echo(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.o b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.o new file mode 100644 index 0000000..746c2d2 Binary files /dev/null and b/apps/examples/rpmsg_sample_echo/rpmsg-sample-echo.o differ diff --git a/apps/examples/rpmsg_sample_echo/rpmsg-sample-ping.c b/apps/examples/rpmsg_sample_echo/rpmsg-sample-ping.c new file mode 100644 index 0000000..03f1893 --- /dev/null +++ b/apps/examples/rpmsg_sample_echo/rpmsg-sample-ping.c @@ -0,0 +1,179 @@ +/* + * This is a sample demonstration application that showcases usage of rpmsg + * This application is meant to run on the remote CPU running baremetal code. + * This application simulate linux sample rpmsg driver. For this it echo 100 + * time message sent by the rpmsg sample client available in linux kernel + * distribution. + */ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" + +#define RPMSG_SERV_NAME "rpmsg-client-sample" + +#define HELLO_MSG "hello world!" +#define BYE_MSG "goodbye!" +#define MSG_LIMIT 100 + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static int err_cnt; + + +/* Globals */ +static struct rpmsg_endpoint lept; +static int rnum = 0; +static int err_cnt = 0; +static int ept_deleted = 0; + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + char payload[RPMSG_BUFFER_SIZE]; + char seed[20]; + + (void)ept; + (void)src; + (void)priv; + + memset(payload, 0, RPMSG_BUFFER_SIZE); + memcpy(payload, data, len); + LPRINTF("received message %d: %s of size %lu \r\n", + rnum + 1, payload, (unsigned long)len); + + if (rnum == (MSG_LIMIT - 1)) + sprintf (seed, "%s", BYE_MSG); + else + sprintf (seed, "%s", HELLO_MSG); + + LPRINTF(" seed %s: \r\n", seed); + + if (strncmp(payload, seed, len)) { + LPERROR(" Invalid message is received.\r\n"); + err_cnt++; + return RPMSG_SUCCESS; + } + LPRINTF(" rnum %d: \r\n", rnum); + rnum++; + + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&lept); + LPRINTF("echo test: service is destroyed\r\n"); + ept_deleted = 1; +} + +static void rpmsg_name_service_bind_cb(struct rpmsg_device *rdev, + const char *name, uint32_t dest) +{ + LPRINTF("new endpoint notification is received.\r\n"); + if (strcmp(name, RPMSG_SERV_NAME)) + LPERROR("Unexpected name service %s.\r\n", name); + else + (void)rpmsg_create_ept(&lept, rdev, RPMSG_SERV_NAME, + RPMSG_ADDR_ANY, dest, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + int i; + + LPRINTF(" 1 - Send data to remote core, retrieve the echo"); + LPRINTF(" and validate its integrity ..\r\n"); + + /* Create RPMsg endpoint */ + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERV_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + + if (ret) { + LPERROR("Failed to create RPMsg endpoint.\r\n"); + return ret; + } + + while (!is_rpmsg_ept_ready(&lept)) + platform_poll(priv); + + LPRINTF("RPMSG endpoint is binded with remote.\r\n"); + for (i = 1; i <= MSG_LIMIT; i++) { + + + if (i < MSG_LIMIT) + ret = rpmsg_send(&lept, HELLO_MSG, strlen(HELLO_MSG)); + else + ret = rpmsg_send(&lept, BYE_MSG, strlen(BYE_MSG)); + + if (ret < 0) { + LPERROR("Failed to send data...\r\n"); + break; + } + LPRINTF("rpmsg sample test: message %d sent\r\n", i); + + do { + platform_poll(priv); + } while ((rnum < i) && !err_cnt); + + } + + LPRINTF("**********************************\r\n"); + LPRINTF(" Test Results: Error count = %d\r\n", err_cnt); + LPRINTF("**********************************\r\n"); + while (!ept_deleted) + platform_poll(priv); + LPRINTF("Quitting application .. rpmsg sample test end\r\n"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, + rpmsg_name_service_bind_cb); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/machine/CMakeLists.txt b/apps/machine/CMakeLists.txt new file mode 100644 index 0000000..9720224 --- /dev/null +++ b/apps/machine/CMakeLists.txt @@ -0,0 +1,3 @@ +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") + add_subdirectory (${PROJECT_MACHINE}) +endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") diff --git a/apps/machine/zynq7/CMakeLists.txt b/apps/machine/zynq7/CMakeLists.txt new file mode 100644 index 0000000..7fb18fc --- /dev/null +++ b/apps/machine/zynq7/CMakeLists.txt @@ -0,0 +1,6 @@ +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") + +collect (APP_COMMON_SOURCES platform_info.c) +collect (APP_COMMON_SOURCES rsc_table.c) +collect (APP_COMMON_SOURCES zynq_a9_rproc.c) + diff --git a/apps/machine/zynq7/platform_info.c b/apps/machine/zynq7/platform_info.c new file mode 100644 index 0000000..d753e0e --- /dev/null +++ b/apps/machine/zynq7/platform_info.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation. All rights reserved. + * Copyright (c) 2017 - 2018 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define platform specific data and implements APIs to set + * platform specific information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rsc_table.h" +#include + +/* Another APU core ID. In this demo, the other APU core is 0. */ +#define A9_CPU_ID 0UL + +/* scugic device, used to raise soft irq */ +#define SCUGIC_DEV_NAME "scugic_dev" +#define SCUGIC_BUS_NAME "generic" + +/* scugic base address */ +#define SCUGIC_PERIPH_BASE 0xF8F00000 +#define SCUGIC_DIST_BASE (SCUGIC_PERIPH_BASE + 0x00001000) + +#define _rproc_wait() asm volatile("wfi") + +/* processor operations for hil_proc for A9. It defines + * notification operation and remote processor management. */ +extern struct remoteproc_ops zynq_a9_proc_ops; +static metal_phys_addr_t scugic_phys_addr = SCUGIC_DIST_BASE; +struct metal_device scugic_device = { + .name = SCUGIC_DEV_NAME, + .bus = NULL, + .num_regions = 1, + .regions = { + { + .virt = (void *)SCUGIC_DIST_BASE, + .physmap = &scugic_phys_addr, + .size = 0x1000, + .page_shift = -1UL, + .page_mask = -1UL, + .mem_flags = DEVICE_MEMORY, + .ops = {NULL}, + }, + }, + .node = {NULL}, + .irq_num = 0, + .irq_info = NULL, +}; + +/* Remoteproc private data */ +static struct remoteproc_priv rproc_priv = { + .gic_name = SCUGIC_DEV_NAME, + .gic_bus_name = SCUGIC_BUS_NAME, + .gic_dev = NULL, + .gic_io = NULL, + .irq_to_notify = SGI_TO_NOTIFY, + .irq_notification = SGI_NOTIFICATION, + /* toggle CPU ID, there is only two CPUs in PS */ + .cpu_id = (~XPAR_CPU_ID) & ZYNQ_CPU_ID_MASK, +}; + +/* Remoteproc instance */ +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void) proc_index; + rsc_table = get_resource_table(rsc_index, &rsc_size); + + /* Register IPI device */ + (void)metal_register_generic_device(&scugic_device); + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &zynq_a9_proc_ops, &rproc_priv)) + return NULL; + + /* + * Mmap shared memories + * Or shall we constraint that they will be set as carved out + * in the resource table? + */ + /* mmap resource table */ + pa = (metal_phys_addr_t)rsc_table; + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, rsc_size, + NORM_NONCACHE | STRONG_ORDERED, + &rproc_inst.rsc_io); + /* mmap shared memory */ + pa = SHARED_MEM_PA; + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, SHARED_MEM_SIZE, + NORM_NONCACHE | STRONG_ORDERED, + NULL); + + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table, rsc_size); + if (ret) { + xil_printf("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + xil_printf("Initialize remoteproc successfully.\r\n"); + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + xil_printf("Failed to initialize platform," + "NULL pointer to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + xil_printf("Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_MEM_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, + SHARED_MEM_PA + SHARED_BUF_OFFSET); + + xil_printf("creating remoteproc virtio\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + xil_printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + + xil_printf("initializing rpmsg vdev\r\n"); + if (role == VIRTIO_DEV_MASTER) { + /* Only RPMsg virtio master needs to initialize the + * shared buffers pool + */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, + (SHARED_MEM_SIZE - + SHARED_BUF_OFFSET)); + + /* RPMsg virtio slave can set shared buffers pool + * argument to NULL + */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, &shpool); + } else { + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, NULL); + } + if (ret) { + xil_printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + xil_printf("initializing rpmsg vdev\r\n"); + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + + prproc = rproc->priv; + while(1) { + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->nokick))) { + metal_irq_restore_enable(flags); + remoteproc_get_notification(rproc, RSC_NOTIFY_ID_ANY); + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev) +{ + (void)rpdev; +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/machine/zynq7/platform_info.h b/apps/machine/zynq7/platform_info.h new file mode 100644 index 0000000..764a76e --- /dev/null +++ b/apps/machine/zynq7/platform_info.h @@ -0,0 +1,107 @@ +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* SGIs */ +#define SGI_TO_NOTIFY 15 /* SGI to notify the remote */ +#define SGI_NOTIFICATION 14 /* SGI from the remote */ + +/* Memory attributes */ +#define NORM_NONCACHE 0x11DE2 /* Normal Non-cacheable */ +#define STRONG_ORDERED 0xC02 /* Strongly ordered */ +#define DEVICE_MEMORY 0xC06 /* Device memory */ +#define RESERVED 0x0 /* reserved memory */ + +/* Shared memory */ +#define SHARED_MEM_PA 0x3e800000UL +#define SHARED_MEM_SIZE 0x80000UL +#define SHARED_BUF_OFFSET 0x80000UL + +/* Zynq CPU ID mask */ +#define ZYNQ_CPU_ID_MASK 0x1UL + +/* Remoteproc private data struct */ +struct remoteproc_priv { + const char *gic_name; /* SCUGIC device name */ + const char *gic_bus_name; /* SCUGIC bus name */ + struct metal_device *gic_dev; /* pointer to SCUGIC device */ + struct metal_io_region *gic_io; /* pointer to SCUGIC i/o region */ + unsigned int irq_to_notify; /* SCUGIC IRQ vector to notify the + * other end. + */ + unsigned int irq_notification; /* SCUGIC IRQ vector received from + * other end. + */ + unsigned int cpu_id; /* CPU ID */ + atomic_int nokick; /* 0 for kick from other side */ +}; + +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H_ */ diff --git a/apps/machine/zynq7/platform_info_remoteproc_master.c b/apps/machine/zynq7/platform_info_remoteproc_master.c new file mode 100644 index 0000000..510ab2a --- /dev/null +++ b/apps/machine/zynq7/platform_info_remoteproc_master.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file implements APIs to get platform specific + * information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include + +/* Reference implementation that show cases platform_get_cpu_info and + platform_get_for_firmware API implementation for Bare metal environment */ + +extern struct hil_platform_ops zynq_a9_proc_ops; + +/* IPC Device parameters */ +#define SHM_ADDR (void *)0x08008000 +#define SHM_SIZE 0x00200000 +#define VRING0_IPI_VECT 6 +#define VRING1_IPI_VECT 3 +#define MASTER_CPU_ID 0 +#define REMOTE_CPU_ID 1 + +/** + * This array provdes defnition of CPU nodes for master and remote + * context. It contains two nodes beacuse the same file is intended + * to use with both master and remote configurations. On zynq platform + * only one node defintion is required for master/remote as there + * are only two cores present in the platform. + * + * Only platform specific info is populated here. Rest of information + * is obtained during resource table parsing.The platform specific + * information includes; + * + * -CPU ID + * -Shared Memory + * -Interrupts + * -Channel info. + * + * Although the channel info is not platform specific information + * but it is conveneient to keep it in HIL so that user can easily + * provide it without modifying the generic part. + * + * It is good idea to define hil_proc structure with platform + * specific fields populated as this can be easily copied to hil_proc + * structure passed as parameter in platform_get_processor_info. The + * other option is to populate the required structures individually + * and copy them one by one to hil_proc structure in platform_get_processor_info + * function. The first option is adopted here. + * + * + * 1) First node in the array is intended for the remote contexts and it + * defines Master CPU ID, shared memory, interrupts info, number of channels + * and there names. This node defines only one channel + * "rpmsg-openamp-demo-channel". + * + * 2)Second node is required by the master and it defines remote CPU ID, + * shared memory and interrupts info. In general no channel info is required by the + * Master node, however in baremetal master and linux remote case the linux + * rpmsg bus driver behaves as master so the rpmsg driver on linux side still needs + * channel info. This information is not required by the masters for baremetal + * remotes. + * + */ +struct hil_proc proc_table []= +{ + { + /* CPU ID of remote */ + REMOTE_CPU_ID, + + /* Shared memory info - Last field is not used currently */ + { + SHM_ADDR, SHM_SIZE, 0x00 + }, + + /* VirtIO device info */ + { + 0, 0, 0, + { + { + /* Provide vring interrupts info here. Other fields are obtained + * from the rsc table so leave them empty. + */ + NULL, NULL, 0, 0, + { + VRING0_IPI_VECT,0x1006,1 + } + }, + { + NULL, NULL, 0, 0, + { + VRING1_IPI_VECT,0x1006,1 + } + } + } + }, + + /* Number of RPMSG channels */ + 1, + + /* RPMSG channel info - Only channel name is expected currently */ + { + {"rpmsg-openamp-demo-channel"} + }, + + /* HIL platform ops table. */ + &zynq_a9_proc_ops, + + /* Next three fields are for future use only */ + 0, + 0, + NULL + } +}; + +/* 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 + +/* 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} +}; + +int fw_table_size = sizeof(fw_table)/sizeof(struct firmware_info); diff --git a/apps/machine/zynq7/rsc_table.c b/apps/machine/zynq7/rsc_table.c new file mode 100644 index 0000000..605f86d --- /dev/null +++ b/apps/machine/zynq7/rsc_table.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#include +#include "rsc_table.h" +#include "platform_info.h" + +/* Place resource table in special ELF section */ +/* Redefine __section for section name with token */ +#define __section_t(S) __attribute__((__section__(#S))) +#define __resource __section_t(.resource_table) + +#define RPMSG_IPU_C0_FEATURES 1 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Remote supports Name Service announcement */ +#define VIRTIO_RPMSG_F_NS 0 + +/* Resource table entries */ +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define RING_TX FW_RSC_U32_ADDR_ANY +#define RING_RX FW_RSC_U32_ADDR_ANY +#define VRING_SIZE 256 + +#define NUM_TABLE_ENTRIES 1 + + +struct remote_resource_table __resource resources = { + /* Version */ + 1, + + /* NUmber of table entries */ + NUM_TABLE_ENTRIES, + /* reserved fields */ + {0, 0,}, + + /* Offsets of rsc entries */ + { + offsetof(struct remote_resource_table, rpmsg_vdev), + }, + + /* Virtio device entry */ + { + RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +void *get_resource_table (int rsc_id, int *len) +{ + (void) rsc_id; + *len = sizeof(resources); + return &resources; +} diff --git a/apps/machine/zynq7/rsc_table.h b/apps/machine/zynq7/rsc_table.h new file mode 100644 index 0000000..25c4d0e --- /dev/null +++ b/apps/machine/zynq7/rsc_table.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef RSC_TABLE_H_ +#define RSC_TABLE_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define NO_RESOURCE_ENTRIES 8 + +/* Resource table for the given remote */ +struct remote_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}__attribute__((packed, aligned(0x100000))); + +void *get_resource_table (int rsc_id, int *len); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_H_ */ diff --git a/apps/machine/zynq7/zynq_a9_rproc.c b/apps/machine/zynq7/zynq_a9_rproc.c new file mode 100644 index 0000000..f830749 --- /dev/null +++ b/apps/machine/zynq7/zynq_a9_rproc.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * zynq_a9_rproc.c + * + * DESCRIPTION + * + * This file define Xilinx Zynq A9 platform specific remoteproc + * implementation. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/* SCUGIC macros */ +#define GIC_DIST_SOFTINT 0xF00 +#define GIC_SFI_TRIG_CPU_MASK 0x00FF0000 +#define GIC_SFI_TRIG_SATT_MASK 0x00008000 +#define GIC_SFI_TRIG_INTID_MASK 0x0000000F +#define GIC_CPU_ID_BASE (1 << 4) + +static int zynq_a9_proc_irq_handler(int vect_id, void *data) +{ + struct remoteproc *rproc = data; + struct remoteproc_priv *prproc; + + (void)vect_id; + if (!rproc) + return METAL_IRQ_NOT_HANDLED; + prproc = rproc->priv; + atomic_flag_clear(&prproc->nokick); + return METAL_IRQ_HANDLED; +} + +static struct remoteproc * +zynq_a9_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *dev; + unsigned int irq_vect; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + ret = metal_device_open(prproc->gic_bus_name, prproc->gic_name, + &dev); + if (ret) { + xil_printf("failed to open GIC device: %d.\r\n", ret); + return NULL; + } + rproc->priv = prproc; + prproc->gic_dev = dev; + prproc->gic_io = metal_device_io_region(dev, 0); + if (!prproc->gic_io) + goto err1; + rproc->ops = ops; + atomic_flag_test_and_set(&prproc->nokick); + + /* Register interrupt handler and enable interrupt */ + irq_vect = prproc->irq_notification; + metal_irq_register(irq_vect, zynq_a9_proc_irq_handler, rproc); + metal_irq_enable(irq_vect); + xil_printf("Successfully intialize remoteproc.\r\n"); + return rproc; +err1: + metal_device_close(dev); + return NULL; +} + +static void zynq_a9_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct metal_device *dev; + + if (!rproc) + return; + prproc = rproc->priv; + metal_irq_disable(prproc->irq_to_notify); + metal_irq_unregister(prproc->irq_to_notify); + dev = prproc->gic_dev; + if (dev) + metal_device_close(dev); +} + +static void * +zynq_a9_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + + if (!attribute) + attribute = NORM_NONCACHE | STRONG_ORDERED; + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + tmpio = metal_allocate_memory(sizeof(*tmpio)); + if (!tmpio) { + metal_free_memory(mem); + return NULL; + } + remoteproc_init_mem(mem, NULL, lpa, lda, size, tmpio); + /* va is the same as pa in this platform */ + metal_io_init(tmpio, (void *)lpa, &mem->pa, size, + sizeof(metal_phys_addr_t)<<3, attribute, NULL); + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, mem->pa); +} + +static int zynq_a9_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + unsigned long mask = 0; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + if (!prproc->gic_io) + return -1; + + mask = ((1 << (GIC_CPU_ID_BASE + prproc->cpu_id)) | + (prproc->irq_to_notify)) + & (GIC_SFI_TRIG_CPU_MASK | GIC_SFI_TRIG_INTID_MASK); + /* Trigger IPI */ + metal_io_write32(prproc->gic_io, GIC_DIST_SOFTINT, mask); + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +struct remoteproc_ops zynq_a9_proc_ops = { + .init = zynq_a9_proc_init, + .remove = zynq_a9_proc_remove, + .mmap = zynq_a9_proc_mmap, + .notify = zynq_a9_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; diff --git a/apps/machine/zynqmp/CMakeLists.txt b/apps/machine/zynqmp/CMakeLists.txt new file mode 100644 index 0000000..f948380 --- /dev/null +++ b/apps/machine/zynqmp/CMakeLists.txt @@ -0,0 +1,3 @@ +collect (APP_COMMON_SOURCES platform_info.c) +collect (APP_COMMON_SOURCES zynqmp_linux_r5_proc.c) +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/apps/machine/zynqmp/openamp-linux-userspace.dtsi b/apps/machine/zynqmp/openamp-linux-userspace.dtsi new file mode 100644 index 0000000..060cba1 --- /dev/null +++ b/apps/machine/zynqmp/openamp-linux-userspace.dtsi @@ -0,0 +1,29 @@ +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + rproc_0_reserved: rproc@3ed000000 { + no-map; + reg = <0x0 0x3ed00000 0x0 0x1000000>; + }; + }; + + amba { + vring: vring@0 { + compatible = "vring_uio"; + reg = <0x0 0x3ed40000 0x0 0x40000>; + }; + shm0: shm@0 { + compatible = "shm_uio"; + reg = <0x0 0x3ed20000 0x0 0x0100000>; + }; + ipi0: ipi@0 { + compatible = "ipi_uio"; + reg = <0x0 0xff340000 0x0 0x1000>; + interrupt-parent = <&gic>; + interrupts = <0 29 4>; + }; + }; +}; + diff --git a/apps/machine/zynqmp/platform_info.c b/apps/machine/zynqmp/platform_info.c new file mode 100644 index 0000000..e882b8e --- /dev/null +++ b/apps/machine/zynqmp/platform_info.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define platform specific data and implements APIs to set + * platform specific information for OpenAMP. + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" + +#define RPU_CPU_ID 0 /* RPU remote CPU Index. We only talk to + * one CPU in the exmaple. We set the CPU + * index to 0. */ +#ifdef versal +#define IPI_CHN_BITMASK 0x08 /* IPI channel bit mask for IPI + * from/to RPU0 */ +#define IPI_DEV_NAME "ff360000.ipi" /* IPI device name */ +#else +#define IPI_CHN_BITMASK 0x00000100 +#define IPI_DEV_NAME "ff340000.ipi" +#endif /* versal */ +#define DEV_BUS_NAME "platform" /* device bus name. "platform" bus + * is used in Linux kernel for generic + * devices */ +/* libmetal devices names used in the examples. + * They are platform devices, you find them in Linux sysfs + * sys/bus/platform/devices */ +#define SHM_DEV_NAME "3ed20000.shm" /* shared device name */ + +#define RSC_MEM_PA 0x3ED20000UL +#define RSC_MEM_SIZE 0x2000UL +#define VRING_MEM_PA 0x3ED40000UL +#define VRING_MEM_SIZE 0x8000UL +#define SHARED_BUF_PA 0x3ED48000UL +#define SHARED_BUF_SIZE 0x40000UL + +#ifdef RPMSG_NO_IPI +#define POLL_DEV_NAME "3ee40000.shm" /* shared device name */ +#define POLL_STOP 0x1U +#endif /* RPMSG_NO_IPI */ + +struct remoteproc_priv rproc_priv = { + .shm_name = SHM_DEV_NAME, + .shm_bus_name = DEV_BUS_NAME, +#ifndef RPMSG_NO_IPI + .ipi_name = IPI_DEV_NAME, + .ipi_bus_name = DEV_BUS_NAME, + .ipi_chn_mask = IPI_CHN_BITMASK, +#endif /* !RPMSG_NO_IPI */ +#ifdef RPMSG_NO_IPI + .shm_poll_name = POLL_DEV_NAME, + .shm_poll_bus_name = DEV_BUS_NAME, +#endif /* RPMSG_NO_IPI */ +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +#define _rproc_wait() metal_cpu_yield() + + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +extern struct remoteproc_ops zynqmp_linux_r5_proc_ops; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void)proc_index; + (void)rsc_index; + rsc_size = RSC_MEM_SIZE; + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &zynqmp_linux_r5_proc_ops, + &rproc_priv)) + return NULL; + printf("Successfully initialized remoteproc\r\n"); + + /* Mmap resource table */ + pa = RSC_MEM_PA; + printf("Calling mmap resource table.\r\n"); + rsc_table = remoteproc_mmap(&rproc_inst, &pa, NULL, rsc_size, + 0, NULL); + if (!rsc_table) { + fprintf(stderr, "ERROR: Failed to mmap resource table.\r\n"); + return NULL; + } + printf("Successfully mmap resource table.\r\n"); + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table, rsc_size); + if (ret) { + printf("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + printf("Successfully set resource table to remoteproc.\r\n"); + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + fprintf(stderr, "Failed to initialize platform, NULL pointer" + "to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + fprintf(stderr, "Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_BUF_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, + SHARED_BUF_PA); + + printf("Creating virtio...\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + printf("Successfully created virtio device.\r\n"); + + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUF_SIZE); + + printf("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, &shpool); + if (ret) { + printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + int ret; + + prproc = rproc->priv; + while(1) { +#ifdef RPMSG_NO_IPI + if (metal_io_read32(prproc->shm_poll_io, 0)) { + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } +#else + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->ipi_nokick))) { + metal_irq_restore_enable(flags); + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); +#endif /* RPMSG_NO_IPI */ + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev) +{ + (void)rpdev; +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/machine/zynqmp/platform_info.h b/apps/machine/zynqmp/platform_info.h new file mode 100644 index 0000000..9ed373c --- /dev/null +++ b/apps/machine/zynqmp/platform_info.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +struct remoteproc_priv { + const char *ipi_name; /**< IPI device name */ + const char *ipi_bus_name; /**< IPI bus name */ + const char *rsc_name; /**< rsc device name */ + const char *rsc_bus_name; /**< rsc bus name */ + const char *shm_name; /**< shared memory device name */ + const char *shm_bus_name; /**< shared memory bus name */ + struct metal_device *ipi_dev; /**< pointer to IPI device */ + struct metal_io_region *ipi_io; /**< pointer to IPI i/o region */ + struct metal_device *shm_dev; /**< pointer to shared memory device */ + struct metal_io_region *shm_io; /**< pointer to sh mem i/o region */ + + struct remoteproc_mem shm_mem; /**< shared memory */ + unsigned int ipi_chn_mask; /**< IPI channel mask */ + atomic_int ipi_nokick; +#ifdef RPMSG_NO_IPI + const char *shm_poll_name; /**< shared memory device name */ + const char *shm_poll_bus_name; /**< shared memory bus name */ + struct metal_device *shm_poll_dev; /**< pointer to poll mem device */ + struct metal_io_region *shm_poll_io; /**< pointer to poll mem i/o */ +#endif /* RPMSG_NO_IPI */ + +}; + + +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H_ */ + diff --git a/apps/machine/zynqmp/zynqmp_linux_r5_proc.c b/apps/machine/zynqmp/zynqmp_linux_r5_proc.c new file mode 100644 index 0000000..d24198a --- /dev/null +++ b/apps/machine/zynqmp/zynqmp_linux_r5_proc.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define Xilinx ZynqMP R5 to A53 platform specific + * remoteproc implementation. + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.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 */ + +static int zynqmp_linux_r5_proc_irq_handler(int vect_id, void *data) +{ + struct remoteproc *rproc = data; + struct remoteproc_priv *prproc; + unsigned int ipi_intr_status; + + (void)vect_id; + if (!rproc) + return METAL_IRQ_NOT_HANDLED; + prproc = rproc->priv; + ipi_intr_status = (unsigned int)metal_io_read32(prproc->ipi_io, + IPI_ISR_OFFSET); + if (ipi_intr_status & prproc->ipi_chn_mask) { + atomic_flag_clear(&prproc->ipi_nokick); + metal_io_write32(prproc->ipi_io, IPI_ISR_OFFSET, + prproc->ipi_chn_mask); + return METAL_IRQ_HANDLED; + } + return METAL_IRQ_NOT_HANDLED; +} + +static struct remoteproc * +zynqmp_linux_r5_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *dev; + unsigned int irq_vect; + metal_phys_addr_t mem_pa; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + rproc->priv = prproc; + rproc->ops = ops; + prproc->ipi_dev = NULL; + prproc->shm_dev = NULL; + /* Get shared memory device */ + ret = metal_device_open(prproc->shm_bus_name, prproc->shm_name, + &dev); + if (ret) { + fprintf(stderr, "ERROR: failed to open shm device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm device.\r\n"); + prproc->shm_dev = dev; + prproc->shm_io = metal_device_io_region(dev, 0); + if (!prproc->shm_io) + goto err2; + +#ifdef RPMSG_NO_IPI + /* Get poll shared memory device */ + ret = metal_device_open(prproc->shm_poll_bus_name, + prproc->shm_poll_name, + &dev); + if (ret) { + fprintf(stderr, + "ERROR: failed to open shm poll device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm poll device.\r\n"); + prproc->shm_poll_dev = dev; + prproc->shm_poll_io = metal_device_io_region(dev, 0); + if (!prproc->shm_poll_io) + goto err2; + metal_io_write32(prproc->shm_poll_io, 0, !POLL_STOP); +#endif /* RPMSG_NO_IPI */ + + mem_pa = metal_io_phys(prproc->shm_io, 0); + remoteproc_init_mem(&prproc->shm_mem, "shm", mem_pa, mem_pa, + metal_io_region_size(prproc->shm_io), + prproc->shm_io); + remoteproc_add_mem(rproc, &prproc->shm_mem); + printf("Successfully added shared memory\r\n"); + /* Get IPI device */ + ret = metal_device_open(prproc->ipi_bus_name, prproc->ipi_name, + &dev); + if (ret) { + printf("failed to open ipi device: %d.\r\n", ret); + goto err2; + } + prproc->ipi_dev = dev; + prproc->ipi_io = metal_device_io_region(dev, 0); + if (!prproc->ipi_io) + goto err3; + printf("Successfully probed IPI device\r\n"); + atomic_store(&prproc->ipi_nokick, 1); + + /* Register interrupt handler and enable interrupt */ + irq_vect = (uintptr_t)dev->irq_info; + metal_irq_register(irq_vect, zynqmp_linux_r5_proc_irq_handler, rproc); + metal_irq_enable(irq_vect); + metal_io_write32(prproc->ipi_io, IPI_IER_OFFSET, + prproc->ipi_chn_mask); + printf("Successfully initialized Linux r5 remoteproc.\r\n"); + return rproc; +err3: + metal_device_close(prproc->ipi_dev); +err2: + metal_device_close(prproc->shm_dev); +err1: + return NULL; +} + +static void zynqmp_linux_r5_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct metal_device *dev; + + if (!rproc) + return; + prproc = rproc->priv; + metal_io_write32(prproc->ipi_io, IPI_IDR_OFFSET, prproc->ipi_chn_mask); + dev = prproc->ipi_dev; + if (dev) { + metal_irq_disable((uintptr_t)dev->irq_info); + metal_irq_unregister((uintptr_t)dev->irq_info); + metal_device_close(dev); + } + if (prproc->shm_dev) + metal_device_close(prproc->shm_dev); +} + +static void * +zynqmp_linux_r5_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_priv *prproc; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + (void)attribute; + (void)size; + if (!rproc) + return NULL; + prproc = rproc->priv; + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + tmpio = prproc->shm_io; + if (!tmpio) + return NULL; + + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, lpa); +} + +static int zynqmp_linux_r5_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + +#ifdef RPMSG_NO_IPI + metal_io_write32(prproc->shm_poll_io, 0, POLL_STOP); +#else /* RPMSG_NO_IPI */ + metal_io_write32(prproc->ipi_io, IPI_TRIG_OFFSET, + prproc->ipi_chn_mask); +#endif /* !RPMSG_NO_IPI */ + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +struct remoteproc_ops zynqmp_linux_r5_proc_ops = { + .init = zynqmp_linux_r5_proc_init, + .remove = zynqmp_linux_r5_proc_remove, + .mmap = zynqmp_linux_r5_proc_mmap, + .notify = zynqmp_linux_r5_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; diff --git a/apps/machine/zynqmp_r5/CMakeLists.txt b/apps/machine/zynqmp_r5/CMakeLists.txt new file mode 100644 index 0000000..bb22b16 --- /dev/null +++ b/apps/machine/zynqmp_r5/CMakeLists.txt @@ -0,0 +1,5 @@ +collect (APP_COMMON_SOURCES platform_info.c) +collect (APP_COMMON_SOURCES rsc_table.c) +collect (APP_COMMON_SOURCES zynqmp_r5_a53_rproc.c) +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") + diff --git a/apps/machine/zynqmp_r5/platform_info.c b/apps/machine/zynqmp_r5/platform_info.c new file mode 100644 index 0000000..4d4a07e --- /dev/null +++ b/apps/machine/zynqmp_r5/platform_info.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define platform specific data and implements APIs to set + * platform specific information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rsc_table.h" + +#define IPI_DEV_NAME "ipi_dev" +#define IPI_BUS_NAME "generic" + +/* Cortex R5 memory attributes */ +#define DEVICE_SHARED 0x00000001U /* device, shareable */ +#define DEVICE_NONSHARED 0x00000010U /* device, non shareable */ +#define NORM_NSHARED_NCACHE 0x00000008U /* Non cacheable non shareable */ +#define NORM_SHARED_NCACHE 0x0000000CU /* Non cacheable shareable */ +#define PRIV_RW_USER_RW (0x00000003U<<8U) /* Full Access */ + +#if XPAR_CPU_ID == 0 +#define SHARED_MEM_PA 0x3ED40000UL +#else +#define SHARED_MEM_PA 0x3EF40000UL +#endif /* XPAR_CPU_ID */ +#define SHARED_MEM_SIZE 0x100000UL +#define SHARED_BUF_OFFSET 0x8000UL + +#ifndef RPMSG_NO_IPI +#define _rproc_wait() asm volatile("wfi") +#endif /* !RPMSG_NO_IPI */ + +/* Polling information used by remoteproc operations. + */ +static metal_phys_addr_t poll_phys_addr = POLL_BASE_ADDR; +struct metal_device poll_device = { + .name = "poll_dev", + .bus = NULL, + .num_regions = 1, + .regions = { + { + .virt = (void *)POLL_BASE_ADDR, + .physmap = &poll_phys_addr, + .size = 0x1000, + .page_shift = -1UL, + .page_mask = -1UL, + .mem_flags = DEVICE_NONSHARED | PRIV_RW_USER_RW, + .ops = {NULL}, + } + }, + .node = {NULL}, +#ifndef RPMSG_NO_IPI + .irq_num = 1, + .irq_info = (void *)IPI_IRQ_VECT_ID, +#endif /* !RPMSG_NO_IPI */ +}; + +static struct remoteproc_priv rproc_priv = { + .poll_dev_name = IPI_DEV_NAME, + .poll_dev_bus_name = IPI_BUS_NAME, +#ifndef RPMSG_NO_IPI + .ipi_chn_mask = IPI_CHN_BITMASK, +#endif /* !RPMSG_NO_IPI */ +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +extern struct remoteproc_ops zynqmp_r5_a53_proc_ops; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void) proc_index; + rsc_table = get_resource_table(rsc_index, &rsc_size); +#ifndef RPMSG_NO_IPI + /* Register IPI device */ + ret = metal_register_generic_device(&ipi_device); + if (ret) + return ret; +#endif /* !RPMSG_NO_IPI */ + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &zynqmp_r5_a53_proc_ops, &rproc_priv)) + return NULL; + + /* + * Mmap shared memories + * Or shall we constraint that they will be set as carved out + * in the resource table? + */ + /* mmap resource table */ + pa = (metal_phys_addr_t)rsc_table; + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, rsc_size, + NORM_NSHARED_NCACHE|PRIV_RW_USER_RW, + &rproc_inst.rsc_io); + /* mmap shared memory */ + pa = SHARED_MEM_PA; + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, SHARED_MEM_SIZE, + NORM_NSHARED_NCACHE|PRIV_RW_USER_RW, + NULL); + + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table, rsc_size); + if (ret) { + xil_printf("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + xil_printf("Initialize remoteproc successfully.\r\n"); + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + xil_printf("Failed to initialize platform," + "NULL pointer to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + xil_printf("Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_MEM_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, + SHARED_MEM_PA + SHARED_BUF_OFFSET); + + xil_printf("creating remoteproc virtio\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + xil_printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + + xil_printf("initializing rpmsg shared buffer pool\r\n"); + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, + (SHARED_MEM_SIZE - SHARED_BUF_OFFSET)); + + xil_printf("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, + &shpool); + if (ret) { + xil_printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + xil_printf("initializing rpmsg vdev\r\n"); + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + int ret; + + prproc = rproc->priv; + while(1) { +#ifdef RPMSG_NO_IPI + if (metal_io_read32(prproc->poll_io, 0)) { + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } +#else /* !RPMSG_NO_IPI */ + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->ipi_nokick))) { + metal_irq_restore_enable(flags); + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); +#endif /* RPMSG_NO_IPI */ + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev) +{ + (void)rpdev; +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/machine/zynqmp_r5/platform_info.h b/apps/machine/zynqmp_r5/platform_info.h new file mode 100644 index 0000000..1ad396d --- /dev/null +++ b/apps/machine/zynqmp_r5/platform_info.h @@ -0,0 +1,109 @@ +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* Cortex R5 memory attributes */ +#define DEVICE_SHARED 0x00000001U /* device, shareable */ +#define DEVICE_NONSHARED 0x00000010U /* device, non shareable */ +#define NORM_NSHARED_NCACHE 0x00000008U /* Non cacheable non shareable */ +#define NORM_SHARED_NCACHE 0x0000000CU /* Non cacheable shareable */ +#define PRIV_RW_USER_RW (0x00000003U<<8U) /* Full Access */ + +/* Interrupt vectors */ +#ifdef versal +#define IPI_IRQ_VECT_ID 63 +#define POLL_BASE_ADDR 0xFF340000 /* IPI base address*/ +#define IPI_CHN_BITMASK 0x0000020 /* IPI channel bit mask for IPI from/to + APU */ +#else +#define IPI_IRQ_VECT_ID XPAR_XIPIPSU_0_INT_ID +#define POLL_BASE_ADDR XPAR_XIPIPSU_0_BASE_ADDRESS +#define IPI_CHN_BITMASK 0x01000000 +#endif /* versal */ + +#ifdef RPMSG_NO_IPI +#undef POLL_BASE_ADDR +#define POLL_BASE_ADDR 0x3EE40000 +#define POLL_STOP 0x1U +#endif /* RPMSG_NO_IPI */ + +struct remoteproc_priv { + const char *poll_dev_name; + const char *poll_dev_bus_name; + struct metal_device *poll_dev; + struct metal_io_region *poll_io; +#ifndef RPMSG_NO_IPI + unsigned int ipi_chn_mask; /**< IPI channel mask */ + atomic_int ipi_nokick; +#endif /* !RPMSG_NO_IPI */ +}; + +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H_ */ diff --git a/apps/machine/zynqmp_r5/rsc_table.c b/apps/machine/zynqmp_r5/rsc_table.c new file mode 100644 index 0000000..f19a91d --- /dev/null +++ b/apps/machine/zynqmp_r5/rsc_table.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#include +#include "rsc_table.h" + +/* Place resource table in special ELF section */ +#define __section_t(S) __attribute__((__section__(#S))) +#define __resource __section_t(.resource_table) + +#define RPMSG_IPU_C0_FEATURES 1 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Remote supports Name Service announcement */ +#define VIRTIO_RPMSG_F_NS 0 + +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define RING_TX FW_RSC_U32_ADDR_ANY +#define RING_RX FW_RSC_U32_ADDR_ANY +#define VRING_SIZE 256 + +#define NUM_TABLE_ENTRIES 1 + +struct remote_resource_table __resource resources = { + /* Version */ + 1, + + /* NUmber of table entries */ + NUM_TABLE_ENTRIES, + /* reserved fields */ + {0, 0,}, + + /* Offsets of rsc entries */ + { + offsetof(struct remote_resource_table, rpmsg_vdev), + }, + + /* Virtio device entry */ + { + RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +void *get_resource_table (int rsc_id, int *len) +{ + (void) rsc_id; + *len = sizeof(resources); + return &resources; +} + diff --git a/apps/machine/zynqmp_r5/rsc_table.h b/apps/machine/zynqmp_r5/rsc_table.h new file mode 100644 index 0000000..a12c153 --- /dev/null +++ b/apps/machine/zynqmp_r5/rsc_table.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (C) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef RSC_TABLE_H_ +#define RSC_TABLE_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define NO_RESOURCE_ENTRIES 8 + +/* Resource table for the given remote */ +struct remote_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}__attribute__((packed, aligned(0x100))); + +void *get_resource_table (int rsc_id, int *len); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_H_ */ diff --git a/apps/machine/zynqmp_r5/zynqmp_r5_a53_rproc.c b/apps/machine/zynqmp_r5/zynqmp_r5_a53_rproc.c new file mode 100644 index 0000000..d8ce2ba --- /dev/null +++ b/apps/machine/zynqmp_r5/zynqmp_r5_a53_rproc.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * zynqmp_r5_a53_rproc.c + * + * DESCRIPTION + * + * This file define Xilinx ZynqMP R5 to A53 platform specific + * remoteproc implementation. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#ifndef RPMSG_NO_IPI +/* 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 */ + +static int zynqmp_r5_a53_proc_irq_handler(int vect_id, void *data) +{ + struct remoteproc *rproc = data; + struct remoteproc_priv *prproc; + unsigned int ipi_intr_status; + + (void)vect_id; + if (!rproc) + return METAL_IRQ_NOT_HANDLED; + prproc = rproc->priv; + ipi_intr_status = (unsigned int)metal_io_read32(prproc->poll_io, + IPI_ISR_OFFSET); + if (ipi_intr_status & prproc->ipi_chn_mask) { + atomic_flag_clear(&prproc->ipi_nokick); + metal_io_write32(prproc->poll_io, IPI_ISR_OFFSET, + prproc->ipi_chn_mask); + return METAL_IRQ_HANDLED; + } + return METAL_IRQ_NOT_HANDLED; +} +#endif /* !RPMSG_NO_IPI */ + +static struct remoteproc * +zynqmp_r5_a53_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *poll_dev; + unsigned int irq_vect; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + ret = metal_device_open(prproc->poll_dev_bus_name, + prproc->poll_dev_name, + &poll_dev); + if (ret) { + xil_printf("failed to open polling device: %d.\r\n", ret); + return NULL; + } + rproc->priv = prproc; + prproc->poll_dev = poll_dev; + prproc->poll_io = metal_device_io_region(poll_dev, 0); + if (!prproc->poll_io) + goto err1; +#ifndef RPMSG_NO_IPI + atomic_store(&prproc->ipi_nokick, 1); + /* Register interrupt handler and enable interrupt */ + irq_vect = (uintptr_t)ipi_dev->irq_info; + metal_irq_register(irq_vect, zynqmp_r5_a53_proc_irq_handler, rproc); + metal_irq_enable(irq_vect); + metal_io_write32(prproc->poll_io, IPI_IER_OFFSET, + prproc->ipi_chn_mask); +#else + metal_io_write32(prproc->poll_io, 0, !POLL_STOP); +#endif /* !RPMSG_NO_IPI */ + rproc->ops = ops; + + return rproc; +err1: + metal_device_close(poll_dev); + return NULL; +} + +static void zynqmp_r5_a53_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct metal_device *dev; + + if (!rproc) + return; + prproc = rproc->priv; +#ifndef RPMSG_NO_IPI + metal_io_write32(prproc->poll_io, IPI_IDR_OFFSET, + prproc->ipi_chn_mask); + dev = prproc->poll_dev; + if (dev) { + metal_irq_disable((uintptr_t)dev->irq_info); + metal_irq_unregister((uintptr_t)dev->irq_info); + } +#endif /* !RPMSG_NO_IPI */ + metal_device_close(prproc->poll_dev); +} + +static void * +zynqmp_r5_a53_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + + if (!attribute) + attribute = NORM_SHARED_NCACHE | PRIV_RW_USER_RW; + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + tmpio = metal_allocate_memory(sizeof(*tmpio)); + if (!tmpio) { + metal_free_memory(mem); + return NULL; + } + remoteproc_init_mem(mem, NULL, lpa, lda, size, tmpio); + /* va is the same as pa in this platform */ + metal_io_init(tmpio, (void *)lpa, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, mem->pa); +} + +static int zynqmp_r5_a53_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + +#ifdef RPMSG_NO_IPI + metal_io_write32(prproc->poll_io, 0, POLL_STOP); +#else + metal_io_write32(prproc->poll_io, IPI_TRIG_OFFSET, + prproc->ipi_chn_mask); +#endif /* RPMSG_NO_IPI */ + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +struct remoteproc_ops zynqmp_r5_a53_proc_ops = { + .init = zynqmp_r5_a53_proc_init, + .remove = zynqmp_r5_a53_proc_remove, + .mmap = zynqmp_r5_a53_proc_mmap, + .notify = zynqmp_r5_a53_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; diff --git a/apps/system/CMakeLists.txt b/apps/system/CMakeLists.txt new file mode 100644 index 0000000..7ded68b --- /dev/null +++ b/apps/system/CMakeLists.txt @@ -0,0 +1,3 @@ +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM}/CMakeLists.txt") + add_subdirectory (${PROJECT_SYSTEM}) +endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_SYSTEM}/CMakeLists.txt") diff --git a/apps/system/generic/CMakeLists.txt b/apps/system/generic/CMakeLists.txt new file mode 100644 index 0000000..ea643a4 --- /dev/null +++ b/apps/system/generic/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory (machine) diff --git a/apps/system/generic/machine/CMakeLists.txt b/apps/system/generic/machine/CMakeLists.txt new file mode 100644 index 0000000..9720224 --- /dev/null +++ b/apps/system/generic/machine/CMakeLists.txt @@ -0,0 +1,3 @@ +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") + add_subdirectory (${PROJECT_MACHINE}) +endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") diff --git a/apps/system/generic/machine/rv64_virt/CMakeLists.txt b/apps/system/generic/machine/rv64_virt/CMakeLists.txt new file mode 100644 index 0000000..308c891 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/CMakeLists.txt @@ -0,0 +1,9 @@ +collect (APP_COMMON_SOURCES platform_info.c) +collect (APP_COMMON_SOURCES rv64_virt_linux_proc.c) +collect (APP_COMMON_SOURCES helper.c) +collect (APP_COMMON_SOURCES rsc_table.c) +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") +if (RPMSG_NO_IPI) + add_definitions(-DRPMSG_NO_IPI) +endif (RPMSG_NO_IPI) + diff --git a/apps/system/generic/machine/rv64_virt/helper.c b/apps/system/generic/machine/rv64_virt/helper.c new file mode 100644 index 0000000..54ca1e5 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/helper.c @@ -0,0 +1,26 @@ + +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include + +int init_system() +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + + return 0; +} + +void cleanup_system() +{ + metal_finish(); +} diff --git a/apps/system/generic/machine/rv64_virt/helper.o b/apps/system/generic/machine/rv64_virt/helper.o new file mode 100644 index 0000000..1da6dc8 Binary files /dev/null and b/apps/system/generic/machine/rv64_virt/helper.o differ diff --git a/apps/system/generic/machine/rv64_virt/platform_info.c b/apps/system/generic/machine/rv64_virt/platform_info.c new file mode 100644 index 0000000..017c022 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/platform_info.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * Copyright (c) 2020 Bamvor Jian ZHANG + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define platform specific data and implements APIs to set + * platform specific information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rsc_table.h" + +#define IPI_DEV_NAME "ipi_dev" +#define IPI_BUS_NAME "generic" + +#ifndef RPMSG_NO_IPI +#define _rproc_wait() asm volatile("wfi") +#endif /* !RPMSG_NO_IPI */ + +#ifndef RPMSG_NO_IPI +#error only support NOIPI right now +#endif /* !RPMSG_NO_IPI */ + +/* Polling information used by remoteproc operations. + */ +static metal_phys_addr_t poll_phys_addr = POLL_BASE_ADDR; +struct metal_device ipi_device = { + .name = IPI_DEV_NAME, + .bus = NULL, + .num_regions = 1, + .regions = { + { + .virt = (void *)POLL_BASE_ADDR, + .physmap = &poll_phys_addr, + .size = 0x1000, + .page_shift = -1UL, + .page_mask = -1UL, + .mem_flags = 0, + .ops = {NULL}, + } + }, + .node = {NULL}, +#ifndef RPMSG_NO_IPI + .irq_num = 1, + .irq_info = (void *)IPI_IRQ_VECT_ID, +#endif /* !RPMSG_NO_IPI */ +}; + +static struct remoteproc_priv rproc_priv = { + .poll_dev_name = IPI_DEV_NAME, + .poll_dev_bus_name = IPI_BUS_NAME, +#ifndef RPMSG_NO_IPI + .ipi_chn_mask = IPI_CHN_BITMASK, +#endif /* !RPMSG_NO_IPI */ +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +extern struct remoteproc_ops virt_rv64_proc_ops; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void) proc_index; + rsc_table = get_resource_table(rsc_index, &rsc_size); + /* Register IPI device */ + ret = metal_register_generic_device(&ipi_device); + if (ret) + return NULL; + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &virt_rv64_proc_ops, &rproc_priv)) + return NULL; + + /* + * Mmap shared memories + * Or shall we constraint that they will be set as carved out + * in the resource table? + */ + /* mmap resource table */ + pa = RSC_MEM_PA; + memcpy((void*)pa, rsc_table, rsc_size); + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, rsc_size, + 0, + &rproc_inst.rsc_io); + /* mmap shared memory */ + pa = SHARED_BUF_PA; + (void *)remoteproc_mmap(&rproc_inst, &pa, + NULL, SHARED_BUF_SIZE, + 0, + NULL); + + /* parse resource table to remoteproc */ + pa = RSC_MEM_PA; + ret = remoteproc_set_rsc_table(&rproc_inst, (struct resource_table *)pa, rsc_size); + if (ret) { + PRINT("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + PRINT("Initialize remoteproc successfully.\r\n"); + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + PRINT("Failed to initialize platform," + "NULL pointer to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + PRINT("Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_BUF_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, + SHARED_BUF_PA); + + PRINT("creating remoteproc virtio\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + PRINT("failed remoteproc_create_virtio\r\n"); + goto err1; + } + + PRINT("initializing rpmsg shared buffer pool\r\n"); + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUF_SIZE); + + PRINT("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, + &shpool); + if (ret) { + PRINT("failed rpmsg_init_vdev\r\n"); + goto err2; + } + PRINT("initializing rpmsg vdev\r\n"); + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + int ret; + + prproc = rproc->priv; + while(1) { +#ifdef RPMSG_NO_IPI + if (metal_io_read32(prproc->poll_io, 0)) { + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } +#else /* !RPMSG_NO_IPI */ + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->ipi_nokick))) { + metal_irq_restore_enable(flags); + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); +#endif /* RPMSG_NO_IPI */ + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev, void *platform) +{ + struct rpmsg_virtio_device *rpvdev; + struct remoteproc *rproc; + + rpvdev = metal_container_of(rpdev, struct rpmsg_virtio_device, rdev); + rproc = platform; + + rpmsg_deinit_vdev(rpvdev); + remoteproc_remove_virtio(rproc, rpvdev->vdev); +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/system/generic/machine/rv64_virt/platform_info.h b/apps/system/generic/machine/rv64_virt/platform_info.h new file mode 100644 index 0000000..6294272 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/platform_info.h @@ -0,0 +1,109 @@ +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define PRINT printf + +/* memory attributes */ + +/* Interrupt vectors */ + +/* POLL device */ +#ifdef RPMSG_NO_IPI +#undef POLL_BASE_ADDR +//qemu virt_memmap hw/riscv/virt.c +//[VIRT_SHM_IPI] = { 0x90000000, 0x00080000 }, +#define POLL_BASE_ADDR 0x90000000UL +#define POLL_STOP 0x1U +#endif /* RPMSG_NO_IPI */ + +//TODO: move to common +#define SHM_BASE 0x90100000UL + +#define RSC_MEM_PA SHM_BASE +#define RSC_MEM_SIZE 0x00020000UL +//shared buffer: 0x90120000--0x90160000 +#define SHARED_BUF_PA (SHM_BASE + RSC_MEM_SIZE) +#define SHARED_BUF_SIZE 0x00040000UL +//TODO: move to common end + +struct remoteproc_priv { + const char *poll_dev_name; + const char *poll_dev_bus_name; + struct metal_device *poll_dev; + struct metal_io_region *poll_io; +#ifndef RPMSG_NO_IPI + unsigned int ipi_chn_mask; /**< IPI channel mask */ + atomic_int ipi_nokick; +#endif /* !RPMSG_NO_IPI */ +}; + +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev, void *platform); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H_ */ diff --git a/apps/system/generic/machine/rv64_virt/platform_info.o b/apps/system/generic/machine/rv64_virt/platform_info.o new file mode 100644 index 0000000..3df6ee5 Binary files /dev/null and b/apps/system/generic/machine/rv64_virt/platform_info.o differ diff --git a/apps/system/generic/machine/rv64_virt/rsc_table.c b/apps/system/generic/machine/rv64_virt/rsc_table.c new file mode 100644 index 0000000..f19a91d --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/rsc_table.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#include +#include "rsc_table.h" + +/* Place resource table in special ELF section */ +#define __section_t(S) __attribute__((__section__(#S))) +#define __resource __section_t(.resource_table) + +#define RPMSG_IPU_C0_FEATURES 1 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Remote supports Name Service announcement */ +#define VIRTIO_RPMSG_F_NS 0 + +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define RING_TX FW_RSC_U32_ADDR_ANY +#define RING_RX FW_RSC_U32_ADDR_ANY +#define VRING_SIZE 256 + +#define NUM_TABLE_ENTRIES 1 + +struct remote_resource_table __resource resources = { + /* Version */ + 1, + + /* NUmber of table entries */ + NUM_TABLE_ENTRIES, + /* reserved fields */ + {0, 0,}, + + /* Offsets of rsc entries */ + { + offsetof(struct remote_resource_table, rpmsg_vdev), + }, + + /* Virtio device entry */ + { + RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +void *get_resource_table (int rsc_id, int *len) +{ + (void) rsc_id; + *len = sizeof(resources); + return &resources; +} + diff --git a/apps/system/generic/machine/rv64_virt/rsc_table.h b/apps/system/generic/machine/rv64_virt/rsc_table.h new file mode 100644 index 0000000..a12c153 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/rsc_table.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (C) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef RSC_TABLE_H_ +#define RSC_TABLE_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define NO_RESOURCE_ENTRIES 8 + +/* Resource table for the given remote */ +struct remote_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}__attribute__((packed, aligned(0x100))); + +void *get_resource_table (int rsc_id, int *len); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_H_ */ diff --git a/apps/system/generic/machine/rv64_virt/rsc_table.o b/apps/system/generic/machine/rv64_virt/rsc_table.o new file mode 100644 index 0000000..9606248 Binary files /dev/null and b/apps/system/generic/machine/rv64_virt/rsc_table.o differ diff --git a/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.c b/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.c new file mode 100644 index 0000000..851dc18 --- /dev/null +++ b/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * virt_rv64_rproc.c + * + * DESCRIPTION + * + * This file define Xilinx ZynqMP R5 to A53 platform specific + * remoteproc implementation. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" + +static struct remoteproc * +virt_rv64_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *poll_dev; + unsigned int irq_vect; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + ret = metal_device_open(prproc->poll_dev_bus_name, + prproc->poll_dev_name, + &poll_dev); + if (ret) { + PRINT("failed to open polling device: %d.\r\n", ret); + return NULL; + } + rproc->priv = prproc; + prproc->poll_dev = poll_dev; + prproc->poll_io = metal_device_io_region(poll_dev, 0); + if (!prproc->poll_io) + goto err1; +#ifndef RPMSG_NO_IPI + atomic_store(&prproc->ipi_nokick, 1); + /* Register interrupt handler and enable interrupt */ + irq_vect = (uintptr_t)ipi_dev->irq_info; + metal_irq_register(irq_vect, virt_rv64_proc_irq_handler, rproc); + metal_irq_enable(irq_vect); + metal_io_write32(prproc->poll_io, IPI_IER_OFFSET, + prproc->ipi_chn_mask); +#else + metal_io_write32(prproc->poll_io, 0, !POLL_STOP); +#endif /* !RPMSG_NO_IPI */ + rproc->ops = ops; + + return rproc; +err1: + metal_device_close(poll_dev); + return NULL; +} + +static void virt_rv64_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct metal_device *dev; + + if (!rproc) + return; + prproc = rproc->priv; +#ifndef RPMSG_NO_IPI + metal_io_write32(prproc->poll_io, IPI_IDR_OFFSET, + prproc->ipi_chn_mask); + dev = prproc->poll_dev; + if (dev) { + metal_irq_disable((uintptr_t)dev->irq_info); + metal_irq_unregister((uintptr_t)dev->irq_info); + } +#endif /* !RPMSG_NO_IPI */ + metal_device_close(prproc->poll_dev); +} + +static void * +virt_rv64_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + + if (!attribute) + attribute = 0; + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) + return NULL; + tmpio = metal_allocate_memory(sizeof(*tmpio)); + if (!tmpio) { + metal_free_memory(mem); + return NULL; + } + remoteproc_init_mem(mem, NULL, lpa, lda, size, tmpio); + /* va is the same as pa in this platform */ + metal_io_init(tmpio, (void *)lpa, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, mem->pa); +} + +static int virt_rv64_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + +#ifdef RPMSG_NO_IPI + metal_io_write32(prproc->poll_io, 0, POLL_STOP); +#else + metal_io_write32(prproc->poll_io, IPI_TRIG_OFFSET, + prproc->ipi_chn_mask); +#endif /* RPMSG_NO_IPI */ + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +struct remoteproc_ops virt_rv64_proc_ops = { + .init = virt_rv64_proc_init, + .remove = virt_rv64_proc_remove, + .mmap = virt_rv64_proc_mmap, + .notify = virt_rv64_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; diff --git a/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.o b/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.o new file mode 100644 index 0000000..056067a Binary files /dev/null and b/apps/system/generic/machine/rv64_virt/virt_rv64_rproc.o differ diff --git a/apps/system/generic/machine/zynq7/CMakeLists.txt b/apps/system/generic/machine/zynq7/CMakeLists.txt new file mode 100644 index 0000000..094fbe7 --- /dev/null +++ b/apps/system/generic/machine/zynq7/CMakeLists.txt @@ -0,0 +1,16 @@ +collect (APP_COMMON_SOURCES helper.c) + +set (_linker_script "${CMAKE_CURRENT_SOURCE_DIR}/linker_remote.ld") + +set_property (GLOBAL PROPERTY APP_LINKER_OPT "-Wl,-build-id=none -specs=${CMAKE_CURRENT_SOURCE_DIR}/Xilinx.spec -T\"${_linker_script}\"") + +find_path(XIL_INCLUDE_DIR NAMES xparameters.h PATHS ${CMAKE_FIND_ROOT_PATH}) +collect (PROJECT_INC_DIRS "${XIL_INCLUDE_DIR}") + +find_library(LIBXIL_LIB NAMES xil PATHS ${CMAKE_FIND_ROOT_PATH}) +get_filename_component(LIBXIL_LIB_DIR ${LIBXIL_LIB} DIRECTORY) +collect(PROJECT_LIB_DIRS ${LIBXIL_LIB_DIR}) + +collect(PROJECT_LIB_DEPS xil) +collect(PROJECT_LIB_DEPS c) +collect(PROJECT_LIB_DEPS m) diff --git a/apps/system/generic/machine/zynq7/Xilinx.spec b/apps/system/generic/machine/zynq7/Xilinx.spec new file mode 100644 index 0000000..8eea377 --- /dev/null +++ b/apps/system/generic/machine/zynq7/Xilinx.spec @@ -0,0 +1,2 @@ +*startfile: +crti%O%s crtbegin%O%s diff --git a/apps/system/generic/machine/zynq7/helper.c b/apps/system/generic/machine/zynq7/helper.c new file mode 100644 index 0000000..f3c4274 --- /dev/null +++ b/apps/system/generic/machine/zynq7/helper.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "xparameters.h" +#include "xil_exception.h" +#include "xil_printf.h" +#include "xscugic.h" +#include "xil_cache.h" +#include +#include +#include "platform_info.h" + + +#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID + +static XScuGic xInterruptController; + +/* Interrupt Controller setup */ +static int app_gic_initialize(void) +{ + u32 Status; + XScuGic_Config *IntcConfig; /* The configuration parameters of the interrupt controller */ + + Xil_ExceptionDisable(); + + /* + * Initialize the interrupt controller driver + */ + IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); + if (NULL == IntcConfig) { + return XST_FAILURE; + } + + Status = XScuGic_CfgInitialize(&xInterruptController, IntcConfig, + IntcConfig->CpuBaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Register the interrupt handler to the hardware interrupt handling + * logic in the ARM processor. + */ + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, + (Xil_ExceptionHandler)XScuGic_InterruptHandler, + &xInterruptController); + + /* Disable the interrupt before enabling exception to avoid interrupts + * received before exception is enabled. + */ + XScuGic_Disable(&xInterruptController, SGI_NOTIFICATION); + + Xil_ExceptionEnable(); + + /* Connect notificaiton interrupt ID with ISR */ + XScuGic_Connect(&xInterruptController, SGI_NOTIFICATION, + (Xil_ExceptionHandler)metal_xlnx_irq_isr, + (void *)SGI_NOTIFICATION); + + return 0; +} + +/* Main hw machinery initialization entry point, called from main()*/ +/* return 0 on success */ +int init_system(void) +{ + int ret; + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + /* Low level abstraction layer for openamp initialization */ + metal_init(&metal_param); + + /* configure the global interrupt controller */ + app_gic_initialize(); + + /* Initialize metal Xilinx IRQ controller */ + ret = metal_xlnx_irq_init(); + if (ret) { + xil_printf("%s: Xilinx metal IRQ controller init failed.\r\n", + __func__); + } + + return ret; +} + +void cleanup_system() +{ + metal_finish(); + + Xil_DCacheDisable(); + Xil_ICacheDisable(); + Xil_DCacheInvalidate(); + Xil_ICacheInvalidate(); +} diff --git a/apps/system/generic/machine/zynq7/linker_master.ld b/apps/system/generic/machine/zynq7/linker_master.ld new file mode 100644 index 0000000..a571ef1 --- /dev/null +++ b/apps/system/generic/machine/zynq7/linker_master.ld @@ -0,0 +1,228 @@ +/* Linker script for Xilinx Zynq-7000 + * + * Version: Sourcery CodeBench Lite 2013.11-24 + * Support: https://sourcery.mentor.com/GNUToolchain/ + * + * Copyright (c) 2007-2010 CodeSourcery, Inc. + * Copyright (c) 2010-2013 Mentor Graphics, Inc. + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +ENTRY(__cs3_reset) +SEARCH_DIR(.) +GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3arm) + +MEMORY +{ + ram (rwx) : ORIGIN = 0x10000000, LENGTH = 256M + rom (rx) : ORIGIN = 0xe4000000, LENGTH = 64M +} + +/* These force the linker to search for particular symbols from + * the start of the link process and thus ensure the user's + * overrides are picked up + */ +EXTERN(__cs3_reset __cs3_reset_zynq7000_ram) +EXTERN(__cs3_start_asm _start) +/* Bring in the interrupt routines & vector */ +INCLUDE arm-names.inc +EXTERN(__cs3_interrupt_vector_arm) +EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end) + +/* Provide fall-back values */ +PROVIDE(__cs3_heap_start = _end); +PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram); +PROVIDE(__cs3_region_num = (__cs3_regions_end - __cs3_regions) / 20); +PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram); + +SECTIONS +{ + .text : + { + CREATE_OBJECT_SYMBOLS + __cs3_region_start_ram = .; + _ftext = .; + *(.cs3.region-head.ram) + ASSERT (. == __cs3_region_start_ram, ".cs3.region-head.ram not permitted"); + __cs3_interrupt_vector = __cs3_interrupt_vector_arm; + *(.cs3.interrupt_vector) + /* Make sure we pulled in an interrupt vector. */ + ASSERT (. != __cs3_interrupt_vector_arm, "No interrupt vector"); + + PROVIDE(__cs3_reset = __cs3_reset_zynq7000_ram); + *(.cs3.reset) + _start = DEFINED(__cs3_start_asm) ? __cs3_start_asm : _start; + + *(.text.cs3.init) + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) + + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.gcc_except_table) + } >ram + .eh_frame_hdr : ALIGN (4) + { + KEEP (*(.eh_frame_hdr)) + *(.eh_frame_entry .eh_frame_entry.*) + } >ram + .eh_frame : ALIGN (4) + { + KEEP (*(.eh_frame)) *(.eh_frame.*) + } >ram + /* .ARM.exidx is sorted, so has to go in its own output section. */ + PROVIDE_HIDDEN (__exidx_start = .); + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } >ram + PROVIDE_HIDDEN (__exidx_end = .); + .rodata : ALIGN (4) + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + + . = ALIGN(4); + KEEP(*(.init)) + + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + . = ALIGN(4); + KEEP(*(.fini)) + + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + . = ALIGN(0x4); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + + . = ALIGN(0x4); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + . = ALIGN(4); + __cs3_regions = .; + LONG (0) + LONG (__cs3_region_init_ram) + LONG (__cs3_region_start_ram) + LONG (__cs3_region_init_size_ram) + LONG (__cs3_region_zero_size_ram) + __cs3_regions_end = .; + . = ALIGN (8); + _etext = .; + } >ram + + ASSERT (!(__cs3_region_init_ram & 7), "__cs3_region_init_ram not aligned") + ASSERT (!(__cs3_region_start_ram & 7), "__cs3_region_start_ram not aligned") + ASSERT (!(__cs3_region_init_size_ram & 7), "__cs3_region_init_size_ram not aligned") + ASSERT (!(__cs3_region_zero_size_ram & 7), "__cs3_region_zero_size_ram not aligned") + .cs3.rom : ALIGN (8) + { + __cs3_region_start_rom = .; + *(.cs3.region-head.rom) + *(.rom) + . = ALIGN (8); + } >rom + .cs3.rom.bss : + { + *(.rom.b .bss.rom) + . = ALIGN (8); + } >rom + /* __cs3_region_end_rom is deprecated */ + __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom); + __cs3_region_size_rom = LENGTH(rom); + __cs3_region_init_rom = LOADADDR (.cs3.rom); + __cs3_region_init_size_rom = LOADADDR (.cs3.rom.bss) - LOADADDR (.cs3.rom); + __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss); + + .data : ALIGN (8) + { + KEEP(*(.jcr)) + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN (8); + *(.ram) + . = ALIGN (8); + _edata = .; + } >ram + .bss : ALIGN (8) + { + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + *(.ram.b .bss.ram) + . = ALIGN (8); + _end = .; + __end = .; + } >ram + /* __cs3_region_end_ram is deprecated */ + __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram); + __cs3_region_size_ram = LENGTH(ram); + __cs3_region_init_ram = LOADADDR (.text); + __cs3_region_init_size_ram = _edata - ADDR (.text); + __cs3_region_zero_size_ram = _end - _edata; + + .stab 0 (NOLOAD) : { *(.stab) } + .stabstr 0 (NOLOAD) : { *(.stabstr) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to + * the beginning of the section so we begin them at 0. + */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* DWARF 2.1 */ + .debug_ranges 0 : { *(.debug_ranges) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) } +} +/* checksum: b15b0bf76673e544380111 */ diff --git a/apps/system/generic/machine/zynq7/linker_remote.ld b/apps/system/generic/machine/zynq7/linker_remote.ld new file mode 100644 index 0000000..3f1996f --- /dev/null +++ b/apps/system/generic/machine/zynq7/linker_remote.ld @@ -0,0 +1,290 @@ +/*******************************************************************/ +/* */ +/* This file is automatically generated by linker script generator.*/ +/* */ +/* Version: */ +/* */ +/* Copyright (c) 2010 Xilinx, Inc. All rights reserved. */ +/* */ +/* Description : Cortex-A9 Linker Script */ +/* */ +/*******************************************************************/ + +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000; + +_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; +_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; +_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 4096; +_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; +_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; + +/* Define Memories in the system */ + +MEMORY +{ + ps7_ddr_0_S_AXI_BASEADDR : ORIGIN = 0x3e000000, LENGTH = 0x00400000 + ps7_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00030000 + ps7_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x0000FE00 +} + +/* Specify the default entry point to the program */ + +ENTRY(_vector_table) + +/* Define the sections, and where they are mapped in memory */ + +SECTIONS +{ +.text : { + +_binary_firmware1_start = 0; +_binary_firmware1_end = 0; +_binary_firmware2_start = 0; +_binary_firmware2_end = 0; + + *(.vectors) + *(.boot) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.gnu_warning) + *(.gcc_execpt_table) + *(.glue_7) + *(.glue_7t) + *(.vfp11_veneer) + *(.ARM.extab) + *(.gnu.linkonce.armextab.*) +} > ps7_ddr_0_S_AXI_BASEADDR + +.init : { + KEEP (*(.init)) +} > ps7_ddr_0_S_AXI_BASEADDR + +.fini : { + KEEP (*(.fini)) +} > ps7_ddr_0_S_AXI_BASEADDR + +.rodata : { + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.rodata1 : { + __rodata1_start = .; + *(.rodata1) + *(.rodata1.*) + __rodata1_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.sdata2 : { + __sdata2_start = .; + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + __sdata2_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.sbss2 : { + __sbss2_start = .; + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + __sbss2_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.data : { + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.jcr) + *(.got) + *(.got.plt) + __data_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.data1 : { + __data1_start = .; + *(.data1) + *(.data1.*) + __data1_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.got : { + *(.got) +} > ps7_ddr_0_S_AXI_BASEADDR + +.ctors : { + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.dtors : { + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + __DTOR_END__ = .; + ___DTORS_END___ = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.fixup : { + __fixup_start = .; + *(.fixup) + __fixup_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.eh_frame : { + *(.eh_frame) +} > ps7_ddr_0_S_AXI_BASEADDR + +.eh_framehdr : { + __eh_framehdr_start = .; + *(.eh_framehdr) + __eh_framehdr_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.gcc_except_table : { + *(.gcc_except_table) +} > ps7_ddr_0_S_AXI_BASEADDR + +.mmu_tbl (ALIGN(16384)) : { + __mmu_tbl_start = .; + *(.mmu_tbl) + __mmu_tbl_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + *(.gnu.linkonce.armexidix.*.*) + __exidx_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.preinit_array : { + __preinit_array_start = .; + KEEP (*(SORT(.preinit_array.*))) + KEEP (*(.preinit_array)) + __preinit_array_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.fini_array : { + __fini_array_start = .; + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + __fini_array_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.ARM.attributes : { + __ARM.attributes_start = .; + *(.ARM.attributes) + __ARM.attributes_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.sdata : { + __sdata_start = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + __sdata_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.sbss (NOLOAD) : { + __sbss_start = .; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + __sbss_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.tdata : { + __tdata_start = .; + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + __tdata_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.tbss : { + __tbss_start = .; + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + __tbss_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.bss (NOLOAD) : { + __bss_start = .; + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + __bss_end = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); + +_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(16); + _heap = .; + HeapBase = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; + HeapLimit = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +.stack (NOLOAD) : { + . = ALIGN(16); + _stack_end = .; + . += _STACK_SIZE; + _stack = .; + __stack = _stack; + . = ALIGN(16); + _irq_stack_end = .; + . += _IRQ_STACK_SIZE; + __irq_stack = .; + _supervisor_stack_end = .; + . += _SUPERVISOR_STACK_SIZE; + . = ALIGN(16); + __supervisor_stack = .; + _abort_stack_end = .; + . += _ABORT_STACK_SIZE; + . = ALIGN(16); + __abort_stack = .; + _fiq_stack_end = .; + . += _FIQ_STACK_SIZE; + . = ALIGN(16); + __fiq_stack = .; + _undef_stack_end = .; + . += _UNDEF_STACK_SIZE; + . = ALIGN(16); + __undef_stack = .; +} > ps7_ddr_0_S_AXI_BASEADDR + +_end = .; +} diff --git a/apps/system/generic/machine/zynqmp_r5/CMakeLists.txt b/apps/system/generic/machine/zynqmp_r5/CMakeLists.txt new file mode 100644 index 0000000..d5d4e10 --- /dev/null +++ b/apps/system/generic/machine/zynqmp_r5/CMakeLists.txt @@ -0,0 +1,36 @@ +include(CheckSymbolExists) + +collect (APP_COMMON_SOURCES helper.c) + +set (_linker_script "${CMAKE_CURRENT_SOURCE_DIR}/linker_remote.ld") +set_property (GLOBAL PROPERTY APP_LINKER_OPT "-T\"${_linker_script}\"") +set (_linker_large_text_script "${CMAKE_CURRENT_SOURCE_DIR}/linker_large_text.ld") +set_property (GLOBAL PROPERTY APP_LINKER_LARGE_TEXT_OPT "-T\"${_linker_large_text_script}\"") + +find_path(XIL_INCLUDE_DIR NAMES xparameters.h PATHS ${CMAKE_FIND_ROOT_PATH}) +collect (PROJECT_INC_DIRS "${XIL_INCLUDE_DIR}") + +find_library(LIBXIL_LIB NAMES xil PATHS ${CMAKE_FIND_ROOT_PATH}) +get_filename_component(LIBXIL_LIB_DIR ${LIBXIL_LIB} DIRECTORY) +collect(PROJECT_LIB_DIRS ${LIBXIL_LIB_DIR}) + +# check PM API for certain headers and set client version based +CHECK_SYMBOL_EXISTS(XPAR_XILPM_ENABLED "xparameters.h" PM_FOUND) +if(PM_FOUND) + find_library(HAS_PM_LIB NAMES xilpm PATHS ${CMAKE_FIND_ROOT_PATH}) + collect (PROJECT_LIB_DEPS xilpm) + CHECK_SYMBOL_EXISTS(versal "xparameters.h" VERSION_2_PM_CLIENT) + if (VERSION_2_PM_CLIENT) + collect (APP_EXTRA_C_FLAGS " -DVERSION_2_PM_CLIENT ") + else() + collect (APP_EXTRA_C_FLAGS " -DVERSION_1_PM_CLIENT ") + endif(VERSION_2_PM_CLIENT) +endif(PM_FOUND) + +collect(PROJECT_LIB_DEPS xil) +collect(PROJECT_LIB_DEPS c) +collect(PROJECT_LIB_DEPS m) +find_library(XILMEM_LIB NAMES xilmem PATHS ${CMAKE_FIND_ROOT_PATH}) +if(XILMEM_LIB) + collect (PROJECT_LIB_DEPS xilmem) +endif(XILMEM_LIB) diff --git a/apps/system/generic/machine/zynqmp_r5/helper.c b/apps/system/generic/machine/zynqmp_r5/helper.c new file mode 100644 index 0000000..5000f3e --- /dev/null +++ b/apps/system/generic/machine/zynqmp_r5/helper.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "xparameters.h" +#include "xil_exception.h" +#include "xil_printf.h" +#include "xscugic.h" +#include "xil_cache.h" +#include +#include +#include "platform_info.h" + +#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID + +static XScuGic xInterruptController; + +/* Interrupt Controller setup */ +static int app_gic_initialize(void) +{ + uint32_t status; + XScuGic_Config *int_ctrl_config; /* interrupt controller configuration params */ + uint32_t int_id; + uint32_t mask_cpu_id = ((u32)0x1 << XPAR_CPU_ID); + uint32_t target_cpu; + + mask_cpu_id |= mask_cpu_id << 8U; + mask_cpu_id |= mask_cpu_id << 16U; + + Xil_ExceptionDisable(); + + /* + * Initialize the interrupt controller driver + */ + int_ctrl_config = XScuGic_LookupConfig(INTC_DEVICE_ID); + if (NULL == int_ctrl_config) { + return XST_FAILURE; + } + + status = XScuGic_CfgInitialize(&xInterruptController, int_ctrl_config, + int_ctrl_config->CpuBaseAddress); + if (status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* Only associate interrupt needed to this CPU */ + for (int_id = 32U; int_id psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.gnu_warning) + *(.gcc_execpt_table) + *(.glue_7) + *(.glue_7t) + *(.vfp11_veneer) + *(.ARM.extab) + *(.gnu.linkonce.armextab.*) +} > psu_ddr_S_AXI_BASEADDR + +.init : { + KEEP (*(.init)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini : { + KEEP (*(.fini)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.interp : { + KEEP (*(.interp)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.note-ABI-tag : { + KEEP (*(.note-ABI-tag)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata : { + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata1 : { + __rodata1_start = .; + *(.rodata1) + *(.rodata1.*) + __rodata1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata2 : { + __sdata2_start = .; + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + __sdata2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss2 : { + __sbss2_start = .; + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + __sbss2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data : { + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.jcr) + *(.got) + *(.got.plt) + __data_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data1 : { + __data1_start = .; + *(.data1) + *(.data1.*) + __data1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.got : { + *(.got) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ctors : { + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.dtors : { + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + __DTOR_END__ = .; + ___DTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fixup : { + __fixup_start = .; + *(.fixup) + __fixup_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_frame : { + *(.eh_frame) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_framehdr : { + __eh_framehdr_start = .; + *(.eh_framehdr) + __eh_framehdr_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.gcc_except_table : { + *(.gcc_except_table) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.mmu_tbl (ALIGN(16384)) : { + __mmu_tbl_start = .; + *(.mmu_tbl) + __mmu_tbl_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + *(.gnu.linkonce.armexidix.*.*) + __exidx_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.preinit_array : { + __preinit_array_start = .; + KEEP (*(SORT(.preinit_array.*))) + KEEP (*(.preinit_array)) + __preinit_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini_array : { + __fini_array_start = .; + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + __fini_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.attributes : { + __ARM.attributes_start = .; + *(.ARM.attributes) + __ARM.attributes_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata : { + __sdata_start = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + __sdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss (NOLOAD) : { + __sbss_start = .; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + __sbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tdata : { + __tdata_start = .; + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + __tdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tbss : { + __tbss_start = .; + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + __tbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.bss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); + +_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(16); + _heap = .; + HeapBase = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; + HeapLimit = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.stack (NOLOAD) : { + . = ALIGN(16); + _stack_end = .; + . += _STACK_SIZE; + _stack = .; + __stack = _stack; + . = ALIGN(16); + _irq_stack_end = .; + . += _IRQ_STACK_SIZE; + __irq_stack = .; + _supervisor_stack_end = .; + . += _SUPERVISOR_STACK_SIZE; + . = ALIGN(16); + __supervisor_stack = .; + _abort_stack_end = .; + . += _ABORT_STACK_SIZE; + . = ALIGN(16); + __abort_stack = .; + _fiq_stack_end = .; + . += _FIQ_STACK_SIZE; + . = ALIGN(16); + __fiq_stack = .; + _undef_stack_end = .; + . += _UNDEF_STACK_SIZE; + . = ALIGN(16); + __undef_stack = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.resource_table 0x3ed20000 : { + . = ALIGN(4); + *(.resource_table) +} > psu_ddr_S_AXI_BASEADDR + +_end = .; +} diff --git a/apps/system/generic/machine/zynqmp_r5/linker_remote.ld b/apps/system/generic/machine/zynqmp_r5/linker_remote.ld new file mode 100644 index 0000000..ed1e900 --- /dev/null +++ b/apps/system/generic/machine/zynqmp_r5/linker_remote.ld @@ -0,0 +1,321 @@ +/****************************************************************************** +* +* 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. +* +* 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 +* XILINX 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. +* +******************************************************************************/ + +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x2000; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x4000; + +_ABORT_STACK_SIZE = DEFINED(_ABORT_STACK_SIZE) ? _ABORT_STACK_SIZE : 1024; +_SUPERVISOR_STACK_SIZE = DEFINED(_SUPERVISOR_STACK_SIZE) ? _SUPERVISOR_STACK_SIZE : 2048; +_IRQ_STACK_SIZE = DEFINED(_IRQ_STACK_SIZE) ? _IRQ_STACK_SIZE : 1024; +_FIQ_STACK_SIZE = DEFINED(_FIQ_STACK_SIZE) ? _FIQ_STACK_SIZE : 1024; +_UNDEF_STACK_SIZE = DEFINED(_UNDEF_STACK_SIZE) ? _UNDEF_STACK_SIZE : 1024; + +/* Define Memories in the system */ +/* TCM size is set to 2*0x20000 for R5 in lockstep mode */ +MEMORY +{ + psu_ddr_S_AXI_BASEADDR : ORIGIN = 0x3ED00000, LENGTH = 0x00040000 + psu_ocm_ram_1_S_AXI_BASEADDR : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000 + psu_r5_tcm_ram_0_S_AXI_BASEADDR : ORIGIN = 0x00000000, LENGTH = 0x00020000 + psu_r5_tcm_ram_1_S_AXI_BASEADDR : ORIGIN = 0x00020000, LENGTH = 0x00020000 +} + +/* Specify the default entry point to the program */ + +/* ENTRY(_boot) */ + +ENTRY(_vector_table) + +/* Define the sections, and where they are mapped in memory */ + +SECTIONS +{ +.vectors : { + KEEP (*(.vectors)) + *(.boot) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.plt) + *(.gnu_warning) + *(.gcc_execpt_table) + *(.glue_7) + *(.glue_7t) + *(.vfp11_veneer) + *(.ARM.extab) + *(.gnu.linkonce.armextab.*) +} > psu_r5_tcm_ram_1_S_AXI_BASEADDR + +.init : { + KEEP (*(.init)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini : { + KEEP (*(.fini)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.interp : { + KEEP (*(.interp)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.note-ABI-tag : { + KEEP (*(.note-ABI-tag)) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata : { + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.rodata1 : { + __rodata1_start = .; + *(.rodata1) + *(.rodata1.*) + __rodata1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata2 : { + __sdata2_start = .; + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + __sdata2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss2 : { + __sbss2_start = .; + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + __sbss2_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data : { + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.jcr) + *(.got) + *(.got.plt) + __data_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.data1 : { + __data1_start = .; + *(.data1) + *(.data1.*) + __data1_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.got : { + *(.got) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ctors : { + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.dtors : { + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + __DTOR_END__ = .; + ___DTORS_END___ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fixup : { + __fixup_start = .; + *(.fixup) + __fixup_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_frame : { + *(.eh_frame) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.eh_framehdr : { + __eh_framehdr_start = .; + *(.eh_framehdr) + __eh_framehdr_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.gcc_except_table : { + *(.gcc_except_table) +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.mmu_tbl (ALIGN(16384)) : { + __mmu_tbl_start = .; + *(.mmu_tbl) + __mmu_tbl_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + *(.gnu.linkonce.armexidix.*.*) + __exidx_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.preinit_array : { + __preinit_array_start = .; + KEEP (*(SORT(.preinit_array.*))) + KEEP (*(.preinit_array)) + __preinit_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.init_array : { + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.fini_array : { + __fini_array_start = .; + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array)) + __fini_array_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.ARM.attributes : { + __ARM.attributes_start = .; + *(.ARM.attributes) + __ARM.attributes_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sdata : { + __sdata_start = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + __sdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.sbss (NOLOAD) : { + __sbss_start = .; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + __sbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tdata : { + __tdata_start = .; + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + __tdata_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.tbss : { + __tbss_start = .; + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + __tbss_end = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.bss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); + +_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(16); + _heap = .; + HeapBase = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; + HeapLimit = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.stack (NOLOAD) : { + . = ALIGN(16); + _stack_end = .; + . += _STACK_SIZE; + _stack = .; + __stack = _stack; + . = ALIGN(16); + _irq_stack_end = .; + . += _IRQ_STACK_SIZE; + __irq_stack = .; + _supervisor_stack_end = .; + . += _SUPERVISOR_STACK_SIZE; + . = ALIGN(16); + __supervisor_stack = .; + _abort_stack_end = .; + . += _ABORT_STACK_SIZE; + . = ALIGN(16); + __abort_stack = .; + _fiq_stack_end = .; + . += _FIQ_STACK_SIZE; + . = ALIGN(16); + __fiq_stack = .; + _undef_stack_end = .; + . += _UNDEF_STACK_SIZE; + . = ALIGN(16); + __undef_stack = .; +} > psu_r5_tcm_ram_0_S_AXI_BASEADDR + +.resource_table 0x3ed20000 : { + . = ALIGN(4); + *(.resource_table) +} > psu_ddr_S_AXI_BASEADDR + +_end = .; +} diff --git a/apps/system/linux/CMakeLists.txt b/apps/system/linux/CMakeLists.txt new file mode 100644 index 0000000..ea643a4 --- /dev/null +++ b/apps/system/linux/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory (machine) diff --git a/apps/system/linux/machine/CMakeLists.txt b/apps/system/linux/machine/CMakeLists.txt new file mode 100644 index 0000000..9720224 --- /dev/null +++ b/apps/system/linux/machine/CMakeLists.txt @@ -0,0 +1,3 @@ +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") + add_subdirectory (${PROJECT_MACHINE}) +endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE}/CMakeLists.txt") diff --git a/apps/system/linux/machine/generic/CMakeLists.txt b/apps/system/linux/machine/generic/CMakeLists.txt new file mode 100644 index 0000000..5bee86b --- /dev/null +++ b/apps/system/linux/machine/generic/CMakeLists.txt @@ -0,0 +1,6 @@ +collect (APP_COMMON_SOURCES helper.c) +collect (APP_COMMON_SOURCES rsc_table.c) +collect (APP_COMMON_SOURCES platform_info.c) + +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") + diff --git a/apps/system/linux/machine/generic/helper.c b/apps/system/linux/machine/generic/helper.c new file mode 100644 index 0000000..d7e1c40 --- /dev/null +++ b/apps/system/linux/machine/generic/helper.c @@ -0,0 +1,31 @@ + +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include +#include + + +int init_system() +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_param.log_level = LOG_DEBUG; + metal_init(&metal_param); + + return 0; +} + +void cleanup_system() +{ + metal_finish(); +} diff --git a/apps/system/linux/machine/generic/platform_info.c b/apps/system/linux/machine/generic/platform_info.c new file mode 100644 index 0000000..ea3adc5 --- /dev/null +++ b/apps/system/linux/machine/generic/platform_info.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file implements APIs to get platform specific + * information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" + +#define DEV_BUS_NAME "platform" /* device bus name. "platform" bus + * is used in Linux kernel for generic + * devices */ +/* libmetal devices names used in the examples. + * They are platform devices, you find them in Linux sysfs + * sys/bus/platform/devices */ +#define SHM_DEV_NAME "90000000.shm" /* shared device name */ + +#define RSC_MEM_PA 0x90000000UL +#define RSC_MEM_SIZE 0x2000UL +#define SHARED_BUF_PA 0x90100000UL +#define SHARED_BUF_SIZE 0x00100000UL + +#define _rproc_wait() metal_cpu_yield() + +struct remoteproc_priv rproc_priv = { + .shm_name = SHM_DEV_NAME, + .shm_bus_name = DEV_BUS_NAME, +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +static struct remoteproc * +linux_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *dev; + metal_phys_addr_t mem_pa; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + rproc->priv = prproc; + rproc->ops = ops; + prproc->ipi_dev = NULL; + prproc->shm_dev = NULL; + /* Get shared memory device */ + ret = metal_device_open(prproc->shm_bus_name, prproc->shm_name, + &dev); + if (ret) { + fprintf(stderr, "ERROR: failed to open shm device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm device.\r\n"); + prproc->shm_dev = dev; + prproc->shm_io = metal_device_io_region(dev, 0); + if (!prproc->shm_io) + goto err2; + +#ifdef RPMSG_IPI +#ifdef RPMSG_NO_IPI + /* Get poll shared memory device */ + ret = metal_device_open(prproc->shm_poll_bus_name, + prproc->shm_poll_name, + &dev); + if (ret) { + fprintf(stderr, + "ERROR: failed to open shm poll device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm poll device.\r\n"); + prproc->shm_poll_dev = dev; + prproc->shm_poll_io = metal_device_io_region(dev, 0); + if (!prproc->shm_poll_io) + goto err2; + metal_io_write32(prproc->shm_poll_io, 0, !POLL_STOP); +#endif /* RPMSG_NO_IPI */ +#endif + + mem_pa = metal_io_phys(prproc->shm_io, 0); + remoteproc_init_mem(&prproc->shm_mem, "shm", mem_pa, mem_pa, + metal_io_region_size(prproc->shm_io), + prproc->shm_io); + remoteproc_add_mem(rproc, &prproc->shm_mem); + printf("Successfully added shared memory\r\n"); +#ifdef RPMSG_IPI + /* Get IPI device */ + ret = metal_device_open(prproc->ipi_bus_name, prproc->ipi_name, + &dev); + if (ret) { + printf("failed to open ipi device: %d.\r\n", ret); + goto err2; + } + prproc->ipi_dev = dev; + prproc->ipi_io = metal_device_io_region(dev, 0); + if (!prproc->ipi_io) + goto err3; + printf("Successfully probed IPI device\r\n"); + atomic_store(&prproc->ipi_nokick, 1); + metal_io_write32(prproc->ipi_io, IPI_IER_OFFSET, + prproc->ipi_chn_mask); +#endif /* #ifdef RPMSG_IPI */ + + printf("Successfully initialized Linux remoteproc.\r\n"); + return rproc; +#ifdef RPMSG_IPI +err3: + metal_device_close(prproc->ipi_dev); +#endif /* #ifdef RPMSG_IPI */ +err2: + metal_device_close(prproc->shm_dev); +err1: + return NULL; +} + + +static void linux_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + + if (!rproc) + return; + prproc = rproc->priv; +#ifdef RPMSG_IPI + metal_io_write32(prproc->ipi_io, IPI_IDR_OFFSET, prproc->ipi_chn_mask); + dev = prproc->ipi_dev; + if (dev) { + metal_irq_disable((uintptr_t)dev->irq_info); + metal_irq_unregister((uintptr_t)dev->irq_info); + metal_device_close(dev); + } +#endif /* #ifdef RPMSG_IPI */ + if (prproc->shm_dev) + metal_device_close(prproc->shm_dev); +} + +static void * +linux_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_priv *prproc; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + (void)attribute; + (void)size; + if (!rproc) + return NULL; + prproc = rproc->priv; + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + tmpio = prproc->shm_io; + if (!tmpio) + return NULL; + + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, lpa); +} + +static int linux_proc_notify(struct remoteproc *rproc, uint32_t id) +{ +#ifdef RPMSG_IPI + struct remoteproc_priv *prproc; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + +#ifdef RPMSG_NO_IPI + metal_io_write32(prproc->shm_poll_io, 0, POLL_STOP); +#else /* RPMSG_NO_IPI */ + metal_io_write32(prproc->ipi_io, IPI_TRIG_OFFSET, + prproc->ipi_chn_mask); +#endif /* !RPMSG_NO_IPI */ +#else + (void)rproc; + (void)id; +#endif /* #ifdef RPMSG_IPI */ + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +static struct remoteproc_ops linux_proc_ops = { + .init = linux_proc_init, + .remove = linux_proc_remove, + .mmap = linux_proc_mmap, + .notify = linux_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void)proc_index; + (void)rsc_index; + rsc_size = RSC_MEM_SIZE; + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &linux_proc_ops, + &rproc_priv)) + return NULL; + printf("Successfully initialized remoteproc\r\n"); + + /* Mmap resource table */ + pa = RSC_MEM_PA; + printf("Calling mmap resource table.\r\n"); + rsc_table = remoteproc_mmap(&rproc_inst, &pa, NULL, rsc_size, + 0, NULL); + if (!rsc_table) { + fprintf(stderr, "ERROR: Failed to mmap resource table.\r\n"); + return NULL; + } + printf("Successfully mmap resource table.\r\n"); +#define OPENAMP_HACK_RSC +#ifndef OPENAMP_HACK_RSC + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table, rsc_size); + if (ret) { + printf("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + printf("Successfully set resource table to remoteproc.\r\n"); +#else + printf("Skip setting resource table to remoteproc.\r\n"); +#endif /* #ifndef OPENAMP_HACK_RSC */ + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + fprintf(stderr, "Failed to initialize platform, NULL pointer" + "to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + fprintf(stderr, "Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + /* Setup resource table */ + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_BUF_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, SHARED_BUF_PA); + + printf("creating remoteproc virtio\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + + printf("initializing rpmsg shared buffer pool\r\n"); + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUF_SIZE); + + printf("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, + &shpool); + if (ret) { + printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + int ret; + + prproc = rproc->priv; + while(1) { +#ifdef RPMSG_NO_IPI + if (metal_io_read32(prproc->shm_poll_io, 0)) { + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } +#else + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->ipi_nokick))) { + metal_irq_restore_enable(flags); + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); +#endif /* RPMSG_NO_IPI */ + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev) +{ + (void)rpdev; +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/system/linux/machine/generic/platform_info.c.orig b/apps/system/linux/machine/generic/platform_info.c.orig new file mode 100644 index 0000000..0b0f721 --- /dev/null +++ b/apps/system/linux/machine/generic/platform_info.c.orig @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file implements APIs to get platform specific + * information for OpenAMP. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rsc_table.h" + +#define IPI_CHAN_NUMS 2 +#define IPI_CHAN_SEND 0 +#define IPI_CHAN_RECV 1 +#define UNIX_PREFIX "unix:" +#define UNIXS_PREFIX "unixs:" + +#define RSC_MEM_PA 0x0UL +#define SHARED_BUF_PA 0x10000UL +#define SHARED_BUF_SIZE 0x40000UL + +#define _rproc_wait() metal_cpu_yield() + +struct vring_ipi_info { + /* Socket file path */ + const char *path; + int fd; + atomic_int sync; +}; + +struct remoteproc_priv { + const char *shm_file; + int shm_size; + struct metal_io_region *shm_old_io; + struct metal_io_region shm_new_io; + struct remoteproc_mem shm; + struct vring_ipi_info ipi; +}; + +static struct remoteproc_priv rproc_priv_table [] = { + { + .shm_file = "openamp.shm", + .shm_size = 0x80000, + .ipi = { + .path = "unixs:/tmp/openamp.event.0", + }, + }, + { + .shm_file = "openamp.shm", + .shm_size = 0x80000, + .ipi = { + .path = "unix:/tmp/openamp.event.0", + }, + }, +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +static int linux_proc_block_read(struct metal_io_region *io, + unsigned long offset, + void *restrict dst, + memory_order order, + int len) +{ + void *src = metal_io_virt(io, offset); + + (void)order; + (void)memcpy(dst, src, len); + return len; +} + +static int linux_proc_block_write(struct metal_io_region *io, + unsigned long offset, + const void *restrict src, + memory_order order, + int len) +{ + void *dst = metal_io_virt(io, offset); + + (void)order; + (void)memcpy(dst, src, len); + return len; +} + +static void linux_proc_block_set(struct metal_io_region *io, + unsigned long offset, + unsigned char value, + memory_order order, + int len) +{ + void *dst = metal_io_virt(io, offset); + + (void)order; + (void)memset(dst, value, len); + return; +} + +static struct metal_io_ops linux_proc_io_ops = { + .write = NULL, + .read = NULL, + .block_read = linux_proc_block_read, + .block_write = linux_proc_block_write, + .block_set = linux_proc_block_set, + .close = NULL, +}; + +static int sk_unix_client(const char *descr) +{ + struct sockaddr_un addr; + int fd; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, descr + strlen(UNIX_PREFIX), + sizeof addr.sun_path); + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) >= 0) { + printf("connected to %s\r\n", descr + strlen(UNIX_PREFIX)); + return fd; + } + + close(fd); + return -1; +} + +static int sk_unix_server(const char *descr) +{ + struct sockaddr_un addr; + int fd, nfd; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, descr + strlen(UNIXS_PREFIX), + sizeof addr.sun_path); + unlink(addr.sun_path); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + goto fail; + } + + listen(fd, 5); + printf("Waiting for connection on %s\r\n", addr.sun_path); + nfd = accept(fd, NULL, NULL); + close(fd); + return nfd; +fail: + close(fd); + return -1; +} + +static inline int is_sk_unix_server(const char *descr) +{ + if (memcmp(UNIXS_PREFIX, descr, strlen(UNIXS_PREFIX))) + return 0; + else + return 1; +} + +static int event_open(const char *descr) +{ + int fd = -1; + int i; + + if (descr == NULL) { + return fd; + } + + if (!is_sk_unix_server(descr)) { + /* UNIX client. Retry to connect a few times to give the peer + * a chance to setup. */ + for (i = 0; i < 100 && fd == -1; i++) { + fd = sk_unix_client(descr); + if (fd == -1) + usleep(i * 10 * 1000); + } + } else { + /* UNIX server. */ + fd = sk_unix_server(descr); + } + printf("Open IPI: %s\r\n", descr); + return fd; +} + +static int linux_proc_irq_handler(int vect_id, void *data) +{ + char dummy_buf[32]; + struct vring_ipi_info *ipi = data; + + read(vect_id, dummy_buf, sizeof(dummy_buf)); + atomic_flag_clear(&ipi->sync); + return 0; +} + +static struct remoteproc * +linux_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_io_region *io; + struct remoteproc_mem *shm; + struct vring_ipi_info *ipi; + int ret; + + if (!rproc || !prproc) + return NULL; + rproc->priv = prproc; + /* Create shared memory io */ + ret = metal_shmem_open(prproc->shm_file, prproc->shm_size, &io); + if (ret) { + printf("Failed to init rproc, failed to open shm %s.\r\n", + prproc->shm_file); + return NULL; + } + prproc->shm_old_io = io; + shm = &prproc->shm; + shm->pa = 0; + shm->da = 0; + shm->size = prproc->shm_size; + metal_io_init(&prproc->shm_new_io, io->virt, &shm->pa, + shm->size, -1, 0, &linux_proc_io_ops); + shm->io = &prproc->shm_new_io; + + /* Open IPI */ + ipi = &prproc->ipi; + if (!ipi->path) { + fprintf(stderr, + "ERROR: No IPI sock path specified.\r\n"); + goto err; + } + ipi->fd = event_open(ipi->path); + if (ipi->fd < 0) { + fprintf(stderr, + "ERROR: Failed to open sock %s for IPI.\r\n", + ipi->path); + goto err; + } + metal_irq_register(ipi->fd, linux_proc_irq_handler, ipi); + metal_irq_enable(ipi->fd); + rproc->ops = ops; + return rproc; + +err: + return NULL; +} + + +static void linux_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct vring_ipi_info *ipi; + struct metal_io_region *io; + + if (!rproc) + return; + prproc = rproc->priv; + + /* Close IPI */ + ipi = &prproc->ipi; + if (ipi->fd >= 0) { + metal_irq_disable(ipi->fd); + metal_irq_unregister(ipi->fd); + close(ipi->fd); + } + + /* Close shared memory */ + io = prproc->shm_old_io; + if (io && io->ops.close) { + io->ops.close(io); + prproc->shm_old_io = NULL; + } +} + +static void * +linux_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_mem *mem; + struct remoteproc_priv *prproc; + metal_phys_addr_t lpa, lda; + void *va; + + (void)attribute; + (void)size; + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + + if (!rproc) + return NULL; + prproc = rproc->priv; + mem = &prproc->shm; + va = metal_io_phys_to_virt(mem->io, lpa); + if (va) { + if (io) + *io = mem->io; + metal_list_add_tail(&rproc->mems, &mem->node); + } + return va; +} + +static int linux_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + struct vring_ipi_info *ipi; + char dummy = 1; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + ipi = &prproc->ipi; + send(ipi->fd, &dummy, 1, MSG_NOSIGNAL); + return 0; +} + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +static struct remoteproc_ops linux_proc_ops = { + .init = linux_proc_init, + .remove = linux_proc_remove, + .mmap = linux_proc_mmap, + .notify = linux_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static int platform_slave_setup_resource_table(const char *shm_file, + int shm_size, + void *rsc_table, int rsc_size, + metal_phys_addr_t rsc_pa) +{ + struct metal_io_region *io; + void *rsc_shm; + int ret; + + ret = metal_shmem_open(shm_file, shm_size, &io); + if (ret) { + printf("Failed to init rproc, failed to open shm %s.\r\n", + shm_file); + return -1; + } + rsc_shm = metal_io_virt(io, rsc_pa); + memcpy(rsc_shm, rsc_table, rsc_size); + io->ops.close(io); + free(io); + return 0; +} + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + struct remoteproc_priv *prproc; + void *rsc_table, *rsc_table_shm; + int rsc_size; + int ret; + metal_phys_addr_t pa; + + (void)proc_index; + rsc_table = get_resource_table(rsc_index, &rsc_size); + + prproc = &rproc_priv_table[proc_index]; + /* Setup resource table + * This step can be done out of the application. + * Assumes the unix server side setup resource table. */ + if (is_sk_unix_server(prproc->ipi.path)) { + ret = platform_slave_setup_resource_table(prproc->shm_file, + prproc->shm_size, + rsc_table, rsc_size, + RSC_MEM_PA); + if (ret) { + printf("Failed to initialize resource table\r\n"); + return NULL; + } + } + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &linux_proc_ops, prproc)) + return NULL; + + /* Mmap resource table */ + pa = RSC_MEM_PA; + rsc_table_shm = remoteproc_mmap(&rproc_inst, &pa, NULL, rsc_size, + 0, &rproc_inst.rsc_io); + + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table_shm, rsc_size); + if (ret) { + printf("Failed to set resource table to remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + printf("Initialize remoteproc successfully.\r\n"); + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + fprintf(stderr, "Failed to initialize platform, NULL pointer" + "to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + fprintf(stderr, "Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + /* Setup resource table */ + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_BUF_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, SHARED_BUF_PA); + + printf("creating remoteproc virtio\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + + printf("initializing rpmsg shared buffer pool\r\n"); + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUF_SIZE); + + printf("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, + &shpool); + if (ret) { + printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + struct vring_ipi_info *ipi; + unsigned int flags; + + prproc = rproc->priv; + ipi = &prproc->ipi; + while(1) { + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&ipi->sync))) { + metal_irq_restore_enable(flags); + remoteproc_get_notification(rproc, RSC_NOTIFY_ID_ANY); + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev) +{ + (void)rpdev; +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/system/linux/machine/generic/platform_info.h b/apps/system/linux/machine/generic/platform_info.h new file mode 100644 index 0000000..f63c972 --- /dev/null +++ b/apps/system/linux/machine/generic/platform_info.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef PLATFORM_INFO_H +#define PLATFORM_INFO_H + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +struct remoteproc_priv { + const char *ipi_name; /**< IPI device name */ + const char *ipi_bus_name; /**< IPI bus name */ + const char *rsc_name; /**< rsc device name */ + const char *rsc_bus_name; /**< rsc bus name */ + const char *shm_name; /**< shared memory device name */ + const char *shm_bus_name; /**< shared memory bus name */ + struct metal_device *ipi_dev; /**< pointer to IPI device */ + struct metal_io_region *ipi_io; /**< pointer to IPI i/o region */ + struct metal_device *shm_dev; /**< pointer to shared memory device */ + struct metal_io_region *shm_io; /**< pointer to sh mem i/o region */ + + struct remoteproc_mem shm_mem; /**< shared memory */ + unsigned int ipi_chn_mask; /**< IPI channel mask */ + atomic_int ipi_nokick; +#ifdef RPMSG_NO_IPI + const char *shm_poll_name; /**< shared memory device name */ + const char *shm_poll_bus_name; /**< shared memory bus name */ + struct metal_device *shm_poll_dev; /**< pointer to poll mem device */ + struct metal_io_region *shm_poll_io; /**< pointer to poll mem i/o */ +#endif /* RPMSG_NO_IPI */ + +}; +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H */ diff --git a/apps/system/linux/machine/generic/rsc_table.c b/apps/system/linux/machine/generic/rsc_table.c new file mode 100644 index 0000000..b7a1934 --- /dev/null +++ b/apps/system/linux/machine/generic/rsc_table.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#include +#include "rsc_table.h" + +#define RPMSG_IPU_C0_FEATURES 1 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Remote supports Name Service announcement */ +#define VIRTIO_RPMSG_F_NS 0 + +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define RING_TX 0x00004000 +#define RING_RX 0x00008000 +#define VRING_SIZE 256 + +#define NUM_TABLE_ENTRIES 1 + +struct remote_resource_table resources = { + /* Version */ + 1, + + /* NUmber of table entries */ + NUM_TABLE_ENTRIES, + /* reserved fields */ + {0, 0,}, + + /* Offsets of rsc entries */ + { + offsetof(struct remote_resource_table, rpmsg_vdev), + }, + + /* Virtio device entry */ + { + RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +void *get_resource_table (int rsc_id, int *len) +{ + (void) rsc_id; + *len = sizeof(resources); + return &resources; +} diff --git a/apps/system/linux/machine/generic/rsc_table.h b/apps/system/linux/machine/generic/rsc_table.h new file mode 100644 index 0000000..010bc2b --- /dev/null +++ b/apps/system/linux/machine/generic/rsc_table.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef RSC_TABLE_H_ +#define RSC_TABLE_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define NO_RESOURCE_ENTRIES 1 + +/* Resource table for the given remote */ +struct remote_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}; + +void *get_resource_table (int rsc_id, int *len); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_H_ */ diff --git a/apps/system/linux/machine/rv64_virt/CMakeLists.txt b/apps/system/linux/machine/rv64_virt/CMakeLists.txt new file mode 100644 index 0000000..308c891 --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/CMakeLists.txt @@ -0,0 +1,9 @@ +collect (APP_COMMON_SOURCES platform_info.c) +collect (APP_COMMON_SOURCES rv64_virt_linux_proc.c) +collect (APP_COMMON_SOURCES helper.c) +collect (APP_COMMON_SOURCES rsc_table.c) +collect (APP_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}") +if (RPMSG_NO_IPI) + add_definitions(-DRPMSG_NO_IPI) +endif (RPMSG_NO_IPI) + diff --git a/apps/system/linux/machine/rv64_virt/helper.c b/apps/system/linux/machine/rv64_virt/helper.c new file mode 100644 index 0000000..54ca1e5 --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/helper.c @@ -0,0 +1,26 @@ + +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include + +int init_system() +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + + return 0; +} + +void cleanup_system() +{ + metal_finish(); +} diff --git a/apps/system/linux/machine/rv64_virt/platform_info.c b/apps/system/linux/machine/rv64_virt/platform_info.c new file mode 100644 index 0000000..d4bf597 --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/platform_info.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * Copyright (c) 2020 Bamvor Jian ZHANG + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define platform specific data and implements APIs to set + * platform specific information for OpenAMP. + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rsc_table.h" + +/** remote processsor private */ +struct remoteproc_priv rproc_priv = { + .shm_name = SHM_DEV_NAME, + .shm_bus_name = DEV_BUS_NAME, +#ifndef RPMSG_NO_IPI + .ipi_name = IPI_DEV_NAME, + .ipi_bus_name = DEV_BUS_NAME, + .ipi_chn_mask = IPI_CHN_BITMASK, +#endif /* !RPMSG_NO_IPI */ +#ifdef RPMSG_NO_IPI + .shm_poll_name = POLL_DEV_NAME, + .shm_poll_bus_name = DEV_BUS_NAME, +#endif /* RPMSG_NO_IPI */ +}; + +static struct remoteproc rproc_inst; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +#define _rproc_wait() metal_cpu_yield() + + +/* processor operations from r5 to a53. It defines + * notification operation and remote processor managementi operations. */ +extern struct remoteproc_ops rv64_virt_linux_proc_ops; + +/* RPMsg virtio shared buffer pool */ +static struct rpmsg_virtio_shm_pool shpool; + +static struct remoteproc * +platform_create_proc(int proc_index, int rsc_index) +{ + void *rsc_table; + int rsc_size; + int ret; + metal_phys_addr_t pa; + int rsc_len = 0; + + (void)proc_index; + (void)rsc_index; + rsc_size = RSC_MEM_SIZE; + + /* Initialize remoteproc instance */ + if (!remoteproc_init(&rproc_inst, &rv64_virt_linux_proc_ops, + &rproc_priv)) + return NULL; + printf("Successfully initialized remoteproc\r\n"); + + /* Mmap resource table */ + pa = RSC_MEM_PA; + printf("Calling mmap resource table.\r\n"); + rsc_table = remoteproc_mmap(&rproc_inst, &pa, NULL, rsc_size, + 0, NULL); + if (!rsc_table) { + fprintf(stderr, "ERROR: Failed to mmap resource table.\r\n"); + return NULL; + } + memcpy(rsc_table, get_resource_table(0, &rsc_len), rsc_len); + printf("Successfully mmap resource table.\r\n"); + /* parse resource table to remoteproc */ + ret = remoteproc_set_rsc_table(&rproc_inst, rsc_table, rsc_size); + if (ret) { + printf("Failed to intialize remoteproc\r\n"); + remoteproc_remove(&rproc_inst); + return NULL; + } + printf("Successfully set resource table to remoteproc.\r\n"); + + return &rproc_inst; +} + +int platform_init(int argc, char *argv[], void **platform) +{ + unsigned long proc_id = 0; + unsigned long rsc_id = 0; + struct remoteproc *rproc; + + if (!platform) { + fprintf(stderr, "Failed to initialize platform, NULL pointer" + "to store platform data.\r\n"); + return -EINVAL; + } + /* Initialize HW system components */ + init_system(); + + if (argc >= 2) { + proc_id = strtoul(argv[1], NULL, 0); + } + + if (argc >= 3) { + rsc_id = strtoul(argv[2], NULL, 0); + } + + rproc = platform_create_proc(proc_id, rsc_id); + if (!rproc) { + fprintf(stderr, "Failed to create remoteproc device.\r\n"); + return -EINVAL; + } + *platform = rproc; + return 0; +} + +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb) +{ + struct remoteproc *rproc = platform; + struct rpmsg_virtio_device *rpmsg_vdev; + struct virtio_device *vdev; + void *shbuf; + struct metal_io_region *shbuf_io; + int ret; + + rpmsg_vdev = metal_allocate_memory(sizeof(*rpmsg_vdev)); + if (!rpmsg_vdev) + return NULL; + shbuf_io = remoteproc_get_io_with_pa(rproc, SHARED_BUF_PA); + if (!shbuf_io) + return NULL; + shbuf = metal_io_phys_to_virt(shbuf_io, + SHARED_BUF_PA); + + printf("Creating virtio...\r\n"); + /* TODO: can we have a wrapper for the following two functions? */ + vdev = remoteproc_create_virtio(rproc, vdev_index, role, rst_cb); + if (!vdev) { + printf("failed remoteproc_create_virtio\r\n"); + goto err1; + } + printf("Successfully created virtio device.\r\n"); + + /* Only RPMsg virtio master needs to initialize the shared buffers pool */ + rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUF_SIZE); + + printf("initializing rpmsg vdev\r\n"); + /* RPMsg virtio slave can set shared buffers pool argument to NULL */ + ret = rpmsg_init_vdev(rpmsg_vdev, vdev, ns_bind_cb, + shbuf_io, &shpool); + if (ret) { + printf("failed rpmsg_init_vdev\r\n"); + goto err2; + } + return rpmsg_virtio_get_rpmsg_device(rpmsg_vdev); +err2: + remoteproc_remove_virtio(rproc, vdev); +err1: + metal_free_memory(rpmsg_vdev); + return NULL; +} + +int platform_poll(void *priv) +{ + struct remoteproc *rproc = priv; + struct remoteproc_priv *prproc; + unsigned int flags; + int ret; + + prproc = rproc->priv; + while(1) { +#ifdef RPMSG_NO_IPI + if (metal_io_read32(prproc->shm_poll_io, 0)) { + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } +#else + flags = metal_irq_save_disable(); + if (!(atomic_flag_test_and_set(&prproc->ipi_nokick))) { + metal_irq_restore_enable(flags); + ret = remoteproc_get_notification(rproc, + RSC_NOTIFY_ID_ANY); + if (ret) + return ret; + break; + } + _rproc_wait(); + metal_irq_restore_enable(flags); +#endif /* RPMSG_NO_IPI */ + } + return 0; +} + +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev, void *platform) +{ + struct rpmsg_virtio_device *rpvdev; + struct remoteproc *rproc; + + rpvdev = metal_container_of(rpdev, struct rpmsg_virtio_device, rdev); + rproc = platform; + + rpmsg_deinit_vdev(rpvdev); + remoteproc_remove_virtio(rproc, rpvdev->vdev); +} + +void platform_cleanup(void *platform) +{ + struct remoteproc *rproc = platform; + + if (rproc) + remoteproc_remove(rproc); + cleanup_system(); +} diff --git a/apps/system/linux/machine/rv64_virt/platform_info.h b/apps/system/linux/machine/rv64_virt/platform_info.h new file mode 100644 index 0000000..08933ab --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/platform_info.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef PLATFORM_INFO_H_ +#define PLATFORM_INFO_H_ + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define RPMSG_NO_IPI +#define RPU_CPU_ID 0 /* RPU remote CPU Index. We only talk to + * one CPU in the exmaple. We set the CPU + * index to 0. */ +//#define IPI_CHN_BITMASK 0x08 /* IPI channel bit mask for IPI +// * from/to RPU0 */ +//#define IPI_DEV_NAME "ff360000.ipi" /* IPI device name */ +#define DEV_BUS_NAME "platform" /* device bus name. "platform" bus + * is used in Linux kernel for generic + * devices */ +/* libmetal devices names used in the examples. + * They are platform devices, you find them in Linux sysfs + * sys/bus/platform/devices */ +#define SHM_DEV_NAME "90100000.shm" /* shared device name */ +//TODO: move to common +#define SHM_BASE 0x90100000UL + +#define RSC_MEM_PA SHM_BASE +#define RSC_MEM_SIZE 0x00020000UL +//shared buffer: 0x90120000--0x90160000 +#define SHARED_BUF_PA (SHM_BASE + RSC_MEM_SIZE) +#define SHARED_BUF_SIZE 0x00040000UL +//TODO: move to common end +#define RING_SIZE 0x00004000UL +//start: 0x90160000 +#define RING_TX (SHARED_BUF_PA + SHARED_BUF_SIZE) +//start: 0x90164000 +#define RING_RX (RING_TX + RING_SIZE) + +#ifdef RPMSG_NO_IPI +#define POLL_DEV_NAME "90000000.shm" /* shared device name */ +#define POLL_STOP 0x1U +#endif /* RPMSG_NO_IPI */ + +struct remoteproc_priv { + const char *ipi_name; /**< IPI device name */ + const char *ipi_bus_name; /**< IPI bus name */ + const char *rsc_name; /**< rsc device name */ + const char *rsc_bus_name; /**< rsc bus name */ + const char *shm_name; /**< shared memory device name */ + const char *shm_bus_name; /**< shared memory bus name */ + struct metal_device *ipi_dev; /**< pointer to IPI device */ + struct metal_io_region *ipi_io; /**< pointer to IPI i/o region */ + struct metal_device *shm_dev; /**< pointer to shared memory device */ + struct metal_io_region *shm_io; /**< pointer to sh mem i/o region */ + + struct remoteproc_mem shm_mem; /**< shared memory */ + unsigned int ipi_chn_mask; /**< IPI channel mask */ + atomic_int ipi_nokick; +#ifdef RPMSG_NO_IPI + const char *shm_poll_name; /**< shared memory device name */ + const char *shm_poll_bus_name; /**< shared memory bus name */ + struct metal_device *shm_poll_dev; /**< pointer to poll mem device */ + struct metal_io_region *shm_poll_io; /**< pointer to poll mem i/o */ +#endif /* RPMSG_NO_IPI */ + +}; + + +/** + * platform_init - initialize the platform + * + * It will initialize the platform. + * + * @argc: number of arguments + * @argv: array of the input arguements + * @platform: pointer to store the platform data pointer + * + * return 0 for success or negative value for failure + */ +int platform_init(int argc, char *argv[], void **platform); + +/** + * platform_create_rpmsg_vdev - create rpmsg vdev + * + * It will create rpmsg virtio device, and returns the rpmsg virtio + * device pointer. + * + * @platform: pointer to the private data + * @vdev_index: index of the virtio device, there can more than one vdev + * on the platform. + * @role: virtio master or virtio slave of the vdev + * @rst_cb: virtio device reset callback + * @ns_bind_cb: rpmsg name service bind callback + * + * return pointer to the rpmsg virtio device + */ +struct rpmsg_device * +platform_create_rpmsg_vdev(void *platform, unsigned int vdev_index, + unsigned int role, + void (*rst_cb)(struct virtio_device *vdev), + rpmsg_ns_bind_cb ns_bind_cb); + +/** + * platform_poll - platform poll function + * + * @platform: pointer to the platform + * + * return negative value for errors, otherwise 0. + */ +int platform_poll(void *platform); + +/** + * platform_release_rpmsg_vdev - release rpmsg virtio device + * + * @rpdev: pointer to the rpmsg device + */ +void platform_release_rpmsg_vdev(struct rpmsg_device *rpdev, void *platform); + +/** + * platform_cleanup - clean up the platform resource + * + * @platform: pointer to the platform + */ +void platform_cleanup(void *platform); + +#if defined __cplusplus +} +#endif + +#endif /* PLATFORM_INFO_H_ */ + diff --git a/apps/system/linux/machine/rv64_virt/rsc_table.c b/apps/system/linux/machine/rv64_virt/rsc_table.c new file mode 100644 index 0000000..09860aa --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/rsc_table.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * Copyright (c) 2020 Bamvor Jian ZHANG. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "platform_info.h" +#include "rsc_table.h" + +#define RPMSG_IPU_C0_FEATURES 1 + +/* VirtIO rpmsg device id */ +#define VIRTIO_ID_RPMSG_ 7 + +/* Remote supports Name Service announcement */ +#define VIRTIO_RPMSG_F_NS 0 + +#define NUM_VRINGS 0x02 +#define VRING_ALIGN 0x1000 +#define VRING_SIZE 256 + +#define NUM_TABLE_ENTRIES 1 + +struct remote_resource_table resources = { + /* Version */ + 1, + + /* NUmber of table entries */ + NUM_TABLE_ENTRIES, + /* reserved fields */ + {0, 0,}, + + /* Offsets of rsc entries */ + { + offsetof(struct remote_resource_table, rpmsg_vdev), + }, + + /* Virtio device entry */ + { + RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0, + NUM_VRINGS, {0, 0}, + }, + + /* Vring rsc entry - part of vdev rsc entry */ + {RING_TX, VRING_ALIGN, VRING_SIZE, 1, 0}, + {RING_RX, VRING_ALIGN, VRING_SIZE, 2, 0}, +}; + +void *get_resource_table (int rsc_id, int *len) +{ + (void) rsc_id; + *len = sizeof(resources); + return &resources; +} diff --git a/apps/system/linux/machine/rv64_virt/rsc_table.h b/apps/system/linux/machine/rv64_virt/rsc_table.h new file mode 100644 index 0000000..52527d6 --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/rsc_table.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file populates resource table for BM remote + * for use by the Linux Master */ + +#ifndef RSC_TABLE_H_ +#define RSC_TABLE_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define NO_RESOURCE_ENTRIES 8 + +/* Resource table for the given remote */ +struct remote_resource_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + unsigned int offset[NO_RESOURCE_ENTRIES]; + /* rpmsg vdev entry */ + struct fw_rsc_vdev rpmsg_vdev; + struct fw_rsc_vdev_vring rpmsg_vring0; + struct fw_rsc_vdev_vring rpmsg_vring1; +}; + +void *get_resource_table (int rsc_id, int *len); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_H_ */ diff --git a/apps/system/linux/machine/rv64_virt/rv64_virt_linux_proc.c b/apps/system/linux/machine/rv64_virt/rv64_virt_linux_proc.c new file mode 100644 index 0000000..92b56ea --- /dev/null +++ b/apps/system/linux/machine/rv64_virt/rv64_virt_linux_proc.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2017 Xilinx, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * FILE NAME + * + * platform_info.c + * + * DESCRIPTION + * + * This file define Xilinx ZynqMP R5 to A53 platform specific + * remoteproc implementation. + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_info.h" + +#ifndef RPMSG_NO_IPI +/* 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 */ + +static int rv64_virt_linux_proc_irq_handler(int vect_id, void *data) +{ + struct remoteproc *rproc = data; + struct remoteproc_priv *prproc; + unsigned int ipi_intr_status; + + (void)vect_id; + if (!rproc) + return METAL_IRQ_NOT_HANDLED; + prproc = rproc->priv; + ipi_intr_status = (unsigned int)metal_io_read32(prproc->ipi_io, + IPI_ISR_OFFSET); + if (ipi_intr_status & prproc->ipi_chn_mask) { + atomic_flag_clear(&prproc->ipi_nokick); + metal_io_write32(prproc->ipi_io, IPI_ISR_OFFSET, + prproc->ipi_chn_mask); + return METAL_IRQ_HANDLED; + } + return METAL_IRQ_NOT_HANDLED; +} +#endif /* #ifndef RPMSG_NO_IPI */ + +static struct remoteproc * +rv64_virt_linux_proc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg) +{ + struct remoteproc_priv *prproc = arg; + struct metal_device *dev; + unsigned int irq_vect; + metal_phys_addr_t mem_pa; + int ret; + + if (!rproc || !prproc || !ops) + return NULL; + rproc->priv = prproc; + rproc->ops = ops; + prproc->ipi_dev = NULL; + prproc->shm_dev = NULL; + /* Get shared memory device */ + ret = metal_device_open(prproc->shm_bus_name, prproc->shm_name, + &dev); + if (ret) { + fprintf(stderr, "ERROR: failed to open shm device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm device.\r\n"); + prproc->shm_dev = dev; + prproc->shm_io = metal_device_io_region(dev, 0); + if (!prproc->shm_io) + goto err2; + +#ifdef RPMSG_NO_IPI + /* Get poll shared memory device */ + ret = metal_device_open(prproc->shm_poll_bus_name, + prproc->shm_poll_name, + &dev); + if (ret) { + fprintf(stderr, + "ERROR: failed to open shm poll device: %d.\r\n", + ret); + goto err1; + } + printf("Successfully open shm poll device.\r\n"); + prproc->shm_poll_dev = dev; + prproc->shm_poll_io = metal_device_io_region(dev, 0); + if (!prproc->shm_poll_io) + goto err2; + metal_io_write32(prproc->shm_poll_io, 0, !POLL_STOP); +#endif /* RPMSG_NO_IPI */ + + mem_pa = metal_io_phys(prproc->shm_io, 0); + remoteproc_init_mem(&prproc->shm_mem, "shm", mem_pa, mem_pa, + metal_io_region_size(prproc->shm_io), + prproc->shm_io); + remoteproc_add_mem(rproc, &prproc->shm_mem); + printf("Successfully added shared memory\r\n"); +#ifndef RPMSG_NO_IPI + /* Get IPI device */ + ret = metal_device_open(prproc->ipi_bus_name, prproc->ipi_name, + &dev); + if (ret) { + printf("failed to open ipi device: %d.\r\n", ret); + goto err2; + } + prproc->ipi_dev = dev; + prproc->ipi_io = metal_device_io_region(dev, 0); + if (!prproc->ipi_io) + goto err3; + printf("Successfully probed IPI device\r\n"); + atomic_store(&prproc->ipi_nokick, 1); + + /* Register interrupt handler and enable interrupt */ + irq_vect = (uintptr_t)dev->irq_info; + metal_irq_register(irq_vect, rv64_virt_linux_proc_irq_handler, rproc); + metal_irq_enable(irq_vect); + metal_io_write32(prproc->ipi_io, IPI_IER_OFFSET, + prproc->ipi_chn_mask); +#endif /* #ifndef RPMSG_NO_IPI */ + printf("Successfully initialized remoteproc.\r\n"); + return rproc; +#ifndef RPMSG_NO_IPI +err3: + metal_device_close(prproc->ipi_dev); +#endif /* #ifndef RPMSG_NO_IPI */ +err2: + metal_device_close(prproc->shm_dev); +err1: + return NULL; +} + +static void rv64_virt_linux_proc_remove(struct remoteproc *rproc) +{ + struct remoteproc_priv *prproc; + struct metal_device *dev; + + if (!rproc) + return; + prproc = rproc->priv; +#ifndef RPMSG_NO_IPI + metal_io_write32(prproc->ipi_io, IPI_IDR_OFFSET, prproc->ipi_chn_mask); + dev = prproc->ipi_dev; + if (dev) { + metal_irq_disable((uintptr_t)dev->irq_info); + metal_irq_unregister((uintptr_t)dev->irq_info); + metal_device_close(dev); + } +#endif /* #ifndef RPMSG_NO_IPI */ + if (prproc->shm_dev) + metal_device_close(prproc->shm_dev); +} + +static void * +rv64_virt_linux_proc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, + unsigned int attribute, struct metal_io_region **io) +{ + struct remoteproc_priv *prproc; + metal_phys_addr_t lpa, lda; + struct metal_io_region *tmpio; + + (void)attribute; + (void)size; + if (!rproc) + return NULL; + prproc = rproc->priv; + lpa = *pa; + lda = *da; + + if (lpa == METAL_BAD_PHYS && lda == METAL_BAD_PHYS) + return NULL; + if (lpa == METAL_BAD_PHYS) + lpa = lda; + if (lda == METAL_BAD_PHYS) + lda = lpa; + tmpio = prproc->shm_io; + if (!tmpio) + return NULL; + + *pa = lpa; + *da = lda; + if (io) + *io = tmpio; + return metal_io_phys_to_virt(tmpio, lpa); +} + +static int rv64_virt_linux_proc_notify(struct remoteproc *rproc, uint32_t id) +{ + struct remoteproc_priv *prproc; + + (void)id; + if (!rproc) + return -1; + prproc = rproc->priv; + +#ifdef RPMSG_NO_IPI + metal_io_write32(prproc->shm_poll_io, 0, POLL_STOP); +#else /* RPMSG_NO_IPI */ + metal_io_write32(prproc->ipi_io, IPI_TRIG_OFFSET, + prproc->ipi_chn_mask); +#endif /* !RPMSG_NO_IPI */ + return 0; +} + +struct remoteproc_ops rv64_virt_linux_proc_ops = { + .init = rv64_virt_linux_proc_init, + .remove = rv64_virt_linux_proc_remove, + .mmap = rv64_virt_linux_proc_mmap, + .notify = rv64_virt_linux_proc_notify, + .start = NULL, + .stop = NULL, + .shutdown = NULL, +}; diff --git a/apps/system/linux/machine/zynqmp/CMakeLists.txt b/apps/system/linux/machine/zynqmp/CMakeLists.txt new file mode 100644 index 0000000..3ff7853 --- /dev/null +++ b/apps/system/linux/machine/zynqmp/CMakeLists.txt @@ -0,0 +1,2 @@ +collect (APP_COMMON_SOURCES helper.c) + diff --git a/apps/system/linux/machine/zynqmp/helper.c b/apps/system/linux/machine/zynqmp/helper.c new file mode 100644 index 0000000..54ca1e5 --- /dev/null +++ b/apps/system/linux/machine/zynqmp/helper.c @@ -0,0 +1,26 @@ + +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include + +int init_system() +{ + struct metal_init_params metal_param = METAL_INIT_DEFAULTS; + + metal_init(&metal_param); + + return 0; +} + +void cleanup_system() +{ + metal_finish(); +} diff --git a/apps/tests/CMakeLists.txt b/apps/tests/CMakeLists.txt new file mode 100644 index 0000000..eac5840 --- /dev/null +++ b/apps/tests/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory (msg) diff --git a/apps/tests/msg/CMakeLists.txt b/apps/tests/msg/CMakeLists.txt new file mode 100644 index 0000000..982b6db --- /dev/null +++ b/apps/tests/msg/CMakeLists.txt @@ -0,0 +1,48 @@ + +set (_cflags "${CMAKE_C_FLAGS} ${APP_EXTRA_C_FLAGS} -fdata-sections -ffunction-sections") +set (_fw_dir "${APPS_SHARE_DIR}") + +collector_list (_list PROJECT_INC_DIRS) +collector_list (_app_list APP_INC_DIRS) +include_directories (${_list} ${_app_list} ${CMAKE_CURRENT_SOURCE_DIR}) + +collector_list (_list PROJECT_LIB_DIRS) +collector_list (_app_list APP_LIB_DIRS) +link_directories (${_list} ${_app_list}) + +get_property (_linker_opt GLOBAL PROPERTY APP_LINKER_OPT) +collector_list (_deps PROJECT_LIB_DEPS) + +set (OPENAMP_LIB open_amp) + +foreach (_app msg-test-rpmsg-ping msg-test-rpmsg-update msg-test-rpmsg-flood-ping) + collector_list (_sources APP_COMMON_SOURCES) + if (${_app} STREQUAL "msg-test-rpmsg-ping") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/rpmsg-ping.c") + elseif (${_app} STREQUAL "msg-test-rpmsg-update") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/rpmsg-update.c") + elseif (${_app} STREQUAL "msg-test-rpmsg-flood-ping") + list (APPEND _sources "${CMAKE_CURRENT_SOURCE_DIR}/rpmsg-flood-ping.c") + endif (${_app} STREQUAL "msg-test-rpmsg-ping") + + if (WITH_SHARED_LIB) + add_executable (${_app}-shared ${_sources}) + target_link_libraries (${_app}-shared ${OPENAMP_LIB}-shared ${_deps}) + install (TARGETS ${_app}-shared RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + if (${PROJECT_SYSTEM} STREQUAL "linux") + add_executable (${_app}-static ${_sources}) + target_link_libraries (${_app}-static ${OPENAMP_LIB}-static ${_deps}) + install (TARGETS ${_app}-static RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else (${PROJECT_SYSTEM}) + add_executable (${_app}.out ${_sources}) + set_source_files_properties(${_sources} PROPERTIES COMPILE_FLAGS "${_cflags}") + + target_link_libraries(${_app}.out -Wl,-Map=${_app}.map -Wl,--gc-sections ${_linker_opt} -Wl,--start-group ${OPENAMP_LIB}-static ${_deps} -Wl,--end-group) + + install (TARGETS ${_app}.out RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif (${PROJECT_SYSTEM} STREQUAL "linux" ) + endif (WITH_STATIC_LIB) +endforeach(_app) diff --git a/apps/tests/msg/rpmsg-flood-ping.c b/apps/tests/msg/rpmsg-flood-ping.c new file mode 100644 index 0000000..28edc66 --- /dev/null +++ b/apps/tests/msg/rpmsg-flood-ping.c @@ -0,0 +1,219 @@ +/* This is a test application to send rpmsgs in flood mode. + * That is it will keep sending messages until there is no available + * buffers. + */ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-ping.h" + +#define APP_EPT_ADDR 1024 +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +struct _payload { + unsigned long num; + unsigned long size; + unsigned char data[]; +}; + +static int err_cnt; + +#define PAYLOAD_MIN_SIZE 1 +#define NUMS_PACKAGES 0x100000 + +/* Globals */ +static struct rpmsg_endpoint lept; +static struct _payload *i_payload; +static int rnum = 0; +static int err_cnt = 0; +static int ept_deleted = 0; + +/* External functions */ +extern int init_system(); +extern void cleanup_system(); + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + struct _payload *r_payload = (struct _payload *)data; + unsigned char *r_buf, *i_buf; + unsigned int i; + + (void)ept; + (void)src; + (void)priv; + (void)len; + + if (r_payload->size == 0 || r_payload->size > 496) { + LPERROR(" Invalid size of package is received 0x%x.\r\n", + (unsigned int)r_payload->size); + err_cnt++; + return RPMSG_SUCCESS; + } + /* Validate data buffer integrity. */ + r_buf = (unsigned char*)r_payload->data; + i_buf = (unsigned char*)i_payload->data; + for (i = 0; i < (unsigned int)r_payload->size; i++) { + if (*r_buf != *i_buf) { + LPERROR("Data corruption %lu, size %lu\r\n", + r_payload->num, r_payload->size); + err_cnt++; + break; + } + r_buf++; + i_buf++; + } + rnum = r_payload->num + 1; + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&lept); + LPRINTF("echo test: service is destroyed\r\n"); + ept_deleted = 1; +} + +static void rpmsg_name_service_bind_cb(struct rpmsg_device *rdev, + const char *name, uint32_t dest) +{ + LPRINTF("new endpoint notification is received.\r\n"); + if (strcmp(name, RPMSG_SERVICE_NAME)) + LPERROR("Unexpected name service %s.\r\n", name); + else + (void)rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + APP_EPT_ADDR, dest, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app (struct rpmsg_device *rdev, void *priv) +{ + int ret; + int i, s, max_size; + int num_pkgs; + + LPRINTF(" 1 - Send data to remote core, retrieve the echo"); + LPRINTF(" and validate its integrity ..\r\n"); + + num_pkgs = NUMS_PACKAGES; + max_size = rpmsg_virtio_get_buffer_size(rdev); + if (max_size < 0) { + LPERROR("No avaiable buffer size.\r\n"); + return -1; + } + i_payload = (struct _payload *)metal_allocate_memory(max_size); + + if (!i_payload) { + LPERROR("memory allocation failed.\r\n"); + return -1; + } + max_size -= sizeof(struct _payload); + + /* Create RPMsg endpoint */ + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, APP_EPT_ADDR, + RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create RPMsg endpoint.\r\n"); + return ret; + } + + while (!is_rpmsg_ept_ready(&lept)) + platform_poll(priv); + LPRINTF("RPMSG endpoint is binded with remote.\r\n"); + + memset(&(i_payload->data[0]), 0xA5, max_size); + for (s = PAYLOAD_MIN_SIZE; s <= max_size; s++) { + int size; + + i_payload->size = s; + size = sizeof(struct _payload) + s; + LPRINTF("echo test: package size %d, num of packages: %d\r\n", + size, num_pkgs); + rnum = 0; + for (i = 0; i < num_pkgs; i++) { + i_payload->num = i; + while (!err_cnt && !ept_deleted) { + ret = rpmsg_trysend(&lept, i_payload, size); + if (ret == RPMSG_ERR_NO_BUFF) { + platform_poll(priv); + } else if (ret < 0) { + LPERROR("Failed to send data...\r\n"); + break; + } else { + break; + } + } + if (ret < 0 || err_cnt || ept_deleted) + break; + } + if (ret < 0) + break; + while (rnum < num_pkgs && !err_cnt && !ept_deleted) + platform_poll(priv); + + if (err_cnt || ept_deleted) + break; + } + + if (ept_deleted) + LPRINTF("Remote RPMsg endpoint is destroyed unexpected.\r\n"); + + LPRINTF("**********************************\r\n"); + LPRINTF(" Test Results: Error count = %d \r\n", err_cnt); + LPRINTF("**********************************\r\n"); + /* Destroy the RPMsg endpoint */ + rpmsg_destroy_ept(&lept); + LPRINTF("Quitting application .. Echo test end\r\n"); + + metal_free_memory(i_payload); + return 0; +} + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, + rpmsg_name_service_bind_cb); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/tests/msg/rpmsg-ping.c b/apps/tests/msg/rpmsg-ping.c new file mode 100644 index 0000000..ec9a9d5 --- /dev/null +++ b/apps/tests/msg/rpmsg-ping.c @@ -0,0 +1,205 @@ +/* This is a sample demonstration application that showcases usage of rpmsg +This application is meant to run on the remote CPU running baremetal code. +This application echoes back data that was sent to it by the master core. */ + +#include +#include +#include +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-ping.h" + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +struct _payload { + unsigned long num; + unsigned long size; + unsigned char data[]; +}; + +static int err_cnt; + +#define PAYLOAD_MIN_SIZE 1 + +/* Globals */ +static struct rpmsg_endpoint lept; +static struct _payload *i_payload; +static int rnum = 0; +static int err_cnt = 0; +static int ept_deleted = 0; + +/* External functions */ +extern int init_system(); +extern void cleanup_system(); + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + int i; + struct _payload *r_payload = (struct _payload *)data; + + (void)ept; + (void)src; + (void)priv; + LPRINTF(" received payload number %lu of size %lu \r\n", + r_payload->num, (unsigned long)len); + + if (r_payload->size == 0) { + LPERROR(" Invalid size of package is received.\r\n"); + err_cnt++; + return RPMSG_SUCCESS; + } + /* Validate data buffer integrity. */ + for (i = 0; i < (int)r_payload->size; i++) { + if (r_payload->data[i] != 0xA5) { + LPRINTF("Data corruption at index %d\r\n", i); + err_cnt++; + break; + } + } + rnum = r_payload->num + 1; + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + rpmsg_destroy_ept(&lept); + LPRINTF("echo test: service is destroyed\r\n"); + ept_deleted = 1; +} + +static void rpmsg_name_service_bind_cb(struct rpmsg_device *rdev, + const char *name, uint32_t dest) +{ + LPRINTF("new endpoint notification is received.\r\n"); + if (strcmp(name, RPMSG_SERVICE_NAME)) + LPERROR("Unexpected name service %s.\r\n", name); + else + (void)rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, dest, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int app (struct rpmsg_device *rdev, void *priv) +{ + int ret; + int i; + int size, max_size, num_payloads; + int expect_rnum = 0; + + LPRINTF(" 1 - Send data to remote core, retrieve the echo"); + LPRINTF(" and validate its integrity ..\r\n"); + + max_size = rpmsg_virtio_get_buffer_size(rdev); + if (max_size < 0) { + LPERROR("No avaiable buffer size.\r\n"); + return -1; + } + max_size -= sizeof(struct _payload); + num_payloads = max_size - PAYLOAD_MIN_SIZE + 1; + i_payload = + (struct _payload *)metal_allocate_memory(2 * sizeof(unsigned long) + + max_size); + + if (!i_payload) { + LPERROR("memory allocation failed.\r\n"); + return -1; + } + + /* Create RPMsg endpoint */ + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, rpmsg_service_unbind); + + if (ret) { + LPERROR("Failed to create RPMsg endpoint.\r\n"); + return ret; + } + + while (!is_rpmsg_ept_ready(&lept)) + platform_poll(priv); + + LPRINTF("RPMSG endpoint is binded with remote.\r\n"); + for (i = 0, size = PAYLOAD_MIN_SIZE; i < num_payloads; i++, size++) { + i_payload->num = i; + i_payload->size = size; + + /* Mark the data buffer. */ + memset(&(i_payload->data[0]), 0xA5, size); + + LPRINTF("sending payload number %lu of size %lu\r\n", + i_payload->num, + (unsigned long)(2 * sizeof(unsigned long)) + size); + + ret = rpmsg_send(&lept, i_payload, + (2 * sizeof(unsigned long)) + size); + + if (ret < 0) { + LPERROR("Failed to send data...\r\n"); + break; + } + LPRINTF("echo test: sent : %lu\r\n", + (unsigned long)(2 * sizeof(unsigned long)) + size); + + expect_rnum++; + do { + platform_poll(priv); + } while ((rnum < expect_rnum) && !err_cnt && !ept_deleted); + + } + + LPRINTF("**********************************\r\n"); + LPRINTF(" Test Results: Error count = %d \r\n", err_cnt); + LPRINTF("**********************************\r\n"); + /* Destroy the RPMsg endpoint */ + rpmsg_destroy_ept(&lept); + LPRINTF("Quitting application .. Echo test end\r\n"); + + metal_free_memory(i_payload); + return 0; +} + +int main(int argc, char *argv[]) +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_MASTER, + NULL, + rpmsg_name_service_bind_cb); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} + diff --git a/apps/tests/msg/rpmsg-ping.h b/apps/tests/msg/rpmsg-ping.h new file mode 100644 index 0000000..b8a86dd --- /dev/null +++ b/apps/tests/msg/rpmsg-ping.h @@ -0,0 +1,6 @@ +#ifndef RPMSG_PING_H +#define RPMSG_PING_H + +#define RPMSG_SERVICE_NAME "rpmsg-openamp-demo-channel" + +#endif /* RPMSG_PING_H */ diff --git a/apps/tests/msg/rpmsg-update.c b/apps/tests/msg/rpmsg-update.c new file mode 100644 index 0000000..5590a60 --- /dev/null +++ b/apps/tests/msg/rpmsg-update.c @@ -0,0 +1,153 @@ +/* This is a sample demonstration application that showcases usage of rpmsg +This application is meant to run on the remote CPU running baremetal code. +This application echoes back data that was sent to it by the master core. */ + +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-ping.h" + +#define SHUTDOWN_MSG 0xEF56A55A + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + (void)priv; + (void)src; + + /* On reception of a shutdown we signal the application to terminate */ + if ((*(unsigned int *)data) == SHUTDOWN_MSG) { + LPRINTF("shutdown message is received.\r\n"); + shutdown_req = 1; + return RPMSG_SUCCESS; + } + + /* Send data back to master */ + while (1) { + int ret; + + ret = rpmsg_send(ept, data, len); + if (ret == RPMSG_ERR_NO_BUFF) { + LPRINTF("%s, wait for buffer\r\n", __func__); + continue; + } else { + if (ret < 0) + LPERROR("rpmsg_send, size %lu failed %d\r\n", + (unsigned long)len, ret); + break; + } + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPRINTF("unexpected Remote endpoint destroy\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int rpmsg_update_app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while(1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int __rpmsg_update(int argc, char *argv[]); + +int rpmsg_update() +{ + __rpmsg_update(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(rpmsg_update, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(rpmsg_update, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int __rpmsg_update(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); +#ifdef METAL_SYSTEM_RTTHREAD + //Make rt-thread happy + shutdown_req = 0; + memset(&lept, 0, sizeof(lept)); +#endif /* #ifdef FINSH_USING_MSH */ + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev,0); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/tests/msg/rpmsg-update.c.orig b/apps/tests/msg/rpmsg-update.c.orig new file mode 100644 index 0000000..659a65b --- /dev/null +++ b/apps/tests/msg/rpmsg-update.c.orig @@ -0,0 +1,148 @@ +/* This is a sample demonstration application that showcases usage of rpmsg +This application is meant to run on the remote CPU running baremetal code. +This application echoes back data that was sent to it by the master core. */ + +#include +#include +#include +#include "platform_info.h" +#include "rpmsg-ping.h" + +#define SHUTDOWN_MSG 0xEF56A55A + +#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +//#define LPRINTF(format, ...) +#define LPERROR(format, ...) LPRINTF("ERROR: " format, ##__VA_ARGS__) + +static struct rpmsg_endpoint lept; +static int shutdown_req = 0; + +/* External functions */ +extern int init_system(void); +extern void cleanup_system(void); + +/*-----------------------------------------------------------------------------* + * RPMSG endpoint callbacks + *-----------------------------------------------------------------------------*/ +static int rpmsg_endpoint_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + (void)priv; + (void)src; + + /* On reception of a shutdown we signal the application to terminate */ + if ((*(unsigned int *)data) == SHUTDOWN_MSG) { + LPRINTF("shutdown message is received.\r\n"); + shutdown_req = 1; + return RPMSG_SUCCESS; + } + + /* Send data back to master */ + while (1) { + int ret; + + ret = rpmsg_send(ept, data, len); + if (ret == RPMSG_ERR_NO_BUFF) { + LPRINTF("%s, wait for buffer\r\n", __func__); + continue; + } else { + if (ret < 0) + LPERROR("rpmsg_send, size %lu failed %d\r\n", + (unsigned long)len, ret); + break; + } + } + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + (void)ept; + LPRINTF("unexpected Remote endpoint destroy\r\n"); + shutdown_req = 1; +} + +/*-----------------------------------------------------------------------------* + * Application + *-----------------------------------------------------------------------------*/ +int rpmsg_update_app(struct rpmsg_device *rdev, void *priv) +{ + int ret; + + /* Initialize RPMSG framework */ + LPRINTF("Try to create rpmsg endpoint.\r\n"); + + ret = rpmsg_create_ept(&lept, rdev, RPMSG_SERVICE_NAME, + RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, + rpmsg_endpoint_cb, + rpmsg_service_unbind); + if (ret) { + LPERROR("Failed to create endpoint.\r\n"); + return -1; + } + + LPRINTF("Successfully created rpmsg endpoint.\r\n"); + while(1) { + platform_poll(priv); + /* we got a shutdown request, exit */ + if (shutdown_req) { + break; + } + } + rpmsg_destroy_ept(&lept); + + return 0; +} + +/*-----------------------------------------------------------------------------* + * Application entry point + *-----------------------------------------------------------------------------*/ +#ifdef METAL_SYSTEM_RTTHREAD +static int __rpmsg_update(int argc, char *argv[]); + +int rpmsg_update() +{ + __rpmsg_update(1, NULL); +} +#ifdef RT_USING_FINSH +FINSH_FUNCTION_EXPORT(rpmsg_update, rpmsg echo); +#endif /* #ifdef RT_USING_FINSH */ +#ifdef FINSH_USING_MSH +MSH_CMD_EXPORT(rpmsg_update, rpmsg echo); +#endif /* #ifdef FINSH_USING_MSH */ + +static int __rpmsg_update(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* #ifdef METAL_SYSTEM_RTTHREAD */ +{ + void *platform; + struct rpmsg_device *rpdev; + int ret; + + LPRINTF("Starting application...\r\n"); + + /* Initialize platform */ + ret = platform_init(argc, argv, &platform); + if (ret) { + LPERROR("Failed to initialize platform.\r\n"); + ret = -1; + } else { + rpdev = platform_create_rpmsg_vdev(platform, 0, + VIRTIO_DEV_SLAVE, + NULL, NULL); + if (!rpdev) { + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { + app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev); + ret = 0; + } + } + + LPRINTF("Stopping application...\r\n"); + platform_cleanup(platform); + + return ret; +} diff --git a/apps/tests/msg/rpmsg-update.c.rej b/apps/tests/msg/rpmsg-update.c.rej new file mode 100644 index 0000000..393986e --- /dev/null +++ b/apps/tests/msg/rpmsg-update.c.rej @@ -0,0 +1,11 @@ +--- apps/tests/msg/rpmsg-update.c ++++ apps/tests/msg/rpmsg-update.c +@@ -139,7 +156,7 @@ int main(int argc, char *argv[]) + LPERROR("Failed to create rpmsg virtio device.\r\n"); + ret = -1; + } else { +- app(rpdev, platform); ++ rpmsg_update_app(rpdev, platform); + platform_release_rpmsg_vdev(rpdev, platform); + ret = 0; + } diff --git a/apps/tests/msg/rpmsg-update.o b/apps/tests/msg/rpmsg-update.o new file mode 100644 index 0000000..45d26b4 Binary files /dev/null and b/apps/tests/msg/rpmsg-update.o differ diff --git a/cmake/collect.cmake b/cmake/collect.cmake new file mode 100644 index 0000000..d1bd178 --- /dev/null +++ b/cmake/collect.cmake @@ -0,0 +1,40 @@ +function (collector_create name base) + set_property (GLOBAL PROPERTY "COLLECT_${name}_LIST") + set_property (GLOBAL PROPERTY "COLLECT_${name}_BASE" "${base}") +endfunction (collector_create) + +function (collector_list var name) + get_property (_list GLOBAL PROPERTY "COLLECT_${name}_LIST") + set (${var} "${_list}" PARENT_SCOPE) +endfunction (collector_list) + +function (collector_base var name) + get_property (_base GLOBAL PROPERTY "COLLECT_${name}_BASE") + set (${var} "${_base}" PARENT_SCOPE) +endfunction (collector_base) + +function (collect name) + collector_base (_base ${name}) + string(COMPARE NOTEQUAL "${_base}" "" _is_rel) + set (_list) + foreach (s IN LISTS ARGN) + if (_is_rel) + get_filename_component (s "${s}" ABSOLUTE) + file (RELATIVE_PATH s "${_base}" "${s}") + else (_is_rel) + get_filename_component (ts "${s}" ABSOLUTE) + if (EXISTS "${ts}") + set (s "${ts}") + endif (EXISTS "${ts}") + endif (_is_rel) + list (APPEND _list "${s}") + endforeach () + set_property (GLOBAL APPEND PROPERTY "COLLECT_${name}_LIST" "${_list}") +endfunction (collect) + +# Create global collectors +collector_create (PROJECT_INC_DIRS "") +collector_create (PROJECT_LIB_DIRS "") +collector_create (PROJECT_LIB_DEPS "") + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/depends.cmake b/cmake/depends.cmake new file mode 100644 index 0000000..db27a60 --- /dev/null +++ b/cmake/depends.cmake @@ -0,0 +1,23 @@ +if (WITH_LIBMETAL_FIND) + find_package (Libmetal REQUIRED) + collect (PROJECT_INC_DIRS "${LIBMETAL_INCLUDE_DIR}") + collect (PROJECT_LIB_DIRS "${LIBMETAL_LIB_DIR}") + collect (PROJECT_LIB_DEPS "${LIBMETAL_LIB}") +endif (WITH_LIBMETAL_FIND) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + check_include_files (stdatomic.h HAVE_STDATOMIC_H) + check_include_files (fcntl.h HAVE_FCNTL_H) +else ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + set (_saved_cmake_required_flags ${CMAKE_REQUIRED_FLAGS}) + set (CMAKE_REQUIRED_FLAGS "-c") + check_include_files (stdatomic.h HAVE_STDATOMIC_H) + check_include_files (fcntl.h HAVE_FCNTL_H) + set (CMAKE_REQUIRED_FLAGS ${_saved_cmake_required_flags}) +endif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + +if (NOT HAVE_FCNTL_H) + unset (WITH_PROXY CACHE) +endif (NOT HAVE_FCNTL_H) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/modules/FindLibmetal.cmake b/cmake/modules/FindLibmetal.cmake new file mode 100644 index 0000000..7f7719a --- /dev/null +++ b/cmake/modules/FindLibmetal.cmake @@ -0,0 +1,31 @@ +# FindLibmetal +# -------- +# +# Find Libmetal +# +# Find the native Libmetal includes and library this module defines +# +# :: +# +# LIBMETAL_INCLUDE_DIR, where to find metal/sysfs.h, etc. +# LIBSYSFS_LIB_DIR, where to find libmetal library. + +# FIX ME, CMAKE_FIND_ROOT_PATH doesn't work +# even use the following +# set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) +# set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) +# set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +find_path(LIBMETAL_INCLUDE_DIR NAMES metal/sys.h PATHS ${CMAKE_FIND_ROOT_PATH}) +find_library(LIBMETAL_LIB NAMES metal PATHS ${CMAKE_FIND_ROOT_PATH}) +get_filename_component(LIBMETAL_LIB_DIR ${LIBMETAL_LIB} DIRECTORY) + +# handle the QUIETLY and REQUIRED arguments and set HUGETLBFS_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS (Libmetal DEFAULT_MSG LIBMETAL_LIB LIBMETAL_INCLUDE_DIR) + +if (LIBMETAL_FOUND) + set (LIBMETAL_LIBS ${LIBMETAL_LIB}) +endif (LIBMETAL_FOUND) + +mark_as_advanced (LIBMETAL_LIB LIBMETAL_INCLUDE_DIR LIBMETAL_LIB_DIR) diff --git a/cmake/options.cmake b/cmake/options.cmake new file mode 100644 index 0000000..6c367d7 --- /dev/null +++ b/cmake/options.cmake @@ -0,0 +1,82 @@ +set (PROJECT_VER_MAJOR 0) +set (PROJECT_VER_MINOR 1) +set (PROJECT_VER_PATCH 0) +set (PROJECT_VER 0.1.0) + +if (NOT DEFINED CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Debug) +endif (NOT DEFINED CMAKE_BUILD_TYPE) + +if (NOT CMAKE_INSTALL_LIBDIR) + set (CMAKE_INSTALL_LIBDIR "lib") +endif (NOT CMAKE_INSTALL_LIBDIR) + +if (NOT CMAKE_INSTALL_BINDIR) + set (CMAKE_INSTALL_BINDIR "bin") +endif (NOT CMAKE_INSTALL_BINDIR) + +set (_host "${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}") +message ("-- Host: ${_host}") + +set (_target "${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}") +message ("-- Target: ${_target}") + +if (NOT DEFINED MACHINE) + set (MACHINE "Generic") +endif (NOT DEFINED MACHINE) +message ("-- Machine: ${MACHINE}") + +string (TOLOWER ${CMAKE_SYSTEM_NAME} PROJECT_SYSTEM) +string (TOUPPER ${CMAKE_SYSTEM_NAME} PROJECT_SYSTEM_UPPER) +string (TOLOWER ${CMAKE_SYSTEM_PROCESSOR} PROJECT_PROCESSOR) +string (TOUPPER ${CMAKE_SYSTEM_PROCESSOR} PROJECT_PROCESSOR_UPPER) +string (TOLOWER ${MACHINE} PROJECT_MACHINE) +string (TOUPPER ${MACHINE} PROJECT_MACHINE_UPPER) + +# Select which components are in the openamp lib +option (WITH_PROXY "Build with proxy(access device controlled by other processor)" ON) +option (WITH_APPS "Build with sample applicaitons" OFF) +option (WITH_PROXY_APPS "Build with proxy sample applicaitons" OFF) +if (WITH_APPS) + if (WITH_PROXY) + set (WITH_PROXY_APPS ON) + endif (WITH_PROXY) +endif (WITH_APPS) + +# LOAD_FW only allowed for R5, otherwise turn off +if (NOT ${MACHINE} STREQUAL "zynqmp_r5") + set (WITH_LOAD_FW OFF) +endif(NOT ${MACHINE} STREQUAL "zynqmp_r5") + +option (WITH_VIRTIO_MASTER "Build with virtio master enabled" ON) +option (WITH_VIRTIO_SLAVE "Build with virtio slave enabled" ON) + +if (NOT WITH_VIRTIO_MASTER) + add_definitions(-DVIRTIO_SLAVE_ONLY) +endif (NOT WITH_VIRTIO_MASTER) + +if (NOT WITH_VIRTIO_SLAVE) + add_definitions(-DVIRTIO_MASTER_ONLY) +endif (NOT WITH_VIRTIO_SLAVE) + +# Set the complication flags +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") + +option (WITH_STATIC_LIB "Build with a static library" ON) + +if ("${PROJECT_SYSTEM}" STREQUAL "linux") + option (WITH_SHARED_LIB "Build with a shared library" ON) +endif ("${PROJECT_SYSTEM}" STREQUAL "linux") + +if (WITH_ZEPHYR) + option (WITH_ZEPHYR_LIB "Build open-amp as a zephyr library" OFF) +endif (WITH_ZEPHYR) + +option (WITH_LIBMETAL_FIND "Check Libmetal library can be found" ON) + +if (DEFINED RPMSG_BUFFER_SIZE) + add_definitions( -DRPMSG_BUFFER_SIZE=${RPMSG_BUFFER_SIZE} ) +endif (DEFINED RPMSG_BUFFER_SIZE) + +message ("-- C_FLAGS : ${CMAKE_C_FLAGS}") +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/cross_generic_gcc.cmake b/cmake/platforms/cross_generic_gcc.cmake new file mode 100644 index 0000000..99a0d71 --- /dev/null +++ b/cmake/platforms/cross_generic_gcc.cmake @@ -0,0 +1,12 @@ +set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "") + +include (CMakeForceCompiler) + +CMAKE_FORCE_C_COMPILER ("${CROSS_PREFIX}gcc" GNU) +CMAKE_FORCE_CXX_COMPILER ("${CROSS_PREFIX}g++" GNU) + +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "") +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER CACHE STRING "") +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER CACHE STRING "") + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/cross_linux_gcc.cmake b/cmake/platforms/cross_linux_gcc.cmake new file mode 100644 index 0000000..a7e2f6f --- /dev/null +++ b/cmake/platforms/cross_linux_gcc.cmake @@ -0,0 +1,9 @@ +set (CMAKE_SYSTEM_NAME "Linux") +set (CMAKE_C_COMPILER "${CROSS_PREFIX}gcc") +set (CMAKE_CXX_COMPILER "${CROSS_PREFIX}g++") + +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/zynq7_generic.cmake b/cmake/platforms/zynq7_generic.cmake new file mode 100644 index 0000000..a422196 --- /dev/null +++ b/cmake/platforms/zynq7_generic.cmake @@ -0,0 +1,8 @@ +set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "") +set (MACHINE "zynq7" CACHE STRING "") +set (CROSS_PREFIX "arm-none-eabi-" CACHE STRING "") +set (CMAKE_C_FLAGS "-mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard" CACHE STRING "") + +include (cross_generic_gcc) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/zynq7_linux.cmake b/cmake/platforms/zynq7_linux.cmake new file mode 100644 index 0000000..9a69973 --- /dev/null +++ b/cmake/platforms/zynq7_linux.cmake @@ -0,0 +1,6 @@ +set (CMAKE_SYSTEM_PROCESSOR "arm") +set (CROSS_PREFIX "arm-xilinx-linux-gnueabi-") + +include (cross-linux-gcc) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/zynqmp_a53_generic.cmake b/cmake/platforms/zynqmp_a53_generic.cmake new file mode 100644 index 0000000..8ece0cc --- /dev/null +++ b/cmake/platforms/zynqmp_a53_generic.cmake @@ -0,0 +1,6 @@ +set (CMAKE_SYSTEM_PROCESSOR "arm64") +set (CROSS_PREFIX "aarch64-none-elf-") + +include (cross_generic_gcc) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/zynqmp_linux.cmake b/cmake/platforms/zynqmp_linux.cmake new file mode 100644 index 0000000..bed0928 --- /dev/null +++ b/cmake/platforms/zynqmp_linux.cmake @@ -0,0 +1,7 @@ +set (CMAKE_SYSTEM_PROCESSOR "arm64") +set (CROSS_PREFIX "aarch64-linux-gnu-") +set (MACHINE "zynqmp" CACHE STRING "") + +include (cross_linux_gcc) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/platforms/zynqmp_r5_generic.cmake b/cmake/platforms/zynqmp_r5_generic.cmake new file mode 100644 index 0000000..5806a0d --- /dev/null +++ b/cmake/platforms/zynqmp_r5_generic.cmake @@ -0,0 +1,10 @@ +set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "") +set (MACHINE "zynqmp_r5" CACHE STRING "") +set (CROSS_PREFIX "armr5-none-eabi-" CACHE STRING "") + +# Xilinx SDK version earlier than 2017.2 use mfloat-abi=soft by default to generat libxil +set (CMAKE_C_FLAGS "-mfloat-abi=hard -mfpu=vfpv3-d16 -mcpu=cortex-r5" CACHE STRING "") + +include (cross_generic_gcc) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/cmake/syscheck.cmake b/cmake/syscheck.cmake new file mode 100644 index 0000000..b503b69 --- /dev/null +++ b/cmake/syscheck.cmake @@ -0,0 +1,13 @@ +# use "Generic" as CMAKE_SYSTEM_NAME + +if (WITH_ZEPHYR) + set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "") + string (TOLOWER "Zephyr" PROJECT_SYSTEM) + string (TOUPPER "Zephyr" PROJECT_SYSTEM_UPPER) + if (NOT WITH_ZEPHYR_LIB) + include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) + endif () + if (CONFIG_CPU_CORTEX_M) + set (MACHINE "cortexm" CACHE STRING "") + endif (CONFIG_CPU_CORTEX_M) +endif (WITH_ZEPHYR) diff --git a/docs/apps/echo_test/README.md b/docs/apps/echo_test/README.md new file mode 100644 index 0000000..a004bbf --- /dev/null +++ b/docs/apps/echo_test/README.md @@ -0,0 +1,58 @@ + +# echo_test +This readme is about the OpenAMP echo_test demo. +The echo_test is about one processor sends message to the other one, and the other one echo back the message. The processor which sends the message will verify the echo message. + +For now, it implements Linux sends the message, and the baremetal echos back. + +## Compilation + +### Baremetal Compilation +Option `WITH_ECHO_TEST` is to control if the application will be built. +By default this option is `ON` when `WITH_APPS` is on. + +Here is an example: + +``` +$ cmake ../open-amp -DCMAKE_TOOLCHAIN_FILE=zynq7_generic -DWITH_OBSOLETE=on -DWITH_APPS=ON +``` + +### Linux Compilation + +#### Linux Kernel Compilation +You will need to manually compile the following kernel modules with your Linux kernel (Please refer to Linux kernel documents for how to add kernel module): + +* Your machine's remoteproc kernel driver +* `obsolete/apps/echo_test/system/linux/kernelspace/rpmsg_user_dev_driver` if you want to run the echo_test app in Linux user space. +* `obsolete/system/linux/kernelspace/rpmsg_echo_test_kern_app` if you want to run the echo_test app in Linux kernel space. + +#### Linux Userspace Compliation +* Compile `obsolete/apps/echo_test/system/linux/userspace/echo_test` into your Linux OS. +* If you are running generic(baremetal) system as remoteproc slave, and Linux as remoteproc master, please also add the built generic `echo_test` executable to the firmware of your Linux OS. + +## Run the Demo + +### Load the Demo +After Linux boots, +* Load the machine remoteproc. If Linux runs as remoteproc master, you will need to pass the other processor's echo_test binary as firmware arguement to the remoteproc module. +* If you run the Linux kernel application demo, load the `rpmsg_echo_test_kern_app` module. You will see the kernel application send the message to remote and the remote reply back and the kernel application will verify the result. +* If you run the userspace application demo, load the `rpmsg_user_dev_driver` module. +* If you run the userspace application demo, you will see the similar output on the console: +``` +**************************************** + Please enter command and press enter key + **************************************** + 1 - Send data to remote core, retrieve the echo and validate its integrity .. + 2 - Quit this application .. + CMD> +``` +* Input `1` to send packages. +* Input `2` to exit the application. + +After you run the demo, you will need to unload the kernel modules. + +### Unload the Demo +* If you run the userspace application demo, unload the `rpmsg_user_dev_driver` module. +* If you run the kernelspace application demo, unload the `rpmsg_echo_test_kern_app` module. +* Unload the machine remoteproc driver. + diff --git a/docs/apps/matrix_multiply/README.md b/docs/apps/matrix_multiply/README.md new file mode 100644 index 0000000..ea4b08c --- /dev/null +++ b/docs/apps/matrix_multiply/README.md @@ -0,0 +1,59 @@ + +# matrix_multiply +This readme is about the OpenAMP matrix_multiply demo. +The matrix_multiply is about one processor generates two matrices, and send them to the one, and the other one calcuate the matrix multiplicaiton and return the result matrix. + +For now, it implements Linux generates the matrices, and the baremetal calculate the matrix mulitplication and send back the result. + +## Compilation + +### Baremetal Compilation +Option `WITH_MATRIX_MULTIPLY` is to control if the application will be built. +By default this option is `ON` when `WITH_APPS` is on. + +Here is an example: + +``` +$ cmake ../open-amp -DCMAKE_TOOLCHAIN_FILE=zynq7_generic -DWITH_OBSOLETE=on -DWITH_APPS=ON +``` + +### Linux Compilation + +#### Linux Kernel Compilation +You will need to manually compile the following kernel modules with your Linux kernel (Please refer to Linux kernel documents for how to add kernel module): + +* Your machine's remoteproc kernel driver +* `obsolete/system/linux/kernelspace/rpmsg_user_dev_driver` if you want to run the matrix_multiply app in Linux user space. +* `obsolete/apps/matrix_multiply/system/linux/kernelspace/rpmsg_mat_mul_kern_app` if you want to run the matrix_multiply app in Linux kernel space. + +#### Linux Userspace Compliation +* Compile `obsolete/apps/matrix_multiply/system/linux/userspace/mat_mul_demo` into your Linux OS. +* If you are running generic(baremetal) system as remoteproc slave, and Linux as remoteproc master, please also add the built generic `matrix_multiply` executable to the firmware of your Linux OS. + +## Run the Demo + +### Load the Demo +After Linux boots, +* Load the machine remoteproc. If Linux runs as remoteproc master, you will need to pass the other processor's matrix_multiply binary as firmware arguement to the remoteproc module. +* If you run the Linux kernel application demo, load the `rpmsg_mat_mul_kern_app` module, you will see the kernel app will generate two matrices to the other processor, and output the result matrix returned by the other processor. +* If you run the userspace application demo, load the `rpmsg_user_dev_driver` module. +* If you run the userspace application demo `mat_mul_demo`, you will see the similar output on the console: +``` +**************************************** +Please enter command and press enter key +**************************************** +1 - Generates random 6x6 matrices and transmits them to remote core over rpmsg +.. +2 - Quit this application .. +CMD> +``` +* Input `1` to run the matrix multiplication. +* Input `2` to exit the application. + +After you run the demo, you will need to unload the kernel modules. + +### Unload the Demo +* If you run the userspace application demo, unload the `rpmsg_user_dev_driver` module. +* If you run the kernelspace application demo, unload the `rpmsg_mat_mul_kern_app` module. +* Unload the machine remoteproc driver. + diff --git a/docs/apps/rpc_demo/README.md b/docs/apps/rpc_demo/README.md new file mode 100644 index 0000000..d393918 --- /dev/null +++ b/docs/apps/rpc_demo/README.md @@ -0,0 +1,38 @@ + +# rpc_demo +This readme is about the OpenAMP rpc_demo demo. +The rpc_demo is about one processor uses the UART on the other processor and create file on the other processor's filesystem with file operations. + +For now, It implements the processor running generic(baremetal) applicaiton access the devices on the Linux. + +## Compilation + +### Baremetal Compilation +Option `WITH_RPC_DEMO` is to control if the application will be built. +By default this option is `ON` when `WITH_APPS` is on. + +Here is an example: + +``` +$ cmake ../open-amp -DCMAKE_TOOLCHAIN_FILE=zynq7_generic -DWITH_OBSOLETE=on -DWITH_APPS=ON +``` + +### Linux Compilation + +#### Linux Kernel Compilation +You will need to manually compile the following kernel modules with your Linux kernel (Please refer to Linux kernel documents for how to add kernel module): + +* Your machine's remoteproc kernel driver +* `obsolete/apps/rpc_demo/system/linux/kernelspace/rpmsg_proxy_dev_driver` + +#### Linux Userspace Compliation +* Compile `obsolete/apps/rpc_demo/system/linux/userspace/proxy_app` into your Linux OS. +* Add the built generic `rpc_demo` executable to the firmware of your Linux OS. + +## Run the Demo +After Linux boots, run `proxy_app` as follows: +``` +# proxy_app [-m REMOTEPROC_MODULE] [-f PATH_OF_THE_RPC_DEMO_FIRMWARE] +``` + +The demo application will load the remoteproc module, then the proxy rpmsg module, will output message sent from the other processor, send the console input back to the other processor. When the demo application exits, it will unload the kernel modules. diff --git a/docs/data-structure.md b/docs/data-structure.md new file mode 100644 index 0000000..4ea6197 --- /dev/null +++ b/docs/data-structure.md @@ -0,0 +1,168 @@ +Libmetal helper data struct +=========================== +``` +struct metal_io_region { + char name[64]; /**< I/O region name */ + void *virt; /**< base virtual address */ + const metal_phys_addr_t *physmap; /**< table of base physical address + of each of the pages in the I/O + region */ + size_t size; /**< size of the I/O region */ + unsigned long page_shift; /**< page shift of I/O region */ + metal_phys_addr_t page_mask; /**< page mask of I/O region */ + unsigned int mem_flags; /**< memory attribute of the + I/O region */ + struct metal_io_ops ops; /**< I/O region operations */ +}; + + +/** Libmetal device structure. */ +struct metal_device { + const char *name; /**< Device name */ + struct metal_bus *bus; /**< Bus that contains device */ + unsigned num_regions; /**< Number of I/O regions in + device */ + struct metal_io_region regions[METAL_MAX_DEVICE_REGIONS]; /**< Array of + I/O regions in device*/ + struct metal_list node; /**< Node on bus' list of devices */ + int irq_num; /**< Number of IRQs per device */ + void *irq_info; /**< IRQ ID */ +}; +``` + +Remoteproc data struct +=========================== +``` +struct remoteproc { + struct metal_device dev; /**< Each remoteproc has a device, each device knows its memories regions */ + metal_mutex_t lock; /**< mutex lock */ + void *rsc_table; /**< pointer to resource table */ + size_t rsc_len; /**< length of the resoruce table */ + struct remoteproc_ops *ops; /**< pointer to remoteproc operation */ + metal_phys_addr_t bootaddr; /**< boot address */ + struct loader_ops *loader_ops; /**< image loader operation */ + unsigned int state; /**< remoteproc state */ + struct metal_list vdevs; /**< list of vdevs (can we limited to one for code size but linux and resource table supports multiple */ + void *priv; /**< remoteproc private data */ +}; + +struct remoteproc_vdev { + struct metal_list node; /**< node */ + struct remoteproc *rproc; /**< pointer to the remoteproc instance */ + struct virtio_dev; /**< virtio device */ + uint32_t notify_id; /**< virtio device notification ID */ + void *vdev_rsc; /**< pointer to the vdev space in resource table */ + struct metal_io_region *vdev_io; /**< pointer to the vdev space I/O region */ + int vrings_num; /**< number of vrings */ + struct rproc_vrings[1]; /**< vrings array */ +}; + +struct remoteproc_vring { + struct remoteproc_vdev *rpvdev; /**< pointer to the remoteproc vdev */ + uint32_t notify_id; /**< vring notify id */ + size_t len; /**< vring length */ + uint32_t alignment; /**< vring alignment */ + void *va; /**< vring start virtual address */ + struct metal_io_region *io; /**< pointer to the vring I/O region */ +}; +``` + +Virtio Data struct +=========================== +``` +struct virtio_dev { + int index; /**< unique position on the virtio bus */ + struct virtio_device_id id; /**< the device type identification (used to match it with a driver). */ + struct metal_device *dev; /**< do we need this in virtio device ? */ + metal_spinlock lock; /**< spin lock */ + uint64_t features; /**< the features supported by both ends. */ + unsigned int role; /**< if it is virtio backend or front end. */ + void (*rst_cb)(struct virtio_dev *vdev); /**< user registered virtio device callback */ + void *priv; /**< pointer to virtio_dev private data */ + int vrings_num; /**< number of vrings */ + struct virtqueue vqs[1]; /**< array of virtqueues */ +}; + +struct virtqueue { + char vq_name[VIRTQUEUE_MAX_NAME_SZ]; /**< virtqueue name */ + struct virtio_device *vdev; /**< pointer to virtio device */ + 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); /**< virtqueue callback */ + void (*notify) (struct virtqueue * vq); /**< virtqueue notify remote function */ + int vq_max_indirect_size; + int vq_indirect_mem_size; + struct vring vq_ring; + uint16_t vq_free_cnt; + uint16_t vq_queued_cnt; + struct metal_io_region *buffers_io; /**< buffers shared memory */ + + /* + * 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. + * Do we needed??/ + struct vq_desc_extra { + void *cookie; + struct vring_desc *indirect; + uint32_t indirect_paddr; + uint16_t ndescs; + } vq_descx[0]; +}; + +struct vring { + unsigned int num; /**< number of buffers of the vring */ + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; +``` +RPMsg Data struct +=========================== +``` +struct rpmsg_virtio_device { + struct virtio_dev *vdev; /**< pointer to the virtio device */ + struct virtqueue *rvq; /**< pointer to receive virtqueue */ + struct virtqueue *svq; /**< pointer to send virtqueue */ + int buffers_number; /**< number of shared buffers */ + struct metal_io_region *shbuf_io; /**< pointer to the shared buffer I/O region */ + void *shbuf; + int (*new_endpoint_cb)(const char *name, uint32_t addr); /**< name service announcement user designed callback which is used for when there is a name service announcement, there is no local endpoints waiting to bind */ + struct metal_list endpoints; /**< list of endpoints */ +}; + +struct rpmsg_endpoint { + char name[SERVICE_NAME_SIZE]; + struct rpmsg_virtio_dev *rvdev; /**< pointer to the RPMsg virtio device */ + uint32_t addr; /**< endpoint local address */ + uint32_t dest_addr; /**< endpoint default target address */ + int (*cb)(struct rpmsg_endpoint *ept, void *data, struct metal_io_region *io, size_t len, uint32_t addr); /**< endpoint callback */ + void (*destroy)(struct rpmsg_endpoint *ept); /**< user registerd endpoint destory callback */ + /* Whether we need another callback for ack ns announcement? */ +}; +``` diff --git a/docs/img-src/coprocessor-rpmsg-ns-dynamic.gv b/docs/img-src/coprocessor-rpmsg-ns-dynamic.gv new file mode 100644 index 0000000..6058f10 --- /dev/null +++ b/docs/img-src/coprocessor-rpmsg-ns-dynamic.gv @@ -0,0 +1,97 @@ +// RPMsg dynamic endpoint creation + +digraph G { + rankdir="LR"; + + subgraph roles { + node [style="filled", fillcolor="lightblue"]; + master [label="Master"]; + slave [label="Slave"]; + } + + subgraph m_comment_nodes { + node [group=m_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + m_remoteproc_init_comment [label="this is initialize rproc call"]; + m_remoteproc_boot_comment [label="it will setup vdev before booting the remote"]; + m_rpmsg_vdev_init_comment [label="\l* It will initialize vrings with the shared memory\l* If vdev supports name service, it will create name service endpoint;\l* it sets vdev status to DRVIER_READY, And will notify remote.\l"]; + m_rpmsg_create_ep_comment [label="\if vdev supports name service,\lit will send out name service.\l"]; + m_rpmsg_send_comment [label="\lIf endpoint hasn't binded, it fail\lreturn failure to indicate ep hasn't been binded.\l"]; + + } + + subgraph m_flow_nodes { + node [shape="box"]; + rank="same"; + m_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"] + m_remoteproc_load [label="calls remoteproc_load() to load applicaiton"]; + m_remoteproc_boot [shape="box", label="ret=remoteproc_boot(&rproc)"]; + m_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, MASTER, NULL);"]; + m_rpmsg_shmpool_init[label="rpmsg_virtio_init_shm_pool(shpool, shbuf, shbuf_pool_size);"]; + m_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, shpool);"]; + m_rpmsg_ns_cb [label="\lrpmsg_ns_callback() will see if there is a local ep registered.\lIf yes, bind the ep; otherwise, call ns_bind_cb.\l"]; + m_rpmsg_create_ep [label="\lept=rpmsg_create_ept(ept, rdev, ept_name, ept_addr, dest_addr, \lendpoint_cb, ns_unbind_cb);\l"]; + m_rpmsg_send [label="rpmsg_send(ept,data)"]; + m_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + m_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + m_rpmsg_destroy_ep [label="rpmsg_destroy_endpoint(ept)"]; + + m_remoteproc_init -> m_remoteproc_load -> m_remoteproc_boot -> m_remoteproc_get_vdev -> + m_rpmsg_shmpool_init -> m_rpmsg_vdev_init -> m_rpmsg_create_ep -> m_rpmsg_ns_cb -> m_rpmsg_send; + m_rpmsg_send -> m_rpmsg_rx_cb -> m_ep_cb -> + m_rpmsg_destroy_ep [dir="none", style="dashed"]; + } + + subgraph s_flow_nodes { + rank="same"; + node [shape="box"]; + s_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"]; + + s_remoteproc_parse_rsc [label="ret = remoteproc_set_rsc_table(rproc, &rsc_table, rsc_size)"]; + s_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, SLAVE, rst_cb);"]; + s_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, NULL);"]; + s_rpmsg_ns_cb [label="\lrpmsg_ns_callback() will see if there is a local ep registered.\lIf yes, bind the ep; otherwise, it will call ns_bind_cb()."]; + s_rpmsg_ns_bind_cb [label="s_rpsmg_ns_bind_cb(ept_name, remote_addr)"]; + s_rpmsg_create_ep [label="\lept=rpmsg_create_endpoint(ept, rdev, ept_name, ept_addr, remote_addr,\lendpoint_cb, ns_unbind_cb);\l"]; + s_rpmsg_send [label="rpmsg_send(ept,data)"]; + s_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + s_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + s_rpmsg_ns_unbind_cb [label="\lrpmsg_ns_callback() will call the previous\lregistered endpoint unbind callback\l"]; + + s_remoteproc_init -> s_remoteproc_parse_rsc -> s_remoteproc_get_vdev -> + s_rpmsg_vdev_init -> s_rpmsg_ns_cb -> s_rpmsg_ns_bind_cb -> + s_rpmsg_create_ep; + s_rpmsg_create_ep-> s_rpmsg_rx_cb -> s_ep_cb -> s_rpmsg_send -> + s_rpmsg_ns_unbind_cb [dir="none", style="dash"]; + + } + + subgraph s_comment_nodes { + node [group=s_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + s_rpmsg_vdev_init_comment [label="\l* If vdev supports name service, it will create name service endpoint;\l* It will not return until the master set status to DRIVER READY\l"]; + s_rpmsg_rx_cb_comment [label="\l* It will look for the endpoint which matches the destination address.\lIf the two endpoints hasn't binded yet,\lit will set the local endpoint's destination address with the source address in the message\l"]; + } + + master -> m_remoteproc_init [dir="none"]; + slave -> s_remoteproc_init [dir="none"]; + s_rpmsg_create_ep -> m_rpmsg_ns_cb [label="NS annoucement"]; + m_rpmsg_create_ep -> s_rpmsg_ns_cb [label="NS annoucement"]; + m_rpmsg_send -> s_rpmsg_rx_cb [label="RPMsg data"]; + s_rpmsg_send -> m_rpmsg_rx_cb [label="RPMsg data"]; + m_rpmsg_destroy_ep -> s_rpmsg_ns_unbind_cb [label="Endpoint destroy NS"]; + + m_remoteproc_init_comment -> m_remoteproc_init [dir="none"]; + m_remoteproc_boot_comment -> m_remoteproc_boot [dir="none"]; + m_rpmsg_vdev_init_comment -> m_rpmsg_vdev_init [dir="none"]; + m_rpmsg_create_ep_comment -> m_rpmsg_create_ep [dir="none"]; + m_rpmsg_send_comment -> m_rpmsg_send [dir="none"]; + + s_rpmsg_vdev_init -> s_rpmsg_vdev_init_comment [dir="none"]; + s_rpmsg_rx_cb -> s_rpmsg_rx_cb_comment [dir="none"]; + + {rank=same; master; m_remoteproc_init} + {rank=same; slave; s_remoteproc_init} + +} + diff --git a/docs/img-src/coprocessor-rpmsg-ns.gv b/docs/img-src/coprocessor-rpmsg-ns.gv new file mode 100644 index 0000000..6c9e10f --- /dev/null +++ b/docs/img-src/coprocessor-rpmsg-ns.gv @@ -0,0 +1,95 @@ +// RPMsg dynamic endpoints binding + +digraph G { + rankdir="LR"; + + subgraph roles { + node [style="filled", fillcolor="lightblue"]; + master [label="Master"]; + slave [label="Slave"]; + } + + subgraph m_comment_nodes { + node [group=m_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + m_remoteproc_init_comment [label="this is initialize rproc call"]; + m_remoteproc_boot_comment [label="it will setup vdev before booting the remote"]; + m_rpmsg_vdev_init_comment [label="\l* It will initialize vrings with the shared memory\l* As vdev doesn't support name service, it will not create name service endpoint;\l* it sets vdev status to DRVIER_READY, And will notify remote.\l"]; + m_rpmsg_create_ep_comment [label="\lIf vdev supports name service,\lit will send out name service.\l"]; + m_rpmsg_send_comment [label="\lIf endpoint hasn't binded, it fail\lreturn failure to indicate ep hasn't been binded.\l"]; + + } + + subgraph m_flow_nodes { + node [shape="box"]; + rank="same"; + m_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"] + m_remoteproc_load [label="calls remoteproc_load() to load applicaiton"]; + m_remoteproc_boot [shape="box", label="ret=remoteproc_boot(&rproc)"]; + m_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, MASTER, NULL);"]; + m_rpmsg_shmpool_init[label="rpmsg_virtio_init_shm_pool(shpool, shbuf, shbuf_pool_size);"]; + m_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, shpool);"]; + m_rpmsg_ns_cb [label="\lrpmsg_ns_callback() will see if there is a local ep registered.\lIf yes, bind the ep; otherwise, call ns_bind_cb.\l"]; + m_rpmsg_create_ep [label="\lept=rpmsg_create_ept(ept, rdev, ept_name, ept_addr, dest_addr, \lendpoint_cb, ns_unbind_cb);\l"]; + m_rpmsg_send [label="rpmsg_send(ept,data)"]; + m_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + m_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + m_rpmsg_destroy_ep [label="rpmsg_destroy_endpoint(ept)"]; + + m_remoteproc_init -> m_remoteproc_load -> m_remoteproc_boot -> m_remoteproc_get_vdev -> + m_rpmsg_shmpool_init -> m_rpmsg_vdev_init -> m_rpmsg_ns_cb -> m_rpmsg_create_ep -> m_rpmsg_send; + m_rpmsg_send -> m_rpmsg_rx_cb -> m_ep_cb -> + m_rpmsg_destroy_ep [dir="none", style="dashed"]; + } + + subgraph s_flow_nodes { + rank="same"; + node [shape="box"]; + s_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"]; + + s_remoteproc_parse_rsc [label="ret = remoteproc_set_rsc_table(rproc, &rsc_table, rsc_size)"]; + s_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, SLAVE, rst_cb);"]; + s_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, NULL);"]; + s_rpmsg_create_ep [label="\lept=rpmsg_create_ept(ept, rdev, ept_name, ept_addr, dest_addr, \lendpoint_cb, ns_unbind_cb);\l"]; + s_rpmsg_ns_cb [label="\lrpmsg_ns_callback() will see if there is a local ep registered.\lIf yes, bind the ep; otherwise, call ns_binc_cb.\l"]; + s_rpmsg_send [label="rpmsg_send(ept,data)"]; + s_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + s_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + s_rpmsg_ns_unbind_cb [label="\lrpmsg_ns_callback() will call the previous\lregistered endpoint unbind callback\l"]; + + s_remoteproc_init -> s_remoteproc_parse_rsc -> s_remoteproc_get_vdev -> + s_rpmsg_vdev_init -> s_rpmsg_create_ep; + s_rpmsg_create_ep -> s_rpmsg_ns_cb -> s_rpmsg_rx_cb -> + s_ep_cb -> s_rpmsg_send -> s_rpmsg_ns_unbind_cb [dir="none", style="dash"]; + + } + + subgraph s_comment_nodes { + node [group=s_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + s_rpmsg_vdev_init_comment [label="\l* If vdev supports name service, it will create name service endpoint;\l* It will not return until the master set status to DRIVER READY\l"]; + s_rpmsg_rx_cb_comment [label="\l* It will look for the endpoint which matches the destination address.\lIf the two endpoints hasn't binded yet,\lit will set the local endpoint's destination address with the source address in the message\l"]; + } + + master -> m_remoteproc_init [dir="none"]; + slave -> s_remoteproc_init [dir="none"]; + s_rpmsg_create_ep -> m_rpmsg_ns_cb [label="NS annoucement"]; + m_rpmsg_create_ep -> s_rpmsg_ns_cb [label="NS annoucement"]; + m_rpmsg_send -> s_rpmsg_rx_cb [label="RPMsg data"]; + s_rpmsg_send -> m_rpmsg_rx_cb [label="RPMsg data"]; + m_rpmsg_destroy_ep -> s_rpmsg_ns_unbind_cb [label="Endpoint destroy NS"]; + + m_remoteproc_init_comment -> m_remoteproc_init [dir="none"]; + m_remoteproc_boot_comment -> m_remoteproc_boot [dir="none"]; + m_rpmsg_vdev_init_comment -> m_rpmsg_vdev_init [dir="none"]; + m_rpmsg_create_ep_comment -> m_rpmsg_create_ep [dir="none"]; + m_rpmsg_send_comment -> m_rpmsg_send [dir="none"]; + + s_rpmsg_vdev_init -> s_rpmsg_vdev_init_comment [dir="none"]; + s_rpmsg_rx_cb -> s_rpmsg_rx_cb_comment [dir="none"]; + + {rank=same; master; m_remoteproc_init} + {rank=same; slave; s_remoteproc_init} + +} + diff --git a/docs/img-src/coprocessor-rpmsg-static-ep.gv b/docs/img-src/coprocessor-rpmsg-static-ep.gv new file mode 100644 index 0000000..ec95744 --- /dev/null +++ b/docs/img-src/coprocessor-rpmsg-static-ep.gv @@ -0,0 +1,87 @@ +// RPMsg static endpoints + +digraph G { + rankdir="LR"; + + subgraph roles { + node [style="filled", fillcolor="lightblue"]; + master [label="Master"]; + slave [label="Slave"]; + } + + subgraph m_comment_nodes { + node [group=m_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + m_remoteproc_init_comment [label="this is initialize rproc call"]; + m_remoteproc_boot_comment [label="it will setup vdev before booting the remote"]; + m_rpmsg_vdev_init_comment [label="\l* It will initialize vrings with the shared memory\l* As vdev doesn't support name service, it will not create name service endpoint;\l* it sets vdev status to DRVIER_READY, And will notify remote.\l"]; + m_rpmsg_create_ep_comment [label="\lAs vdev doesn't supports name service,\lit will not send out name service.\l"]; + } + + subgraph m_flow_nodes { + node [shape="box"]; + rank="same"; + m_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"]; + m_remoteproc_load [label="calls remoteproc_load() to load applicaiton"]; + m_remoteproc_boot [shape="box", label="ret=remoteproc_boot(&rproc)"]; + m_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, MASTER, NULL);"]; + m_rpmsg_shmpool_init[label="rpmsg_virtio_init_shm_pool(shpool, shbuf, shbuf_pool_size);"]; + m_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, shpool);"]; + m_rpmsg_create_ep [label="\lept=rpmsg_create_ept(ept, rdev, ept_name, ept_addr, dest_addr, \lendpoint_cb, ns_unbind_cb);\l"]; + m_rpmsg_send [label="rpmsg_send(ept,data)"]; + m_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + m_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + m_rpmsg_destroy_ep [label="rpmsg_destroy_endpoint(ept)"]; + + m_remoteproc_init -> m_remoteproc_load -> m_remoteproc_boot -> m_remoteproc_get_vdev -> + m_rpmsg_shmpool_init -> m_rpmsg_vdev_init -> m_rpmsg_create_ep -> m_rpmsg_send; + m_rpmsg_send -> m_rpmsg_rx_cb -> m_ep_cb -> + m_rpmsg_destroy_ep [dir="none", style="dashed"]; + } + + subgraph s_flow_nodes { + rank="same"; + node [shape="box"]; + s_remoteproc_init [label="rproc = remoteproc_init(&remoteproc_ops, &arg);"]; + + s_remoteproc_parse_rsc [label="ret = remoteproc_set_rsc_table(rproc, &rsc_table, rsc_size)"]; + s_remoteproc_get_vdev [label="vdev=remoteproc_create_virtio(rproc, rpmsg_vdev_id, SLAVE, rst_cb);"]; + s_rpmsg_vdev_init [label="rpdev=rpmsg_init_vdev(rpvdev, vdev, ns_bind_cb, &shm_io, NULL);"]; + s_rpmsg_create_ep [label="\lept=rpmsg_create_ept(ept, rdev, ept_name, ept_addr, dest_addr, \lendpoint_cb, ns_unbind_cb);\l"]; + s_rpmsg_send [label="rpmsg_send(ept,data)"]; + s_rpmsg_rx_cb [label="rpmsg_rx_callback()"]; + s_ep_cb [label="endpoint_cb(ept, data, size, src_addr)"]; + s_rpmsg_destroy_ep [label="rpmsg_destroy_endpoint(ept)"]; + + s_remoteproc_init -> s_remoteproc_parse_rsc -> s_remoteproc_get_vdev -> + s_rpmsg_vdev_init -> s_rpmsg_create_ep; + s_rpmsg_create_ep -> s_rpmsg_rx_cb -> + s_ep_cb -> s_rpmsg_send -> s_rpmsg_destroy_ep [dir="none", style="dash"]; + + } + + subgraph s_comment_nodes { + node [group=s_comment, shape="note", style="filled", fillcolor="yellow"]; + rank="same"; + s_rpmsg_vdev_init_comment [label="\l* As vdev doesn't support name service, it will not create name service endpoint;\l* It will not return until the master set status to DRIVER READY\l"]; + s_rpmsg_rx_cb_comment [label="\l* It will look for the endpoint which matches the destination address.\lIf no endpoint has found, it will drop the message.\l"]; + } + + master -> m_remoteproc_init [dir="none"]; + slave -> s_remoteproc_init [dir="none"]; + m_rpmsg_send -> s_rpmsg_rx_cb [label="RPMsg data"]; + s_rpmsg_send -> m_rpmsg_rx_cb [label="RPMsg data"]; + + m_remoteproc_init_comment -> m_remoteproc_init [dir="none"]; + m_remoteproc_boot_comment -> m_remoteproc_boot [dir="none"]; + m_rpmsg_vdev_init_comment -> m_rpmsg_vdev_init [dir="none"]; + m_rpmsg_create_ep_comment -> m_rpmsg_create_ep [dir="none"]; + + s_rpmsg_vdev_init -> s_rpmsg_vdev_init_comment [dir="none"]; + s_rpmsg_rx_cb -> s_rpmsg_rx_cb_comment [dir="none"]; + + {rank=same; master; m_remoteproc_init} + {rank=same; slave; s_remoteproc_init} + +} + diff --git a/docs/img-src/gen-graph.py b/docs/img-src/gen-graph.py new file mode 100644 index 0000000..481f1d3 --- /dev/null +++ b/docs/img-src/gen-graph.py @@ -0,0 +1,55 @@ +import argparse +import os +import pydot +import sys +import warnings + +def gen_graph_from_gv(ifile, odir, oformat="png"): + (graph,) = pydot.graph_from_dot_file(ifile) + gen_graph_func = getattr(graph, "write_" + oformat) + filename = os.path.basename(ifile) + ofile = odir + "/" + os.path.splitext(filename)[0] + "." + oformat + gen_graph_func(ofile) + +parser = argparse.ArgumentParser(description='Process some integers.') +parser.add_argument('-i', "--infile", action="append", + help="graphviz file path") +parser.add_argument('-o', '--outdir', + help='sum the integers (default: find the max)') +parser.add_argument('-f', '--outformat', default="png", + help='output image format (default: png)') + +args = parser.parse_args() + +# Image source directory +img_src_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + +img_files = [] +if args.infile: + for f in args.infile: + if not os.path.isfile(f): + f = img_src_dir + "/" + f + if not os.path.isfile(f): + warnings.warn("Input file: " + f + " doesn't exist.") + else: + img_files.append(f) +else: + for f in os.listdir(img_src_dir): + if f.endswith(".gv"): + img_files.append(img_src_dir + "/" + f) + +if not img_files: + sys.exit("ERROR: no found image files.") + +oformat = args.outformat + +if args.outdir: + odir = args.outdir + if not os.path.isdir(odir): + sys.exit("--outdir " + odir + "doesn't exist") +else: + odir = os.path.dirname(img_src_dir) + "/img" + +for f in img_files: + print("Generating " + oformat + " for " + f + " ...") + gen_graph_from_gv(f, odir, oformat) diff --git a/docs/img-src/rproc-lcm-state-machine.gv b/docs/img-src/rproc-lcm-state-machine.gv new file mode 100644 index 0000000..1faab82 --- /dev/null +++ b/docs/img-src/rproc-lcm-state-machine.gv @@ -0,0 +1,19 @@ +// Remoteproc Life Cycle Management State Machine + +digraph G { + rankdir="LR" + st_offline [label="Offline"] + st_configured [label="Configured"] + st_ready [label="Ready"] + st_running [label="Running"] + st_stopped [label="Stopped"] + + st_offline -> st_configured + st_configured -> st_ready + st_ready -> st_running + st_ready -> st_stopped + st_stopped -> st_offline + st_running -> st_stopped + + {rank=same; st_configured; st_ready; st_running} +} diff --git a/docs/img/coprocessor-rpmsg-ns-dynamic.png b/docs/img/coprocessor-rpmsg-ns-dynamic.png new file mode 100644 index 0000000..7fe1602 Binary files /dev/null and b/docs/img/coprocessor-rpmsg-ns-dynamic.png differ diff --git a/docs/img/coprocessor-rpmsg-ns.png b/docs/img/coprocessor-rpmsg-ns.png new file mode 100644 index 0000000..4c330d0 Binary files /dev/null and b/docs/img/coprocessor-rpmsg-ns.png differ diff --git a/docs/img/coprocessor-rpmsg-static-ep.png b/docs/img/coprocessor-rpmsg-static-ep.png new file mode 100644 index 0000000..4732069 Binary files /dev/null and b/docs/img/coprocessor-rpmsg-static-ep.png differ diff --git a/docs/img/rproc-lcm-state-machine.png b/docs/img/rproc-lcm-state-machine.png new file mode 100644 index 0000000..7e8a9d2 Binary files /dev/null and b/docs/img/rproc-lcm-state-machine.png differ diff --git a/docs/remoteproc-design.md b/docs/remoteproc-design.md new file mode 100644 index 0000000..e93f8d5 --- /dev/null +++ b/docs/remoteproc-design.md @@ -0,0 +1,103 @@ +# Remoteproc Design Document +Remoteproc provides abstraction to manage the life cycle of a remote +application. For now, it only provides APIs on bringing up and +tearing down the remote application, and parsing resource table. +It will extend to crash detection, suspend and resume. + +## Remoteproc LCM States +| State | State Description | +|:------|:------------------| +| Offline | Initial state of a remoteproc instance. The remote presented by the remoteproc instance and its resource has been powered off. | +| Configured | The remote presented by the remoteproc instance has been configured. And ready to load applicaiton. | +| Ready | The remote presented by the remoteproc instance has applicaiton loaded, and ready to run. | +| Stopped | The remote presented by the remoteproc instance has stopped from running. But the remote is still powered on. And the remote's resource hasn't been released. | + +![Rproc LCM States](img/rproc-lcm-state-machine.png) + +### State Transition +| State Transition | Transition Trigger | +|:-----------------|:-------------------| +| Offline -> Configured | Configure the remote to make it able to load application;
`remoteproc_configure(&rproc, &config_data)`| +| Configured -> Ready | load firmware ;
`remoteproc_load(&rproc, &path, &image_store, &image_store_ops, &image_info)` | +| Ready -> Running | start the processor;
`remoteproc_start(&rproc)` | +| Ready -> Stopped | stop the processor;
`remoteproc_stop(&rproc)`;
`remoteproc_shutdown(&rproc)`(Stopped is the intermediate state of shutdown operation) | +| Running -> Stopped | stop the processor;
`remoteproc_stop(&rproc)`;
`remoteproc_shutdown(&rproc)` | +| Stopped -> Offline | shutdown the processor;
`remoteproc_shutdown(&rproc)` | + +## Remote User APIs +* Initialize remoteproc instance: + ``` + struct remoteproc *remoteproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *priv) + ``` +* Release remoteproc instance: + ``` + int remoteproc_remove(struct remoteproc *rproc) + ``` +* Add memory to remoteproc: + ``` + void remoteproc_add_mem(struct remoteproc *rproc, struct remoteproc_mem *mem) + ``` +* Get memory libmetal I/O region from remoteproc specifying memory name: + ``` + struct metal_io_region *remoteproc_get_io_with_name(struct remoteproc *rproc, const char *name) + ``` +* Get memory libmetal I/O region from remoteproc specifying physical address: + ``` + struct metal_io_region *remoteproc_get_io_with_pa(struct remoteproc *rproc, metal_phys_addr_t pa); + ``` +* Get memory libmetal I/O region from remoteproc specifying virtual address: + ``` + struct metal_io_region *remoteproc_get_io_with_va(struct remoteproc *rproc, void *va); + ``` +* Map memory and add the memory to the remoteproc instance: + ``` + void *remoteproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io); + ``` +* Set resource table to remoteproc: + ``` + int remoteproc_set_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, + size_t rsc_size) + ``` +* Configure the remote presented by the remoteproc instance to make it able + to load applicaiton: + ``` + int remoteproc_config(struct remoteproc *rproc, void *data) + ``` +* Load application to the remote presented by the remoteproc instance to make + it ready to run: + ``` + int remoteproc_load(struct remoteproc *rproc, const char *path, + void *store, struct image_store_ops *store_ops, + void **img_info) + ``` +* Run application on the remote presented by the remoteproc instance: + ``` + int remoteproc_start(struct remoteproc *rproc) + ``` +* Stop application on remote presented by the remoteproc instance: + ``` + int remoteproc_stop(struct remoteproc *rproc) + ``` +* Shutdown the remote presented by the remoteproc instance: + ``` + int remoteproc_shutdown(struct remoteproc *rproc) + ``` +* Create virtio device from the resource table vdev resource, and add it to the + remoteproc instance: + ``` + struct virtio_device *remoteproc_create_virtio(struct remoteproc *rproc, + int vdev_id, unsigned int role, + void (*rst_cb)(struct virtio_device *vdev)) + ``` +* Remove virtio device from the remoteproc instance: + ``` + void remoteproc_remove_virtio(struct remoteproc *rproc, + struct virtio_device *vdev) + ``` + + diff --git a/docs/rpmsg-design.md b/docs/rpmsg-design.md new file mode 100644 index 0000000..206ee02 --- /dev/null +++ b/docs/rpmsg-design.md @@ -0,0 +1,109 @@ +# RPMsg Design Document +RPMsg is a framework to allow communication between two processors. +RPMsg implementation in OpenAMP library is based on virtio. It complies +the RPMsg Linux kernel implementation. It defines the handshaking on +setting up and tearing down the communication between applicaitons +running on two processors. + +## RPMsg User API Flow Chats +### RPMsg Static Endpoint +![Static Endpoint](img/coprocessor-rpmsg-static-ep.png) +### Binding Endpoint Dynamically with Name Service +![Binding Endpoint Dynamically with Name Service](img/coprocessor-rpmsg-ns.png) +### Creating Endpoint Dynamically with Name Service +![Creating Endpoint Dynamically with Name Service](img/coprocessor-rpmsg-ns-dynamic.png) + +## RPMsg User APIs +* RPMsg virtio master to initialize the shared buffers pool(RPMsg virtio slave + doesn't need to use this API): + ``` + void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool, + void *shbuf, size_t size) + ``` +* Initialize RPMsg virtio device: + ``` + int rpmsg_init_vdev(struct rpmsg_virtio_device *rvdev, + struct virtio_device *vdev, + rpmsg_ns_bind_cb ns_bind_cb, + struct metal_io_region *shm_io, + struct rpmsg_virtio_shm_pool *shpool) + ``` +* Deinitialize RPMsg virtio device: + ``` + void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev)` + ``` +* Get RPMsg device from RPMsg virtio device: + ``` + struct rpmsg_device *rpmsg_virtio_get_rpmsg_device(struct rpmsg_virtio_device *rvdev) + ``` +* Create RPMsg endpoint: + ``` + int rpmsg_create_ept(struct rpmsg_endpoint *ept, + struct rpmsg_device *rdev, + const char *name, uint32_t src, uint32_t dest, + rpmsg_ept_cb cb, rpmsg_ns_unbind_cb ns_unbind_cb) + ``` +* Destroy RPMsg endpoint: + ``` + void rpmsg_destroy_ept(struct rpsmg_endpoint *ept) + ``` +* Check if the local RPMsg endpoint is binded to the remote, and ready to send + message: + ``` + int is_rpmsg_ept_ready(struct rpmsg_endpoint *ept) + ``` +* Send message with RPMsg endpoint default binding: + ``` + int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, int len) + ``` +* Send message with RPMsg endpoint, specify destination address: + ``` + int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, + uint32_t dst) + ``` +* Send message with RPMsg endpoint using explicit source and destination + addresses: + ``` + int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, + uint32_t src, uint32_t dst, + const void *data, int len) + ``` +* Try to send message with RPMsg endpoint default binding, if no buffer + available, returns: + ``` + int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, + int len) + ``` +* Try to send message with RPMsg endpoint, specify destination address, + if no buffer available, returns: + ``` + int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, + uint32_t dst) + ``` +* Try to send message with RPMsg endpoint using explicit source and destination + addresses, if no buffer available, returns: + ``` + int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, + uint32_t src, uint32_t dst, + const void *data, int len)` + ``` +## RPMsg User Defined Callbacks +* RPMsg endpoint message received callback: + ``` + int (*rpmsg_ept_cb)(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) + ``` +* RPMsg name service binding callback. If user defines such callback, when + there is a name service announcement arrives, if there is no registered + endpoint found to bind to this name service, it will call this callback. + If this callback is not defined, it will drop this name service.: + ``` + void (*rpmsg_ns_bind_cb)(struct rpmsg_device *rdev, + const char *name, uint32_t dest) + ``` +* RPMsg endpoint name service unbind callback. If user defines such callback, + when there is name service destroy arrives, it will call this callback to + notify the user application about the remote has destroyed the service.: + ``` + void (*rpmsg_ns_unbind_cb)(struct rpmsg_endpoint *ept) + ``` diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..aa2edc5 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,65 @@ + +set_property (GLOBAL PROPERTY "PROJECT_LIB_EXTRA_CFLAGS") + +collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") +collect (PROJECT_LIB_DIRS "${CMAKE_CURRENT_BINARY_DIR}") +collect (PROJECT_INC_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include") + + +add_subdirectory (virtio) +add_subdirectory (rpmsg) +add_subdirectory (remoteproc) + +if (WITH_PROXY) + add_subdirectory (proxy) +endif (WITH_PROXY) + +set (OPENAMP_LIB open_amp) + +if (NOT CMAKE_INSTALL_LIBDIR) + set (CMAKE_INSTALL_LIBDIR "lib") +endif (NOT CMAKE_INSTALL_LIBDIR) + +collector_list (_include PROJECT_INC_DIRS) +include_directories (${_include}) + +collector_list (_deps PROJECT_LIB_DEPS) + +get_property (_ecflags GLOBAL PROPERTY "PROJECT_LIB_EXTRA_CFLAGS") + +collector_list (_sources PROJECT_LIB_SOURCES) +set_property (SOURCE ${_sources} + APPEND_STRING PROPERTY COMPILE_FLAGS " ${_ecflags}") + +# Build a shared library if so configured. +if (WITH_ZEPHYR) + zephyr_library_named(${OPENAMP_LIB}) + add_dependencies(${ZEPHYR_CURRENT_LIBRARY} ${OFFSETS_H_TARGET}) + zephyr_library_sources(${_sources}) + zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +else (WITH_ZEPHYR) + if (WITH_SHARED_LIB) + set (_lib ${OPENAMP_LIB}-shared) + add_library (${_lib} SHARED ${_sources}) + target_link_libraries (${_lib} ${_deps}) + install (TARGETS ${_lib} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties (${_lib} PROPERTIES + OUTPUT_NAME "${OPENAMP_LIB}" + VERSION "${PROJECT_VER}" + SOVERSION "${PROJECT_VER_MAJOR}" + ) + endif (WITH_SHARED_LIB) + + if (WITH_STATIC_LIB) + set (_lib ${OPENAMP_LIB}-static) + add_library (${_lib} STATIC ${_sources}) + install (TARGETS ${_lib} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties (${_lib} PROPERTIES + OUTPUT_NAME "${OPENAMP_LIB}" + ) + endif (WITH_STATIC_LIB) +endif (WITH_ZEPHYR) + +install (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/openamp" DESTINATION include) + +# vim: expandtab:ts=2:sw=2:smartindent diff --git a/lib/include/openamp/elf_loader.h b/lib/include/openamp/elf_loader.h new file mode 100644 index 0000000..bc36b5c --- /dev/null +++ b/lib/include/openamp/elf_loader.h @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ELF_LOADER_H_ +#define ELF_LOADER_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* ELF32 base types - 32-bit. */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +/* ELF64 base types - 64-bit. */ +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Size of ELF identifier field in the ELF file header. */ +#define EI_NIDENT 16 + +/* ELF32 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; + +/* ELF64 file header */ +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_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] */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +/* + * 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 + +/* ELF32 program header */ +typedef struct elf32_phdr { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* ELF64 program header */ +typedef struct elf64_phdr { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* segment types */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 /* Thread local storage segment */ +#define PT_LOOS 0x60000000 /* OS-specific */ +#define PT_HIOS 0x6fffffff /* OS-specific */ +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +/* ELF32 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; + +/* ELF64 section header. */ +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_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; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +/* Relocation entry with addend */ +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct elf64_rela { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_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)) +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) + +/* 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; + +typedef struct elf64_sym { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_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 elf32_info { + Elf32_Ehdr ehdr; + int load_state; + Elf32_Phdr *phdrs; + Elf32_Shdr *shdrs; + void *shstrtab; +}; + +struct elf64_info { + Elf64_Ehdr ehdr; + int load_state; + Elf64_Phdr *phdrs; + Elf64_Shdr *shdrs; + void *shstrtab; +}; + +#define ELF_STATE_INIT 0x0L +#define ELF_STATE_WAIT_FOR_PHDRS 0x100L +#define ELF_STATE_WAIT_FOR_SHDRS 0x200L +#define ELF_STATE_WAIT_FOR_SHSTRTAB 0x400L +#define ELF_STATE_HDRS_COMPLETE 0x800L +#define ELF_STATE_MASK 0xFF00L +#define ELF_NEXT_SEGMENT_MASK 0x00FFL + +extern struct loader_ops elf_ops; + +/** + * elf_identify - check if it is an ELF file + * + * It will check if the input image header is an ELF header. + * + * @img_data: firmware private data which will be passed to user defined loader + * operations + * @len: firmware header length + * + * return 0 for success or negative value for failure. + */ +int elf_identify(const void *img_data, size_t len); + +/** + * elf_load_header - Load ELF headers + * + * It will get the ELF header, the program header, and the section header. + * + * @img_data: image data + * @offset: input image data offset to the start of image file + * @len: input image data length + * @img_info: pointer to store image information data + * @last_load_state: last state return by this function + * @noffset: pointer to next offset required by loading ELF header + * @nlen: pointer to next data length required by loading ELF header + * + * return ELF loading header state, or negative value for failure + */ +int elf_load_header(const void *img_data, size_t offset, size_t len, + void **img_info, int last_load_state, + size_t *noffset, size_t *nlen); + +/** + * elf_load - load ELF data + * + * It will parse the ELF image and return the target device address, + * offset to the start of the ELF image of the data to load and the + * length of the data to load. + * + * @rproc: pointer to remoteproc instance + * @img_data: image data which will passed to the function. + * it can be NULL, if image data doesn't need to be handled + * by the load function. E.g. binary data which was + * loaded to the target memory. + * @offset: last loaded image data offset to the start of image file + * @len: last loaded image data length + * @img_info: pointer to store image information data + * @last_load_state: the returned state of the last function call. + * @da: target device address, if the data to load is not for target memory + * the da will be set to ANY. + * @noffset: pointer to next offset required by loading ELF header + * @nlen: pointer to next data length required by loading ELF header + * @padding: value to pad it is possible that a size of a segment in memory + * is larger than what it is in the ELF image. e.g. a segment + * can have stack section .bss. It doesn't need to copy image file + * space, in this case, it will be packed with 0. + * @nmemsize: pointer to next data target memory size. The size of a segment + * in the target memory can be larger than the its size in the + * image file. + * + * return 0 for success, otherwise negative value for failure + */ +int elf_load(struct remoteproc *rproc, const void *img_data, + size_t offset, size_t len, + void **img_info, int last_load_state, + metal_phys_addr_t *da, + size_t *noffset, size_t *nlen, + unsigned char *padding, size_t *nmemsize); + +/** + * elf_release - Release ELF image information + * + * It will release ELF image information data. + * + * @img_info: pointer to ELF image information + */ +void elf_release(void *img_info); + +/** + * elf_get_entry - Get entry point + * + * It will return entry point specified in the ELF file. + * + * @img_info: pointer to ELF image information + * + * return entry address + */ +metal_phys_addr_t elf_get_entry(void *img_info); + +/** + * elf_locate_rsc_table - locate the resource table information + * + * It will return the length of the resource table, and the device address of + * the resource table. + * + * @img_info: pointer to ELF image information + * @da: pointer to the device address + * @offset: pointer to the offset to in the ELF image of the resource + * table section. + * @size: pointer to the size of the resource table section. + * + * return 0 if successfully locate the resource table, negative value for + * failure. + */ +int elf_locate_rsc_table(void *img_info, metal_phys_addr_t *da, + size_t *offset, size_t *size); + +#if defined __cplusplus +} +#endif + +#endif /* ELF_LOADER_H_ */ diff --git a/lib/include/openamp/open_amp.h b/lib/include/openamp/open_amp.h new file mode 100644 index 0000000..f5d93ed --- /dev/null +++ b/lib/include/openamp/open_amp.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef OPEN_AMP_H_ +#define OPEN_AMP_H_ + +#include +#include +#include +#include + +#endif /* OPEN_AMP_H_ */ diff --git a/lib/include/openamp/remoteproc.h b/lib/include/openamp/remoteproc.h new file mode 100644 index 0000000..a83aa12 --- /dev/null +++ b/lib/include/openamp/remoteproc.h @@ -0,0 +1,819 @@ +/* + * Remoteproc Framework + * + * Copyright(c) 2018 Xilinx Ltd. + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef REMOTEPROC_H +#define REMOTEPROC_H + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define RSC_NOTIFY_ID_ANY 0xFFFFFFFFUL + +#define RPROC_MAX_NAME_LEN 32 + +/** + * 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 remoteproc. It may also include configuration entries. + * If needed, the remote remoteproc 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). + */ +METAL_PACKED_BEGIN +struct resource_table { + uint32_t ver; + uint32_t num; + uint32_t reserved[2]; + uint32_t offset[0]; +} METAL_PACKED_END; + +/** + * 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. + */ +METAL_PACKED_BEGIN +struct fw_rsc_hdr { + uint32_t type; + uint8_t data[0]; +} METAL_PACKED_END; + +/** + * 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 remoteproc will be writing logs. + * @RSC_VDEV: declare support for a virtio device, and serve as its + * virtio header. + * @RSC_VENDOR_START: start of the vendor specific resource types range + * @RSC_VENDOR_END : end of the vendor specific resource types range + * @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, + RSC_VENDOR_START = 128, + RSC_VENDOR_END = 512, +}; + +#define FW_RSC_U64_ADDR_ANY 0xFFFFFFFFFFFFFFFFUL +#define FW_RSC_U32_ADDR_ANY 0xFFFFFFFFUL + +/** + * 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 remoteprocs 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 remoteprocs 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). + */ +METAL_PACKED_BEGIN +struct fw_rsc_carveout { + uint32_t type; + uint32_t da; + uint32_t pa; + uint32_t len; + uint32_t flags; + uint32_t reserved; + uint8_t name[RPROC_MAX_NAME_LEN]; +} METAL_PACKED_END; + +/** + * 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 remoteproc requires + * access to certain memory-based peripherals; _never_ use it to access + * regular memory. + * + * This is obviously only needed if the remote remoteproc 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. + */ +METAL_PACKED_BEGIN +struct fw_rsc_devmem { + uint32_t type; + uint32_t da; + uint32_t pa; + uint32_t len; + uint32_t flags; + uint32_t reserved; + uint8_t name[RPROC_MAX_NAME_LEN]; +} METAL_PACKED_END; + +/** + * 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 remoteproc 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 remoteproc, the trace buffers are exposed to the + * user via debugfs entries (called trace0, trace1, etc..). + */ +METAL_PACKED_BEGIN +struct fw_rsc_trace { + uint32_t type; + uint32_t da; + uint32_t len; + uint32_t reserved; + uint8_t name[RPROC_MAX_NAME_LEN]; +} METAL_PACKED_END; + +/** + * 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 remoteproc, 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 remoteproc is expecting the vring, or indicate that + * dynamically allocation of the vring's device address is supported. + */ +METAL_PACKED_BEGIN +struct fw_rsc_vdev_vring { + uint32_t da; + uint32_t align; + uint32_t num; + uint32_t notifyid; + uint32_t reserved; +} METAL_PACKED_END; + +/** + * 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 remoteproc, 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 remoteprocs + * 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 + * remoteprocs. 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. + */ +METAL_PACKED_BEGIN +struct fw_rsc_vdev { + uint32_t type; + uint32_t id; + uint32_t notifyid; + uint32_t dfeatures; + uint32_t gfeatures; + uint32_t config_len; + uint8_t status; + uint8_t num_of_vrings; + uint8_t reserved[2]; + struct fw_rsc_vdev_vring vring[0]; +} METAL_PACKED_END; + +/** + * struct fw_rsc_vendor - remote processor vendor specific resource + * @len: length of the resource + * + * This resource entry tells the host the vendor specific resource + * required by the remote. + * + * These request entries should precede other shared resource entries + * such as vdevs, vrings. + */ +METAL_PACKED_BEGIN +struct fw_rsc_vendor { + uint32_t type; + uint32_t len; +} METAL_PACKED_END; + +struct loader_ops; +struct image_store_ops; +struct remoteproc_ops; + +/** + * struct remoteproc_mem + * + * This structure presents the memory used by the remote processor + * + * @da: device memory + * @pa: physical memory + * @size: size of the memory + * @io: pointer to the I/O region + * @node: list node + */ +struct remoteproc_mem { + metal_phys_addr_t da; + metal_phys_addr_t pa; + size_t size; + char name[RPROC_MAX_NAME_LEN]; + struct metal_io_region *io; + struct metal_list node; +}; + +/** + * struct remoteproc + * + * 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. + * + * @bootaddr: boot address + * @loader: executable loader + * @lock: mutex lock + * @ops: remoteproc operations + * @rsc_table: pointer to resource table + * @rsc_len: length of resource table + * @rsc_io: metal I/O region of resource table + * @mems: remoteproc memories + * @vdevs: remoteproc virtio devices + * @bitmap: bitmap for notify IDs for remoteproc subdevices + * @state: remote processor state + * @priv: private data + */ +struct remoteproc { + metal_mutex_t lock; + void *rsc_table; + size_t rsc_len; + struct metal_io_region *rsc_io; + struct metal_list mems; + struct metal_list vdevs; + unsigned long bitmap; + struct remoteproc_ops *ops; + metal_phys_addr_t bootaddr; + struct loader_ops *loader; + unsigned int state; + void *priv; +}; + +/** + * struct remoteproc_ops + * + * remoteproc operations needs to be implemented by each remoteproc driver + * + * @init: initialize the remoteproc instance + * @remove: remove the remoteproc instance + * @mmap: memory mapped the memory with physical address or destination + * address as input. + * @handle_rsc: handle the vendor specific resource + * @config: configure the remoteproc to make it ready to load and run + * executable + * @start: kick the remoteproc to run application + * @stop: stop the remoteproc from running application, the resource such as + * memory may not be off. + * @shutdown: shutdown the remoteproc and release its resources. + * @notify: notify the remote + */ +struct remoteproc_ops { + struct remoteproc *(*init)(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *arg); + void (*remove)(struct remoteproc *rproc); + void *(*mmap)(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io); + int (*handle_rsc)(struct remoteproc *rproc, void *rsc, size_t len); + int (*config)(struct remoteproc *rproc, void *data); + int (*start)(struct remoteproc *rproc); + int (*stop)(struct remoteproc *rproc); + int (*shutdown)(struct remoteproc *rproc); + int (*notify)(struct remoteproc *rproc, uint32_t id); +}; + +/* Remoteproc error codes */ +#define RPROC_EBASE 0 +#define RPROC_ENOMEM (RPROC_EBASE + 1) +#define RPROC_EINVAL (RPROC_EBASE + 2) +#define RPROC_ENODEV (RPROC_EBASE + 3) +#define RPROC_EAGAIN (RPROC_EBASE + 4) +#define RPROC_ERR_RSC_TAB_TRUNC (RPROC_EBASE + 5) +#define RPROC_ERR_RSC_TAB_VER (RPROC_EBASE + 6) +#define RPROC_ERR_RSC_TAB_RSVD (RPROC_EBASE + 7) +#define RPROC_ERR_RSC_TAB_VDEV_NRINGS (RPROC_EBASE + 9) +#define RPROC_ERR_RSC_TAB_NP (RPROC_EBASE + 10) +#define RPROC_ERR_RSC_TAB_NS (RPROC_EBASE + 11) +#define RPROC_ERR_LOADER_STATE (RPROC_EBASE + 12) +#define RPROC_EMAX (RPROC_EBASE + 16) +#define RPROC_EPTR (void *)(-1) +#define RPROC_EOF (void *)(-1) + +static inline long RPROC_PTR_ERR(const void *ptr) +{ + return (long)ptr; +} + +static inline int RPROC_IS_ERR(const void *ptr) +{ + if ((unsigned long)ptr >= (unsigned long)(-RPROC_EMAX)) + return 1; + else + return 0; +} + +static inline void *RPROC_ERR_PTR(long error) +{ + return (void *)error; +} + +/** + * enum rproc_state - remote processor states + * @RPROC_OFFLINE: remote is offline + * @RPROC_CONFIGURED: remote is configured + * @RPROC_READY: remote is ready to start + * @RPROC_RUNNING: remote is up and running + * @RPROC_SUSPENDED: remote is suspended + * @RPROC_ERROR: remote has error; need to recover + * @RPROC_STOPPED: remote is stopped + * @RPROC_LAST: just keep this one at the end + */ +enum remoteproc_state { + RPROC_OFFLINE = 0, + RPROC_CONFIGURED = 1, + RPROC_READY = 2, + RPROC_RUNNING = 3, + RPROC_SUSPENDED = 4, + RPROC_ERROR = 5, + RPROC_STOPPED = 6, + RPROC_LAST = 7, +}; + +/** + * remoteproc_init + * + * Initializes remoteproc resource. + * + * @rproc - pointer to remoteproc instance + * @ops - pointer to remoteproc operations + * @priv - pointer to private data + * + * @returns created remoteproc pointer + */ +struct remoteproc *remoteproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *priv); + +/** + * remoteproc_remove + * + * Remove remoteproc resource + * + * @rproc - pointer to remoteproc instance + * + * returns 0 for success, negative value for failure + */ +int remoteproc_remove(struct remoteproc *rproc); + +/** + * remoteproc_init_mem + * + * Initialize remoteproc memory + * + * @mem - pointer to remoteproc memory + * @name - memory name + * @pa - physical address + * @da - device address + * @size - memory size + * @io - pointer to the I/O region + */ +static inline void +remoteproc_init_mem(struct remoteproc_mem *mem, const char *name, + metal_phys_addr_t pa, metal_phys_addr_t da, + size_t size, struct metal_io_region *io) +{ + if (!mem) + return; + if (name) + strncpy(mem->name, name, sizeof(mem->name)); + else + mem->name[0] = 0; + mem->pa = pa; + mem->da = da; + mem->io = io; + mem->size = size; +} + +/** + * remoteproc_add_mem + * + * Add remoteproc memory + * + * @rproc - pointer to remoteproc + * @mem - pointer to remoteproc memory + */ +static inline void +remoteproc_add_mem(struct remoteproc *rproc, struct remoteproc_mem *mem) +{ + if (!rproc || !mem) + return; + metal_list_add_tail(&rproc->mems, &mem->node); +} + +/** + * remoteproc_get_io_with_name + * + * get remoteproc memory I/O region with name + * + * @rproc - pointer to the remote processor + * @name - name of the shared memory + * @io - pointer to the pointer of the I/O region + * + * returns metal I/O region pointer, NULL for failure + */ +struct metal_io_region * +remoteproc_get_io_with_name(struct remoteproc *rproc, + const char *name); + +/** + * remoteproc_get_io_with_pa + * + * get remoteproc memory I/O region with physical address + * + * @rproc - pointer to the remote processor + * @pa - physical address + * + * returns metal I/O region pointer, NULL for failure + */ +struct metal_io_region * +remoteproc_get_io_with_pa(struct remoteproc *rproc, + metal_phys_addr_t pa); + +/** + * remoteproc_get_io_with_da + * + * get remoteproc memory I/O region with device address + * + * @rproc - pointer to the remote processor + * @da - device address + * @offset - I/O region offset of the device address + * + * returns metal I/O region pointer, NULL for failure + */ +struct metal_io_region * +remoteproc_get_io_with_da(struct remoteproc *rproc, + metal_phys_addr_t da, + unsigned long *offset); + +/** + * remoteproc_get_io_with_va + * + * get remoteproc memory I/O region with virtual address + * + * @rproc - pointer to the remote processor + * @va - virtual address + * + * returns metal I/O region pointer, NULL for failure + */ +struct metal_io_region * +remoteproc_get_io_with_va(struct remoteproc *rproc, + void *va); + +/** + * remoteproc_mmap + * + * remoteproc mmap memory + * + * @rproc - pointer to the remote processor + * @pa - physical address pointer + * @da - device address pointer + * @size - size of the memory + * @attribute - memory attribute + * @io - pointer to the I/O region + * + * returns pointer to the memory + */ +void *remoteproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io); + +/** + * remoteproc_set_rsc_table + * + * Parse and set resource table of remoteproc + * + * @rproc - pointer to remoteproc instance + * @rsc_table - pointer to resource table + * @rsc_size - resource table size + * + * returns 0 for success and negative value for errors + */ +int remoteproc_set_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, + size_t rsc_size); + +/** + * remoteproc_config + * + * This function configures the remote processor to get it + * ready to load and run executable. + * + * @rproc - pointer to remoteproc instance to start + * @data - configuration data + * + * returns 0 for success and negative value for errors + */ +int remoteproc_config(struct remoteproc *rproc, void *data); + +/** + * remoteproc_start + * + * This function starts the remote processor. + * It assumes the firmware is already loaded, + * + * @rproc - pointer to remoteproc instance to start + * + * returns 0 for success and negative value for errors + */ +int remoteproc_start(struct remoteproc *rproc); + +/** + * remoteproc_stop + * + * This function stops the remote processor but it + * will not release its resource. + * + * @rproc - pointer to remoteproc instance + * + * returns 0 for success and negative value for errors + */ +int remoteproc_stop(struct remoteproc *rproc); + +/** + * remoteproc_shutdown + * + * This function shutdown the remote processor and + * release its resources. + * + * @rproc - pointer to remoteproc instance + * + * returns 0 for success and negative value for errors + */ +int remoteproc_shutdown(struct remoteproc *rproc); + +/** + * remoteproc_load + * + * load executable, it expects the user application defines how to + * open the executable file and how to get data from the executable file + * and how to load data to the target memory. + * + * @rproc: pointer to the remoteproc instance + * @path: optional path to the image file + * @store: pointer to user defined image store argument + * @store_ops: pointer to image store operations + * @image_info: pointer to memory which stores image information used + * by remoteproc loader + * + * return 0 for success and negative value for failure + */ +int remoteproc_load(struct remoteproc *rproc, const char *path, + void *store, struct image_store_ops *store_ops, + void **img_info); + +/** + * remoteproc_load_noblock + * + * load executable, it expects the caller has loaded image data to local + * memory and passed to the this function. If the function needs more + * image data it will return the next expected image data offset and + * the next expected image data length. If the function requires the + * caller to download image data to the target memory, it will also + * return the target physical address besides the offset and length. + * This function can be used to load firmware in stream mode. In this + * mode, you cannot do seek to the executable file. If the executable + * is ELF, it cannot get the resource table section before it loads + * the full ELF file. Furthermore, application usually don't store + * the data which is loaded to local memory in streaming mode, and + * thus, in this mode, it will load the binary to the target memory + * before it gets the resource table. And thus, when calling this function + * don't put the target executable memory in the resource table, as + * this function will parse the resource table after it loads the binary + * to target memory. + * + * @rproc: pointer to the remoteproc instance + * @img_data: pointer to image data for remoteproc loader to parse + * @offset: image data offset to the beginning of the image file + * @len: image data length + * @image_info: pointer to memory which stores image information used + * by remoteproc loader + * @pa: pointer to the target memory physical address. If the next expected + * data doesn't need to load to the target memory, the function will + * set it to ANY. + * @io: pointer to the io region. If the next expected + * data doesn't need to load to the target memory, the function will + * set it to NULL. + * @noffset: pointer to the next image data offset to the beginning of + * the image file needs to load to local or to the target + * memory. + * @nlen: pointer to the next image data length needs to load to local + * or to the target memory. + * @nmlen: pointer to the memory size. It is only used when the next + * expected data is going to be loaded to the target memory. E.g. + * in ELF, it is possible that loadable segment in memory is + * larger that the segment data in the ELF file. In this case, + * application will need to pad the rest of the memory with + * padding. + * @padding: pointer to the padding value. It is only used when the next + * expected data is going to be loaded to the target memory. + * and the target memory size is larger than the segment data in + * the executable file. + * + * return 0 for success and negative value for failure + */ +int remoteproc_load_noblock(struct remoteproc *rproc, + const void *img_data, size_t offset, size_t len, + void **img_info, + metal_phys_addr_t *pa, struct metal_io_region **io, + size_t *noffset, size_t *nlen, + size_t *nmlen, unsigned char *padding); + +/** + * remoteproc_allocate_id + * + * allocate notifyid for resource + * + * @rproc - pointer to the remoteproc instance + * @start - start of the id range + * @end - end of the id range + * + * return allocated notify id + */ +unsigned int remoteproc_allocate_id(struct remoteproc *rproc, + unsigned int start, + unsigned int end); + +/* remoteproc_create_virtio + * + * create virtio device, it returns pointer to the created virtio device. + * + * @rproc: pointer to the remoteproc instance + * @vdev_id: virtio device ID + * @role: virtio device role + * @rst_cb: virtio device reset callback + * + * return pointer to the created virtio device, NULL for failure. + */ +struct virtio_device * +remoteproc_create_virtio(struct remoteproc *rproc, + int vdev_id, unsigned int role, + void (*rst_cb)(struct virtio_device *vdev)); + +/* remoteproc_remove_virtio + * + * Remove virtio device + * + * @rproc: pointer to the remoteproc instance + * @vdev: pointer to the virtio device + * + */ +void remoteproc_remove_virtio(struct remoteproc *rproc, + struct virtio_device *vdev); + +/* remoteproc_get_notification + * + * remoteproc is got notified, it will check its subdevices + * for the notification + * + * @rproc - pointer to the remoteproc instance + * @notifyid - notification id + * + * return 0 for succeed, negative value for failure + */ +int remoteproc_get_notification(struct remoteproc *rproc, + uint32_t notifyid); +#if defined __cplusplus +} +#endif + +#endif /* REMOTEPROC_H_ */ diff --git a/lib/include/openamp/remoteproc_loader.h b/lib/include/openamp/remoteproc_loader.h new file mode 100644 index 0000000..14d3716 --- /dev/null +++ b/lib/include/openamp/remoteproc_loader.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/************************************************************************** + * 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 +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* Loader feature macros */ +#define SUPPORT_SEEK 1UL + +/* Remoteproc loader any address */ +#define RPROC_LOAD_ANYADDR ((metal_phys_addr_t)-1) + +/* Remoteproc loader Executable Image Parsing States */ +/* Remoteproc loader parser initial state */ +#define RPROC_LOADER_NOT_READY 0x0L +/* Remoteproc loader ready to load, even it can be not finish parsing */ +#define RPROC_LOADER_READY_TO_LOAD 0x10000L +/* Remoteproc loader post data load */ +#define RPROC_LOADER_POST_DATA_LOAD 0x20000L +/* Remoteproc loader finished loading */ +#define RPROC_LOADER_LOAD_COMPLETE 0x40000L +/* Remoteproc loader state mask */ +#define RPROC_LOADER_MASK 0x00FF0000L +/* Remoteproc loader private mask */ +#define RPROC_LOADER_PRIVATE_MASK 0x0000FFFFL +/* Remoteproc loader reserved mask */ +#define RPROC_LOADER_RESERVED_MASK 0x0F000000L + +/** + * struct image_store_ops - user defined image store operations + * @open: user defined callback to open the "firmware" to prepare loading + * @close: user defined callback to close the "firmware" to clean up + * after loading + * @load: user defined callback to load the firmware contents to target + * memory or local memory + * @features: loader supported features. e.g. seek + */ +struct image_store_ops { + int (*open)(void *store, const char *path, const void **img_data); + void (*close)(void *store); + int (*load)(void *store, size_t offset, size_t size, + const void **data, + metal_phys_addr_t pa, + struct metal_io_region *io, char is_blocking); + unsigned int features; +}; + +/** + * struct loader_ops - loader operations + * @load_header: define how to get the executable headers + * @load_data: define how to load the target data + * @locate_rsc_table: define how to get the resource table target address, + * offset to the ELF image file and size of the resource + * table. + * @release: define how to release the loader + * @get_entry: get entry address + * @get_load_state: get load state from the image information + */ +struct loader_ops { + int (*load_header)(const void *img_data, size_t offset, size_t len, + void **img_info, int last_state, + size_t *noffset, size_t *nlen); + int (*load_data)(struct remoteproc *rproc, + const void *img_data, size_t offset, size_t len, + void **img_info, int last_load_state, + metal_phys_addr_t *da, + size_t *noffset, size_t *nlen, + unsigned char *padding, size_t *nmemsize); + int (*locate_rsc_table)(void *img_info, metal_phys_addr_t *da, + size_t *offset, size_t *size); + void (*release)(void *img_info); + metal_phys_addr_t (*get_entry)(void *img_info); + int (*get_load_state)(void *img_info); +}; + +#if defined __cplusplus +} +#endif + +#endif /* REMOTEPROC_LOADER_H_ */ diff --git a/lib/include/openamp/remoteproc_virtio.h b/lib/include/openamp/remoteproc_virtio.h new file mode 100644 index 0000000..1912f54 --- /dev/null +++ b/lib/include/openamp/remoteproc_virtio.h @@ -0,0 +1,124 @@ +/* + * Remoteproc Virtio Framework + * + * Copyright(c) 2018 Xilinx Ltd. + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef REMOTEPROC_VIRTIO_H +#define REMOTEPROC_VIRTIO_H + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* define vdev notification funciton user should implement */ +typedef int (*rpvdev_notify_func)(void *priv, uint32_t id); + +/** + * struct remoteproc_virtio + * @priv pointer to private data + * @vdev_rsc address of vdev resource + * @vdev_rsc_io metal I/O region of vdev_info, can be NULL + * @notify notification function + * @vdev virtio device + * @node list node + */ +struct remoteproc_virtio { + void *priv; + void *vdev_rsc; + struct metal_io_region *vdev_rsc_io; + rpvdev_notify_func notify; + struct virtio_device vdev; + struct metal_list node; +}; + +/** + * rproc_virtio_create_vdev + * + * Create rproc virtio vdev + * + * @role: 0 - virtio master, 1 - virtio slave + * @notifyid: virtio device notification id + * @rsc: pointer to the virtio device resource + * @rsc_io: pointer to the virtio device resource I/O region + * @priv: pointer to the private data + * @notify: vdev and virtqueue notification function + * @rst_cb: reset virtio device callback + * + * return pointer to the created virtio device for success, + * NULL for failure. + */ +struct virtio_device * +rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid, + void *rsc, struct metal_io_region *rsc_io, + void *priv, + rpvdev_notify_func notify, + virtio_dev_reset_cb rst_cb); + +/** + * rproc_virtio_remove_vdev + * + * Remove rproc virtio vdev + * + * @vdev - pointer to the virtio device + */ +void rproc_virtio_remove_vdev(struct virtio_device *vdev); + +/** + * rproc_virtio_init_vring + * + * Initialize rproc virtio vring + * + * @vdev: pointer to the virtio device + * @index: vring index in the virtio device + * @notifyid: remoteproc vring notification id + * @va: vring virtual address + * @io: pointer to vring I/O region + * @num_desc: number of descriptors + * @align: vring alignment + * + * return 0 for success, negative value for failure. + */ +int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index, + unsigned int notifyid, void *va, + struct metal_io_region *io, + unsigned int num_descs, unsigned int align); + +/** + * rproc_virtio_notified + * + * remoteproc virtio is got notified + * + * @vdev - pointer to the virtio device + * @notifyid - notify id + * + * return 0 for successful, negative value for failure + */ +int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid); + +/** + * rproc_virtio_wait_remote_ready + * + * Blocking function, waiting for the remote core is ready to start + * communications. + * + * @vdev - pointer to the virtio device + * + * return true when remote processor is ready. + */ +void rproc_virtio_wait_remote_ready(struct virtio_device *vdev); + +#if defined __cplusplus +} +#endif + +#endif /* REMOTEPROC_VIRTIO_H */ diff --git a/lib/include/openamp/rpmsg.h b/lib/include/openamp/rpmsg.h new file mode 100644 index 0000000..181b58c --- /dev/null +++ b/lib/include/openamp/rpmsg.h @@ -0,0 +1,355 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _RPMSG_H_ +#define _RPMSG_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* Configurable parameters */ +#define RPMSG_NAME_SIZE (32) +#define RPMSG_ADDR_BMP_SIZE (128) + +#define RPMSG_NS_EPT_ADDR (0x35) +#define RPMSG_RESERVED_ADDRESSES (1024) +#define RPMSG_ADDR_ANY 0xFFFFFFFF + +/* Error macros. */ +#define RPMSG_SUCCESS 0 +#define RPMSG_ERROR_BASE -2000 +#define RPMSG_ERR_NO_MEM (RPMSG_ERROR_BASE - 1) +#define RPMSG_ERR_NO_BUFF (RPMSG_ERROR_BASE - 2) +#define RPMSG_ERR_PARAM (RPMSG_ERROR_BASE - 3) +#define RPMSG_ERR_DEV_STATE (RPMSG_ERROR_BASE - 4) +#define RPMSG_ERR_BUFF_SIZE (RPMSG_ERROR_BASE - 5) +#define RPMSG_ERR_INIT (RPMSG_ERROR_BASE - 6) +#define RPMSG_ERR_ADDR (RPMSG_ERROR_BASE - 7) + +struct rpmsg_endpoint; +struct rpmsg_device; + +/* Returns positive value on success or negative error value on failure */ +typedef int (*rpmsg_ept_cb)(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv); +typedef void (*rpmsg_ns_unbind_cb)(struct rpmsg_endpoint *ept); +typedef void (*rpmsg_ns_bind_cb)(struct rpmsg_device *rdev, + const char *name, uint32_t dest); + +/** + * struct rpmsg_endpoint - binds a local rpmsg address to its user + * @name: name of the service supported + * @rdev: pointer to the rpmsg device + * @addr: local address of the endpoint + * @dest_addr: address of the default remote endpoint binded. + * @cb: user rx callback, return value of this callback is reserved + * for future use, for now, only allow RPMSG_SUCCESS as return value. + * @ns_unbind_cb: end point service unbind callback, called when remote + * ept is destroyed. + * @node: end point node. + * @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. + */ +struct rpmsg_endpoint { + char name[RPMSG_NAME_SIZE]; + struct rpmsg_device *rdev; + uint32_t addr; + uint32_t dest_addr; + rpmsg_ept_cb cb; + rpmsg_ns_unbind_cb ns_unbind_cb; + struct metal_list node; + void *priv; +}; + +/** + * struct rpmsg_device_ops - RPMsg device operations + * @send_offchannel_raw: send RPMsg data + */ +struct rpmsg_device_ops { + int (*send_offchannel_raw)(struct rpmsg_device *rdev, + uint32_t src, uint32_t dst, + const void *data, int size, int wait); +}; + +/** + * struct rpmsg_device - representation of a RPMsg device + * @endpoints: list of endpoints + * @ns_ept: name service endpoint + * @bitmap: table endpoint address allocation. + * @lock: mutex lock for rpmsg management + * @ns_bind_cb: callback handler for name service announcement without local + * endpoints waiting to bind. + * @ops: RPMsg device operations + * @support_ns: create/destroy namespace message + */ +struct rpmsg_device { + struct metal_list endpoints; + struct rpmsg_endpoint ns_ept; + unsigned long bitmap[metal_bitmap_longs(RPMSG_ADDR_BMP_SIZE)]; + metal_mutex_t lock; + rpmsg_ns_bind_cb ns_bind_cb; + struct rpmsg_device_ops ops; + bool support_ns; +}; + +/** + * rpmsg_send_offchannel_raw() - send a message across to the remote processor, + * specifying source and destination address. + * @ept: the rpmsg endpoint + * @data: payload of the message + * @len: length of the payload + * + * This function sends @data of length @len to the remote @dst address from + * the source @src address. + * The message will be sent to the remote processor which the channel belongs + * to. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src, + uint32_t dst, const void *data, int size, + int wait); + +/** + * rpmsg_send() - send a message across to the remote processor + * @ept: the rpmsg endpoint + * @data: payload of the message + * @len: length of the payload + * + * This function sends @data of length @len based on the @ept. + * The message will be sent to the remote processor which the channel belongs + * to, using @ept'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. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_send(struct rpmsg_endpoint *ept, const void *data, + int len) +{ + return rpmsg_send_offchannel_raw(ept, ept->addr, ept->dest_addr, data, + len, true); +} + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @ept: the rpmsg endpoint + * @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 @ept + * channel belongs to, using @ept'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. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_sendto(struct rpmsg_endpoint *ept, const void *data, + int len, uint32_t dst) +{ + return rpmsg_send_offchannel_raw(ept, ept->addr, dst, data, len, true); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @ept: the rpmsg endpoint + * @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 @ept + * 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. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, + uint32_t src, uint32_t dst, + const void *data, int len) +{ + return rpmsg_send_offchannel_raw(ept, src, dst, data, len, true); +} + +/** + * rpmsg_trysend() - send a message across to the remote processor + * @ept: the rpmsg endpoint + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @ept channel. + * The message will be sent to the remote processor which the @ept + * channel belongs to, using @ept'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. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_trysend(struct rpmsg_endpoint *ept, const void *data, + int len) +{ + return rpmsg_send_offchannel_raw(ept, ept->addr, ept->dest_addr, data, + len, false); +} + +/** + * rpmsg_trysendto() - send a message across to the remote processor, + * specify dst + * @ept: the rpmsg endpoint + * @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 @ept + * channel belongs to, using @ept's source address. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_trysendto(struct rpmsg_endpoint *ept, const void *data, + int len, uint32_t dst) +{ + return rpmsg_send_offchannel_raw(ept, ept->addr, dst, data, len, false); +} + +/** + * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses + * @ept: the rpmsg endpoint + * @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 @ept + * channel belongs to. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Returns number of bytes it has sent or negative error value on failure. + */ +static inline int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, + uint32_t src, uint32_t dst, + const void *data, int len) +{ + return rpmsg_send_offchannel_raw(ept, src, dst, data, len, false); +} + +/** + * rpmsg_init_ept - initialize rpmsg endpoint + * + * Initialize an RPMsg endpoint with a name, source address, + * remoteproc address, endpoint callback, and destroy endpoint callback. + * + * API deprecated since release v2020.10 + * + * @ept: pointer to rpmsg endpoint + * @name: service name associated to the endpoint + * @src: local address of the endpoint + * @dest: target address of the endpoint + * @cb: endpoint callback + * @ns_unbind_cb: end point service unbind callback, called when remote ept is + * destroyed. + */ +__deprecated static inline void rpmsg_init_ept(struct rpmsg_endpoint *ept, + const char *name, + uint32_t src, uint32_t dest, + rpmsg_ept_cb cb, + rpmsg_ns_unbind_cb ns_unbind_cb) +{ + strncpy(ept->name, name ? name : "", sizeof(ept->name)); + ept->addr = src; + ept->dest_addr = dest; + ept->cb = cb; + ept->ns_unbind_cb = ns_unbind_cb; +} + +/** + * rpmsg_create_ept - create rpmsg endpoint and register it to rpmsg device + * + * Create a RPMsg endpoint, initialize it with a name, source address, + * remoteproc address, endpoint callback, and destroy endpoint callback, + * and register it to the RPMsg device. + * + * @ept: pointer to rpmsg endpoint + * @name: service name associated to the endpoint + * @src: local address of the endpoint + * @dest: target address of the endpoint + * @cb: endpoint callback + * @ns_unbind_cb: end point service unbind callback, called when remote ept is + * destroyed. + * + * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as + * it binds an rpmsg address with an rx callback handler. + * + * Rpmsg client should create an endpoint to discuss with remote. rpmsg client + * provide at least a channel name, a callback for message notification and by + * default endpoint source address should be set to RPMSG_ADDR_ANY. + * + * As an option Some rpmsg clients can specify an endpoint with a specific + * source address. + */ + +int rpmsg_create_ept(struct rpmsg_endpoint *ept, struct rpmsg_device *rdev, + const char *name, uint32_t src, uint32_t dest, + rpmsg_ept_cb cb, rpmsg_ns_unbind_cb ns_unbind_cb); + +/** + * rpmsg_destroy_ept - destroy rpmsg endpoint and unregister it from rpmsg + * device + * + * @ept: pointer to the rpmsg endpoint + * + * It unregisters the rpmsg endpoint from the rpmsg device and calls the + * destroy endpoint callback if it is provided. + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *ept); + +/** + * is_rpmsg_ept_ready - check if the rpmsg endpoint ready to send + * + * @ept: pointer to rpmsg endpoint + * + * Returns 1 if the rpmsg endpoint has both local addr and destination + * addr set, 0 otherwise + */ +static inline unsigned int is_rpmsg_ept_ready(struct rpmsg_endpoint *ept) +{ + return ept && ept->rdev && ept->dest_addr != RPMSG_ADDR_ANY; +} + +#if defined __cplusplus +} +#endif + +#endif /* _RPMSG_H_ */ diff --git a/lib/include/openamp/rpmsg_retarget.h b/lib/include/openamp/rpmsg_retarget.h new file mode 100644 index 0000000..1f63a63 --- /dev/null +++ b/lib/include/openamp/rpmsg_retarget.h @@ -0,0 +1,119 @@ +#ifndef RPMSG_RETARGET_H +#define RPMSG_RETARGET_H + +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* File Operations System call definitions */ +#define OPEN_SYSCALL_ID 0x1UL +#define CLOSE_SYSCALL_ID 0x2UL +#define WRITE_SYSCALL_ID 0x3UL +#define READ_SYSCALL_ID 0x4UL +#define ACK_STATUS_ID 0x5UL + +#define TERM_SYSCALL_ID 0x6UL + +#define DEFAULT_PROXY_ENDPOINT 0xFFUL + +struct rpmsg_rpc_data; + +typedef int (*rpmsg_rpc_poll)(void *arg); +typedef void (*rpmsg_rpc_shutdown_cb)(struct rpmsg_rpc_data *rpc); + +struct rpmsg_rpc_syscall_header { + int32_t int_field1; + int32_t int_field2; + uint32_t data_len; +}; + +struct rpmsg_rpc_syscall { + uint32_t id; + struct rpmsg_rpc_syscall_header args; +}; + +struct rpmsg_rpc_data { + struct rpmsg_endpoint ept; + int ept_destroyed; + atomic_int nacked; + void *respbuf; + size_t respbuf_len; + rpmsg_rpc_poll poll; + void *poll_arg; + rpmsg_rpc_shutdown_cb shutdown_cb; + metal_mutex_t lock; + struct metal_spinlock buflock; +}; + +/** + * rpmsg_rpc_init - initialize RPMsg remote procedure call + * + * This function is to initialize the remote procedure call + * global data. RPMsg RPC will send request to remote and + * wait for callback. + * + * @rpc: pointer to the global remote procedure call data + * @rdev: pointer to the rpmsg device + * @ept_name: name of the endpoint used by RPC + * @ept_addr: address of the endpoint used by RPC + * @ept_raddr: remote address of the endpoint used by RPC + * @poll_arg: pointer to poll function argument + * @poll: poll function + * @shutdown_cb: shutdown callback function + * + * return 0 for success, and negative value for failure. + */ +int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc, + struct rpmsg_device *rdev, + const char *ept_name, uint32_t ept_addr, + uint32_t ept_raddr, + void *poll_arg, rpmsg_rpc_poll poll, + rpmsg_rpc_shutdown_cb shutdown_cb); + +/** + * rpmsg_rpc_release - release RPMsg remote procedure call + * + * This function is to release remoteproc procedure call + * global data. + * + * @rpc: pointer to the globacl remote procedure call + */ +void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc); + +/** + * rpmsg_rpc_send - Request RPMsg RPC call + * + * This function sends RPC request it will return with the length + * of data and the response buffer. + * + * @rpc: pointer to remoteproc procedure call data struct + * @req: pointer to request buffer + * @len: length of the request data + * @resp: pointer to where store the response buffer + * @resp_len: length of the response buffer + * + * return length of the received response, negative value for failure. + */ +int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc, + void *req, size_t len, + void *resp, size_t resp_len); + +/** + * rpmsg_set_default_rpc - set default RPMsg RPC data + * + * The default RPC data is used to redirect standard C file operations + * to RPMsg channels. + * + * @rpc: pointer to remoteproc procedure call data struct + */ +void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc); + +#if defined __cplusplus +} +#endif + +#endif /* RPMSG_RETARGET_H */ diff --git a/lib/include/openamp/rpmsg_virtio.h b/lib/include/openamp/rpmsg_virtio.h new file mode 100644 index 0000000..12cb47f --- /dev/null +++ b/lib/include/openamp/rpmsg_virtio.h @@ -0,0 +1,202 @@ +/* + * rpmsg based on virtio + * + * Copyright (C) 2018 Linaro, Inc. + * + * All rights reserved. + * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _RPMSG_VIRTIO_H_ +#define _RPMSG_VIRTIO_H_ + +#include +#include +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* Configurable parameters */ +#ifndef RPMSG_BUFFER_SIZE +#define RPMSG_BUFFER_SIZE (512) +#endif + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ + +/** + * struct rpmsg_virtio_shm_pool - shared memory pool used for rpmsg buffers + * @base: base address of the memory pool + * @avail: available memory size + * @size: total pool size + */ +struct rpmsg_virtio_shm_pool { + void *base; + size_t avail; + size_t size; +}; + +/** + * struct rpmsg_virtio_device - representation of a rpmsg device based on virtio + * @rdev: rpmsg device, first property in the struct + * @vdev: pointer to the virtio device + * @rvq: pointer to receive virtqueue + * @svq: pointer to send virtqueue + * @shbuf_io: pointer to the shared buffer I/O region + * @shpool: pointer to the shared buffers pool + */ +struct rpmsg_virtio_device { + struct rpmsg_device rdev; + struct virtio_device *vdev; + struct virtqueue *rvq; + struct virtqueue *svq; + struct metal_io_region *shbuf_io; + struct rpmsg_virtio_shm_pool *shpool; +}; + +#define RPMSG_REMOTE VIRTIO_DEV_SLAVE +#define RPMSG_MASTER VIRTIO_DEV_MASTER + +static inline unsigned int +rpmsg_virtio_get_role(struct rpmsg_virtio_device *rvdev) +{ + return rvdev->vdev->role; +} + +static inline void rpmsg_virtio_set_status(struct rpmsg_virtio_device *rvdev, + uint8_t status) +{ + rvdev->vdev->func->set_status(rvdev->vdev, status); +} + +static inline uint8_t rpmsg_virtio_get_status(struct rpmsg_virtio_device *rvdev) +{ + return rvdev->vdev->func->get_status(rvdev->vdev); +} + +static inline uint32_t +rpmsg_virtio_get_features(struct rpmsg_virtio_device *rvdev) +{ + return rvdev->vdev->func->get_features(rvdev->vdev); +} + +static inline void +rpmsg_virtio_read_config(struct rpmsg_virtio_device *rvdev, + uint32_t offset, void *dst, int length) +{ + rvdev->vdev->func->read_config(rvdev->vdev, offset, dst, length); +} + +static inline void +rpmsg_virtio_write_config(struct rpmsg_virtio_device *rvdev, + uint32_t offset, void *dst, int length) +{ + rvdev->vdev->func->write_config(rvdev->vdev, offset, dst, length); +} + +static inline int +rpmsg_virtio_create_virtqueues(struct rpmsg_virtio_device *rvdev, + int flags, unsigned int nvqs, + const char *names[], + vq_callback *callbacks) +{ + return virtio_create_virtqueues(rvdev->vdev, flags, nvqs, names, + callbacks); +} + +/** + * rpmsg_virtio_get_buffer_size - get rpmsg virtio buffer size + * + * @rdev - pointer to the rpmsg device + * + * @return - next available buffer size for text, negative value for failure + */ +int rpmsg_virtio_get_buffer_size(struct rpmsg_device *rdev); + +/** + * rpmsg_init_vdev - initialize rpmsg virtio device + * Master side: + * Initialize RPMsg virtio queues and shared buffers, the address of shm can be + * ANY. In this case, function will get shared memory from system shared memory + * pools. If the vdev has RPMsg name service feature, this API will create an + * name service endpoint. + * + * Slave side: + * This API will not return until the driver ready is set by the master side. + * + * @param rvdev - pointer to the rpmsg virtio device + * @param vdev - pointer to the virtio device + * @param ns_bind_cb - callback handler for name service announcement without + * local endpoints waiting to bind. + * @param shm_io - pointer to the share memory I/O region. + * @param shpool - pointer to shared memory pool. rpmsg_virtio_init_shm_pool has + * to be called first to fill this structure. + * + * @return - status of function execution + */ +int rpmsg_init_vdev(struct rpmsg_virtio_device *rvdev, + struct virtio_device *vdev, + rpmsg_ns_bind_cb ns_bind_cb, + struct metal_io_region *shm_io, + struct rpmsg_virtio_shm_pool *shpool); + +/** + * rpmsg_deinit_vdev - deinitialize rpmsg virtio device + * + * @param rvdev - pointer to the rpmsg virtio device + */ +void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev); + +/** + * rpmsg_virtio_init_shm_pool - initialize default shared buffers pool + * + * RPMsg virtio has default shared buffers pool implementation. + * The memory assigned to this pool will be dedicated to the RPMsg + * virtio. This function has to be called before calling rpmsg_init_vdev, + * to initialize the rpmsg_virtio_shm_pool structure. + * + * @param shpool - pointer to the shared buffers pool structure + * @param shbuf - pointer to the beginning of shared buffers + * @param size - shared buffers total size + */ +void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool, + void *shbuf, size_t size); + +/** + * rpmsg_virtio_get_rpmsg_device - get RPMsg device from RPMsg virtio device + * + * @param rvdev - pointer to RPMsg virtio device + * @return - RPMsg device pointed by RPMsg virtio device + */ +static inline struct rpmsg_device * +rpmsg_virtio_get_rpmsg_device(struct rpmsg_virtio_device *rvdev) +{ + return &rvdev->rdev; +} + +/** + * rpmsg_virtio_shm_pool_get_buffer - get buffer in the shared memory pool + * + * RPMsg virtio has default shared buffers pool implementation. + * The memory assigned to this pool will be dedicated to the RPMsg + * virtio. If you prefer to have other shared buffers allocation, + * you can implement your rpmsg_virtio_shm_pool_get_buffer function. + * + * @param shpool - pointer to the shared buffers pool + * @param size - shared buffers total size + * @return - buffer pointer if free buffer is available, NULL otherwise. + */ +metal_weak void * +rpmsg_virtio_shm_pool_get_buffer(struct rpmsg_virtio_shm_pool *shpool, + size_t size); + +#if defined __cplusplus +} +#endif + +#endif /* _RPMSG_VIRTIO_H_ */ diff --git a/lib/include/openamp/rsc_table_parser.h b/lib/include/openamp/rsc_table_parser.h new file mode 100644 index 0000000..8e12385 --- /dev/null +++ b/lib/include/openamp/rsc_table_parser.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RSC_TABLE_PARSER_H +#define RSC_TABLE_PARSER_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define RSC_TAB_SUPPORTED_VERSION 1 + +/* Standard control request handling. */ +typedef int (*rsc_handler)(struct remoteproc *rproc, void *rsc); + +/** + * handle_rsc_table + * + * This function parses resource table. + * + * @param rproc - pointer to remote remoteproc + * @param rsc_table - resource table to parse + * @param len - size of rsc table + * @param io - pointer to the resource table I/O region + * It can be NULL if the resource table + * is in the local memory. + * + * @returns - execution status + * + */ +int handle_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, size_t len, + struct metal_io_region *io); +int handle_carve_out_rsc(struct remoteproc *rproc, void *rsc); +int handle_trace_rsc(struct remoteproc *rproc, void *rsc); +int handle_vdev_rsc(struct remoteproc *rproc, void *rsc); +int handle_vendor_rsc(struct remoteproc *rproc, void *rsc); + +/** + * find_rsc + * + * find out location of a resource type in the resource table. + * + * @rsc_table - pointer to the resource table + * @rsc_type - type of the resource + * @index - index of the resource of the specified type + * + * return the offset to the resource on success, or 0 on failure + */ +size_t find_rsc(void *rsc_table, unsigned int rsc_type, unsigned int index); + +#if defined __cplusplus +} +#endif + +#endif /* RSC_TABLE_PARSER_H */ diff --git a/lib/include/openamp/virtio.h b/lib/include/openamp/virtio.h new file mode 100644 index 0000000..55c8ea5 --- /dev/null +++ b/lib/include/openamp/virtio.h @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_H_ +#define _VIRTIO_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* VirtIO device IDs. */ +#define VIRTIO_ID_NETWORK 0x01UL +#define VIRTIO_ID_BLOCK 0x02UL +#define VIRTIO_ID_CONSOLE 0x03UL +#define VIRTIO_ID_ENTROPY 0x04UL +#define VIRTIO_ID_BALLOON 0x05UL +#define VIRTIO_ID_IOMEMORY 0x06UL +#define VIRTIO_ID_RPMSG 0x07UL /* remote processor messaging */ +#define VIRTIO_ID_SCSI 0x08UL +#define VIRTIO_ID_9P 0x09UL +#define VIRTIO_DEV_ANY_ID (-1)UL + +/* Status byte for guest to report progress. */ +#define VIRTIO_CONFIG_STATUS_ACK 0x01 +#define VIRTIO_CONFIG_STATUS_DRIVER 0x02 +#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04 +#define VIRTIO_CONFIG_STATUS_NEEDS_RESET 0x40 +#define VIRTIO_CONFIG_STATUS_FAILED 0x80 + +/* Virtio device role */ +#define VIRTIO_DEV_MASTER 0UL +#define VIRTIO_DEV_SLAVE 1UL + +struct virtio_device_id { + uint32_t device; + uint32_t vendor; +}; + +/* + * 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 void (*virtio_dev_reset_cb)(struct virtio_device *vdev); + +struct virtio_dispatch; + +struct virtio_feature_desc { + uint32_t vfd_val; + const char *vfd_str; +}; + +/** + * struct virtio_vring_info + * @vq virtio queue + * @info vring alloc info + * @notifyid vring notify id + * @io metal I/O region of the vring memory, can be NULL + */ +struct virtio_vring_info { + struct virtqueue *vq; + struct vring_alloc_info info; + uint32_t notifyid; + struct metal_io_region *io; +}; + +/* + * Structure definition for virtio devices for use by the + * applications/drivers + */ + +struct virtio_device { + uint32_t notifyid; /**< unique position on the virtio bus */ + struct virtio_device_id id; /**< the device type identification + * (used to match it with a driver + */ + uint64_t features; /**< the features supported by both ends. */ + unsigned int role; /**< if it is virtio backend or front end. */ + virtio_dev_reset_cb reset_cb; /**< user registered device callback */ + const struct virtio_dispatch *func; /**< Virtio dispatch table */ + void *priv; /**< TODO: remove pointer to virtio_device private data */ + unsigned int vrings_num; /**< number of vrings */ + struct virtio_vring_info *vrings_info; +}; + +/* + * 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 { + 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); + void (*notify)(struct virtqueue *vq); +}; + +int virtio_create_virtqueues(struct virtio_device *vdev, unsigned int flags, + unsigned int nvqs, const char *names[], + vq_callback callbacks[]); + +#if defined __cplusplus +} +#endif + +#endif /* _VIRTIO_H_ */ diff --git a/lib/include/openamp/virtio_ring.h b/lib/include/openamp/virtio_ring.h new file mode 100644 index 0000000..af2dd43 --- /dev/null +++ b/lib/include/openamp/virtio_ring.h @@ -0,0 +1,161 @@ +/* + * Copyright Rusty Russell IBM Corporation 2007. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * $FreeBSD$ + */ + +#ifndef VIRTIO_RING_H +#define VIRTIO_RING_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* 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". + */ +METAL_PACKED_BEGIN +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; +} METAL_PACKED_END; + +METAL_PACKED_BEGIN +struct vring_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[0]; +} METAL_PACKED_END; + +/* uint32_t is used here for ids for padding reasons. */ +METAL_PACKED_BEGIN +struct vring_used_elem { + union { + uint16_t event; + /* Index of start of used descriptor chain. */ + uint32_t id; + }; + /* Total length of the descriptor chain which was written to. */ + uint32_t len; +} METAL_PACKED_END; + +METAL_PACKED_BEGIN +struct vring_used { + uint16_t flags; + uint16_t idx; + struct vring_used_elem ring[0]; +} METAL_PACKED_END; + +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) ((vr)->used->ring[(vr)->num].event) + +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 = (struct vring_used *) + (((unsigned long)&vr->avail->ring[num] + sizeof(uint16_t) + + 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); +} + +#if defined __cplusplus +} +#endif + +#endif /* VIRTIO_RING_H */ diff --git a/lib/include/openamp/virtqueue.h b/lib/include/openamp/virtqueue.h new file mode 100644 index 0000000..ed86f20 --- /dev/null +++ b/lib/include/openamp/virtqueue.h @@ -0,0 +1,230 @@ +#ifndef VIRTQUEUE_H_ +#define VIRTQUEUE_H_ + +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * $FreeBSD$ + */ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* 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 VQUEUE_SUCCESS 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 + +/* 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) + +struct virtqueue_buf { + void *buf; + int len; +}; + +struct vq_desc_extra { + void *cookie; + uint16_t ndescs; +}; + +struct virtqueue { + struct virtio_device *vq_dev; + const char *vq_name; + uint16_t vq_queue_index; + uint16_t vq_nentries; + void (*callback)(struct virtqueue *vq); + void (*notify)(struct virtqueue *vq); + struct vring vq_ring; + uint16_t vq_free_cnt; + uint16_t vq_queued_cnt; + void *shm_io; /* opaque pointer to data needed to allow v2p & p2v */ + + /* + * 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; + +#ifdef VQUEUE_DEBUG + bool vq_inuse; +#endif + + /* + * 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 vq_descx[0]; +}; + +/* struct to hold vring specific information */ +struct vring_alloc_info { + void *vaddr; + uint32_t align; + uint16_t num_descs; + uint16_t pad; +}; + +typedef void (*vq_callback)(struct virtqueue *); +typedef void (*vq_notify)(struct virtqueue *); + +#ifdef VQUEUE_DEBUG +#include +#include + +#define VQASSERT(_vq, _exp, _msg) \ + do { \ + if (!(_exp)) { \ + metal_log(METAL_LOG_EMERGENCY, \ + "%s: %s - "_msg, __func__, (_vq)->vq_name); \ + metal_assert(_exp); \ + } \ + } 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) \ + do { \ + if (((status_var) == 0) && (condition)) { \ + status_var = status_err; \ + } \ + } while (0) + +#define VQUEUE_BUSY(vq) \ + do { \ + if (!(vq)->vq_inuse) \ + (vq)->vq_inuse = true; \ + else \ + VQASSERT(vq, !(vq)->vq_inuse,\ + "VirtQueue already in use") \ + } while (0) + +#define VQUEUE_IDLE(vq) ((vq)->vq_inuse = false) + +#else + +#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, + const char *name, struct vring_alloc_info *ring, + void (*callback)(struct virtqueue *vq), + void (*notify)(struct virtqueue *vq), + struct virtqueue *vq); + +/* + * virtqueue_set_shmem_io + * + * set virtqueue shared memory I/O region + * + * @vq - virt queue + * @io - pointer to the shared memory I/O region + */ +static inline void virtqueue_set_shmem_io(struct virtqueue *vq, + struct metal_io_region *io) +{ + vq->shm_io = io; +} + +int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list, + int readable, int writable, void *cookie); + +void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx); + +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, + uint32_t len); + +void virtqueue_disable_cb(struct virtqueue *vq); + +int virtqueue_enable_cb(struct virtqueue *vq); + +void virtqueue_kick(struct virtqueue *vq); + +static inline struct virtqueue *virtqueue_allocate(unsigned int num_desc_extra) +{ + struct virtqueue *vqs; + uint32_t vq_size = sizeof(struct virtqueue) + + num_desc_extra * sizeof(struct vq_desc_extra); + + vqs = (struct virtqueue *)metal_allocate_memory(vq_size); + if (vqs) { + memset(vqs, 0x00, vq_size); + } + + return vqs; +} + +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); + +uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx); + +#if defined __cplusplus +} +#endif + +#endif /* VIRTQUEUE_H_ */ diff --git a/lib/proxy/CMakeLists.txt b/lib/proxy/CMakeLists.txt new file mode 100644 index 0000000..4cbc599 --- /dev/null +++ b/lib/proxy/CMakeLists.txt @@ -0,0 +1 @@ +collect (PROJECT_LIB_SOURCES rpmsg_retarget.c) diff --git a/lib/proxy/rpmsg_retarget.c b/lib/proxy/rpmsg_retarget.c new file mode 100644 index 0000000..aa91b67 --- /dev/null +++ b/lib/proxy/rpmsg_retarget.c @@ -0,0 +1,341 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************* + * Description + * This files contains rpmsg based redefinitions for C RTL system calls + * such as _open, _read, _write, _close. + *************************************************************************/ +static struct rpmsg_rpc_data *rpmsg_default_rpc; + +static int rpmsg_rpc_ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len, + uint32_t src, void *priv) +{ + struct rpmsg_rpc_syscall *syscall; + + (void)priv; + (void)src; + + if (data && ept) { + syscall = data; + if (syscall->id == TERM_SYSCALL_ID) { + rpmsg_destroy_ept(ept); + } else { + struct rpmsg_rpc_data *rpc; + + rpc = metal_container_of(ept, + struct rpmsg_rpc_data, + ept); + metal_spinlock_acquire(&rpc->buflock); + if (rpc->respbuf && rpc->respbuf_len != 0) { + if (len > rpc->respbuf_len) + len = rpc->respbuf_len; + memcpy(rpc->respbuf, data, len); + } + atomic_flag_clear(&rpc->nacked); + metal_spinlock_release(&rpc->buflock); + } + } + + return RPMSG_SUCCESS; +} + +static void rpmsg_service_unbind(struct rpmsg_endpoint *ept) +{ + struct rpmsg_rpc_data *rpc; + + rpc = metal_container_of(ept, struct rpmsg_rpc_data, ept); + rpc->ept_destroyed = 1; + rpmsg_destroy_ept(ept); + atomic_flag_clear(&rpc->nacked); + if (rpc->shutdown_cb) + rpc->shutdown_cb(rpc); +} + +int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc, + struct rpmsg_device *rdev, + const char *ept_name, uint32_t ept_addr, + uint32_t ept_raddr, + void *poll_arg, rpmsg_rpc_poll poll, + rpmsg_rpc_shutdown_cb shutdown_cb) +{ + int ret; + + if (!rpc || !rdev) + return -EINVAL; + metal_spinlock_init(&rpc->buflock); + metal_mutex_init(&rpc->lock); + rpc->shutdown_cb = shutdown_cb; + rpc->poll_arg = poll_arg; + rpc->poll = poll; + rpc->ept_destroyed = 0; + rpc->respbuf = NULL; + rpc->respbuf_len = 0; + atomic_init(&rpc->nacked, 1); + ret = rpmsg_create_ept(&rpc->ept, rdev, + ept_name, ept_addr, ept_raddr, + rpmsg_rpc_ept_cb, rpmsg_service_unbind); + if (ret != 0) { + metal_mutex_release(&rpc->lock); + return -EINVAL; + } + while (!is_rpmsg_ept_ready(&rpc->ept)) { + if (rpc->poll) + rpc->poll(rpc->poll_arg); + } + return 0; +} + +void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc) +{ + if (!rpc) + return; + if (rpc->ept_destroyed == 0) + rpmsg_destroy_ept(&rpc->ept); + metal_mutex_acquire(&rpc->lock); + metal_spinlock_acquire(&rpc->buflock); + rpc->respbuf = NULL; + rpc->respbuf_len = 0; + metal_spinlock_release(&rpc->buflock); + metal_mutex_release(&rpc->lock); + metal_mutex_deinit(&rpc->lock); +} + +int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc, + void *req, size_t len, + void *resp, size_t resp_len) +{ + int ret; + + if (!rpc) + return -EINVAL; + metal_spinlock_acquire(&rpc->buflock); + rpc->respbuf = resp; + rpc->respbuf_len = resp_len; + metal_spinlock_release(&rpc->buflock); + (void)atomic_flag_test_and_set(&rpc->nacked); + ret = rpmsg_send(&rpc->ept, req, len); + if (ret < 0) + return -EINVAL; + if (!resp) + return ret; + while ((atomic_flag_test_and_set(&rpc->nacked))) { + if (rpc->poll) + rpc->poll(rpc->poll_arg); + } + return ret; +} + +void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc) +{ + if (!rpc) + return; + rpmsg_default_rpc = rpc; +} + +/************************************************************************* + * + * FUNCTION + * + * _open + * + * DESCRIPTION + * + * Open a file. Minimal implementation + * + *************************************************************************/ +#define MAX_BUF_LEN 496UL + +int _open(const char *filename, int flags, int mode) +{ + struct rpmsg_rpc_data *rpc = rpmsg_default_rpc; + struct rpmsg_rpc_syscall *syscall; + struct rpmsg_rpc_syscall resp; + int filename_len = strlen(filename) + 1; + unsigned int payload_size = sizeof(*syscall) + filename_len; + unsigned char tmpbuf[MAX_BUF_LEN]; + int ret; + + if (!filename || payload_size > (int)MAX_BUF_LEN) { + return -EINVAL; + } + + if (!rpc) + return -EINVAL; + + /* Construct rpc payload */ + syscall = (struct rpmsg_rpc_syscall *)tmpbuf; + syscall->id = OPEN_SYSCALL_ID; + syscall->args.int_field1 = flags; + syscall->args.int_field2 = mode; + syscall->args.data_len = filename_len; + memcpy(tmpbuf + sizeof(*syscall), filename, filename_len); + + resp.id = 0; + ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size, + (void *)&resp, sizeof(resp)); + if (ret >= 0) { + /* Obtain return args and return to caller */ + if (resp.id == OPEN_SYSCALL_ID) + ret = resp.args.int_field1; + else + ret = -EINVAL; + } + + return ret; +} + +/************************************************************************* + * + * FUNCTION + * + * _read + * + * DESCRIPTION + * + * Low level function to redirect IO to serial. + * + *************************************************************************/ +int _read(int fd, char *buffer, int buflen) +{ + struct rpmsg_rpc_syscall syscall; + struct rpmsg_rpc_syscall *resp; + struct rpmsg_rpc_data *rpc = rpmsg_default_rpc; + int payload_size = sizeof(syscall); + unsigned char tmpbuf[MAX_BUF_LEN]; + int ret; + + if (!rpc || !buffer || buflen == 0) + return -EINVAL; + + /* Construct rpc payload */ + syscall.id = READ_SYSCALL_ID; + syscall.args.int_field1 = fd; + syscall.args.int_field2 = buflen; + syscall.args.data_len = 0; /*not used */ + + resp = (struct rpmsg_rpc_syscall *)tmpbuf; + resp->id = 0; + ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size, + tmpbuf, sizeof(tmpbuf)); + + /* Obtain return args and return to caller */ + if (ret >= 0) { + if (resp->id == READ_SYSCALL_ID) { + if (resp->args.int_field1 > 0) { + int tmplen = resp->args.data_len; + unsigned char *tmpptr = tmpbuf; + + tmpptr += sizeof(*resp); + if (tmplen > buflen) + tmplen = buflen; + memcpy(buffer, tmpptr, tmplen); + } + ret = resp->args.int_field1; + } else { + ret = -EINVAL; + } + } + + return ret; +} + +/************************************************************************* + * + * FUNCTION + * + * _write + * + * DESCRIPTION + * + * Low level function to redirect IO to serial. + * + *************************************************************************/ +int _write(int fd, const char *ptr, int len) +{ + int ret; + struct rpmsg_rpc_syscall *syscall; + struct rpmsg_rpc_syscall resp; + int payload_size = sizeof(*syscall) + len; + struct rpmsg_rpc_data *rpc = rpmsg_default_rpc; + unsigned char tmpbuf[MAX_BUF_LEN]; + unsigned char *tmpptr; + int null_term = 0; + + if (!rpc) + return -EINVAL; + if (fd == 1) + null_term = 1; + + syscall = (struct rpmsg_rpc_syscall *)tmpbuf; + syscall->id = WRITE_SYSCALL_ID; + syscall->args.int_field1 = fd; + syscall->args.int_field2 = len; + syscall->args.data_len = len + null_term; + tmpptr = tmpbuf + sizeof(*syscall); + memcpy(tmpptr, ptr, len); + if (null_term == 1) { + *(char *)(tmpptr + len + null_term) = 0; + payload_size += 1; + } + resp.id = 0; + ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size, + (void *)&resp, sizeof(resp)); + + if (ret >= 0) { + if (resp.id == WRITE_SYSCALL_ID) + ret = resp.args.int_field1; + else + ret = -EINVAL; + } + + return ret; + +} + +/************************************************************************* + * + * FUNCTION + * + * _close + * + * DESCRIPTION + * + * Close a file. Minimal implementation + * + *************************************************************************/ +int _close(int fd) +{ + int ret; + struct rpmsg_rpc_syscall syscall; + struct rpmsg_rpc_syscall resp; + int payload_size = sizeof(syscall); + struct rpmsg_rpc_data *rpc = rpmsg_default_rpc; + + if (!rpc) + return -EINVAL; + syscall.id = CLOSE_SYSCALL_ID; + syscall.args.int_field1 = fd; + syscall.args.int_field2 = 0; /*not used */ + syscall.args.data_len = 0; /*not used */ + + resp.id = 0; + ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size, + (void *)&resp, sizeof(resp)); + + if (ret >= 0) { + if (resp.id == CLOSE_SYSCALL_ID) + ret = resp.args.int_field1; + else + ret = -EINVAL; + } + + return ret; +} diff --git a/lib/proxy/rpmsg_retarget.o b/lib/proxy/rpmsg_retarget.o new file mode 100644 index 0000000..6ba1dae Binary files /dev/null and b/lib/proxy/rpmsg_retarget.o differ diff --git a/lib/remoteproc/CMakeLists.txt b/lib/remoteproc/CMakeLists.txt new file mode 100644 index 0000000..158df1d --- /dev/null +++ b/lib/remoteproc/CMakeLists.txt @@ -0,0 +1,4 @@ +collect (PROJECT_LIB_SOURCES elf_loader.c) +collect (PROJECT_LIB_SOURCES remoteproc.c) +collect (PROJECT_LIB_SOURCES remoteproc_virtio.c) +collect (PROJECT_LIB_SOURCES rsc_table_parser.c) diff --git a/lib/remoteproc/elf_loader.c b/lib/remoteproc/elf_loader.c new file mode 100644 index 0000000..057f465 --- /dev/null +++ b/lib/remoteproc/elf_loader.c @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static int elf_is_64(const void *elf_info) +{ + const unsigned char *tmp = elf_info; + + if (tmp[EI_CLASS] == ELFCLASS64) + return 1; + else + return 0; +} + +static size_t elf_ehdr_size(const void *elf_info) +{ + if (!elf_info) + return sizeof(Elf64_Ehdr); + else if (elf_is_64(elf_info) != 0) + return sizeof(Elf64_Ehdr); + else + return sizeof(Elf32_Ehdr); +} + +static size_t elf_phoff(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_phoff; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_phoff; + } +} + +static size_t elf_phentsize(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_phentsize; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_phentsize; + } +} + +static int elf_phnum(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_phnum; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_phnum; + } +} + +static size_t elf_shoff(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_shoff; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_shoff; + } +} + +static size_t elf_shentsize(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_shentsize; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_shentsize; + } +} + +static int elf_shnum(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_shnum; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_shnum; + } +} + +static int elf_shstrndx(const void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Ehdr *ehdr = elf_info; + + return ehdr->e_shstrndx; + } else { + const Elf64_Ehdr *ehdr = elf_info; + + return ehdr->e_shstrndx; + } +} + +static void **elf_phtable_ptr(void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + + return (void **)&einfo->phdrs; + } else { + struct elf64_info *einfo = elf_info; + + return (void **)&einfo->phdrs; + } +} + +static void **elf_shtable_ptr(void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + + return (void **)&einfo->shdrs; + } else { + struct elf64_info *einfo = elf_info; + + return (void **)&einfo->shdrs; + } +} + +static void **elf_shstrtab_ptr(void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + + return &einfo->shstrtab; + } else { + struct elf64_info *einfo = elf_info; + + return &einfo->shstrtab; + } +} + +static int *elf_load_state(void *elf_info) +{ + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + + return &einfo->load_state; + } else { + struct elf64_info *einfo = elf_info; + + return &einfo->load_state; + } +} + +static void elf_parse_segment(void *elf_info, const void *elf_phdr, + unsigned int *p_type, size_t *p_offset, + metal_phys_addr_t *p_vaddr, + metal_phys_addr_t *p_paddr, + size_t *p_filesz, size_t *p_memsz) +{ + if (elf_is_64(elf_info) == 0) { + const Elf32_Phdr *phdr = elf_phdr; + + if (p_type) + *p_type = (unsigned int)phdr->p_type; + if (p_offset) + *p_offset = (size_t)phdr->p_offset; + if (p_vaddr) + *p_vaddr = (metal_phys_addr_t)phdr->p_vaddr; + if (p_paddr) + *p_paddr = (metal_phys_addr_t)phdr->p_paddr; + if (p_filesz) + *p_filesz = (size_t)phdr->p_filesz; + if (p_memsz) + *p_memsz = (size_t)phdr->p_memsz; + } else { + const Elf64_Phdr *phdr = elf_phdr; + + if (p_type) + *p_type = (unsigned int)phdr->p_type; + if (p_offset) + *p_offset = (size_t)phdr->p_offset; + if (p_vaddr) + *p_vaddr = (metal_phys_addr_t)phdr->p_vaddr; + if (p_paddr) + *p_paddr = (metal_phys_addr_t)phdr->p_paddr; + if (p_filesz) + *p_filesz = (size_t)phdr->p_filesz; + if (p_memsz) + *p_memsz = (size_t)phdr->p_memsz; + } +} + +static const void *elf_get_segment_from_index(void *elf_info, int index) +{ + if (elf_is_64(elf_info) == 0) { + const struct elf32_info *einfo = elf_info; + const Elf32_Ehdr *ehdr = &einfo->ehdr; + const Elf32_Phdr *phdrs = einfo->phdrs; + + if (!phdrs) + return NULL; + if (index < 0 || index >= ehdr->e_phnum) + return NULL; + return &phdrs[index]; + } else { + const struct elf64_info *einfo = elf_info; + const Elf64_Ehdr *ehdr = &einfo->ehdr; + const Elf64_Phdr *phdrs = einfo->phdrs; + + if (!phdrs) + return NULL; + if (index < 0 || index >= ehdr->e_phnum) + return NULL; + return &phdrs[index]; + } +} + +static void *elf_get_section_from_name(void *elf_info, const char *name) +{ + unsigned int i; + const char *name_table; + + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + Elf32_Ehdr *ehdr = &einfo->ehdr; + Elf32_Shdr *shdr = einfo->shdrs; + + name_table = einfo->shstrtab; + if (!shdr || !name_table) + return NULL; + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (strcmp(name, name_table + shdr->sh_name)) + continue; + else + return shdr; + } + } else { + struct elf64_info *einfo = elf_info; + Elf64_Ehdr *ehdr = &einfo->ehdr; + Elf64_Shdr *shdr = einfo->shdrs; + + name_table = einfo->shstrtab; + if (!shdr || !name_table) + return NULL; + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + if (strcmp(name, name_table + shdr->sh_name)) + continue; + else + return shdr; + } + } + return NULL; +} + +static void *elf_get_section_from_index(void *elf_info, int index) +{ + if (elf_is_64(elf_info) == 0) { + struct elf32_info *einfo = elf_info; + Elf32_Ehdr *ehdr = &einfo->ehdr; + Elf32_Shdr *shdr = einfo->shdrs; + + if (!shdr) + return NULL; + if (index < 0 || index >= ehdr->e_shnum) + return NULL; + return &einfo->shdrs[index]; + } else { + struct elf64_info *einfo = elf_info; + Elf64_Ehdr *ehdr = &einfo->ehdr; + Elf64_Shdr *shdr = einfo->shdrs; + + if (!shdr) + return NULL; + if (index < 0 || index >= ehdr->e_shnum) + return NULL; + return &einfo->shdrs[index]; + } +} + +static void elf_parse_section(void *elf_info, void *elf_shdr, + unsigned int *sh_type, unsigned int *sh_flags, + metal_phys_addr_t *sh_addr, + size_t *sh_offset, size_t *sh_size, + unsigned int *sh_link, unsigned int *sh_info, + unsigned int *sh_addralign, + size_t *sh_entsize) +{ + if (elf_is_64(elf_info) == 0) { + Elf32_Shdr *shdr = elf_shdr; + + if (sh_type) + *sh_type = shdr->sh_type; + if (sh_flags) + *sh_flags = shdr->sh_flags; + if (sh_addr) + *sh_addr = (metal_phys_addr_t)shdr->sh_addr; + if (sh_offset) + *sh_offset = shdr->sh_offset; + if (sh_size) + *sh_size = shdr->sh_size; + if (sh_link) + *sh_link = shdr->sh_link; + if (sh_info) + *sh_info = shdr->sh_info; + if (sh_addralign) + *sh_addralign = shdr->sh_addralign; + if (sh_entsize) + *sh_entsize = shdr->sh_entsize; + } else { + Elf64_Shdr *shdr = elf_shdr; + + if (sh_type) + *sh_type = shdr->sh_type; + if (sh_flags) + *sh_flags = shdr->sh_flags; + if (sh_addr) + *sh_addr = (metal_phys_addr_t)shdr->sh_addr; + if (sh_offset) + *sh_offset = shdr->sh_offset; + if (sh_size) + *sh_size = shdr->sh_size; + if (sh_link) + *sh_link = shdr->sh_link; + if (sh_info) + *sh_info = shdr->sh_info; + if (sh_addralign) + *sh_addralign = shdr->sh_addralign; + if (sh_entsize) + *sh_entsize = shdr->sh_entsize; + } +} + +static const void *elf_next_load_segment(void *elf_info, int *nseg, + metal_phys_addr_t *da, + size_t *noffset, size_t *nfsize, + size_t *nmsize) +{ + const void *phdr = PT_NULL; + unsigned int p_type = PT_NULL; + + if (!elf_info || !nseg) + return NULL; + while (p_type != PT_LOAD) { + phdr = elf_get_segment_from_index(elf_info, *nseg); + if (!phdr) + return NULL; + elf_parse_segment(elf_info, phdr, &p_type, noffset, + da, NULL, nfsize, nmsize); + *nseg = *nseg + 1; + } + return phdr; +} + +static size_t elf_info_size(const void *img_data) +{ + if (elf_is_64(img_data) == 0) + return sizeof(struct elf32_info); + else + return sizeof(struct elf64_info); +} + +int elf_identify(const void *img_data, size_t len) +{ + if (len < SELFMAG || !img_data) + return -RPROC_EINVAL; + if (memcmp(img_data, ELFMAG, SELFMAG) != 0) + return -RPROC_EINVAL; + else + return 0; +} + +int elf_load_header(const void *img_data, size_t offset, size_t len, + void **img_info, int last_load_state, + size_t *noffset, size_t *nlen) +{ + int *load_state; + + metal_assert(noffset); + metal_assert(nlen); + /* Get ELF header */ + if (last_load_state == ELF_STATE_INIT) { + size_t tmpsize; + + metal_log(METAL_LOG_DEBUG, "Loading ELF headering\r\n"); + tmpsize = elf_ehdr_size(img_data); + if (len < tmpsize) { + *noffset = 0; + *nlen = tmpsize; + return ELF_STATE_INIT; + } else { + size_t infosize = elf_info_size(img_data); + + if (!*img_info) { + *img_info = metal_allocate_memory(infosize); + if (!*img_info) + return -RPROC_ENOMEM; + memset(*img_info, 0, infosize); + } + memcpy(*img_info, img_data, tmpsize); + load_state = elf_load_state(*img_info); + *load_state = ELF_STATE_WAIT_FOR_PHDRS; + last_load_state = ELF_STATE_WAIT_FOR_PHDRS; + } + } + metal_assert(*img_info); + load_state = elf_load_state(*img_info); + if (last_load_state != *load_state) + return -RPROC_EINVAL; + /* Get ELF program headers */ + if (*load_state == ELF_STATE_WAIT_FOR_PHDRS) { + size_t phdrs_size; + size_t phdrs_offset; + void **phdrs; + const void *img_phdrs; + + metal_log(METAL_LOG_DEBUG, "Loading ELF program header.\r\n"); + phdrs_offset = elf_phoff(*img_info); + phdrs_size = elf_phnum(*img_info) * elf_phentsize(*img_info); + if (offset > phdrs_offset || + offset + len < phdrs_offset + phdrs_size) { + *noffset = phdrs_offset; + *nlen = phdrs_size; + return *load_state; + } + /* calculate the programs headers offset to the image_data */ + phdrs_offset -= offset; + img_phdrs = (const char *)img_data + phdrs_offset; + phdrs = elf_phtable_ptr(*img_info); + *phdrs = metal_allocate_memory(phdrs_size); + if (!*phdrs) + return -RPROC_ENOMEM; + memcpy(*phdrs, img_phdrs, phdrs_size); + *load_state = ELF_STATE_WAIT_FOR_SHDRS | + RPROC_LOADER_READY_TO_LOAD; + } + /* Get ELF Section Headers */ + if ((*load_state & ELF_STATE_WAIT_FOR_SHDRS) != 0) { + size_t shdrs_size; + size_t shdrs_offset; + void **shdrs; + const void *img_shdrs; + + metal_log(METAL_LOG_DEBUG, "Loading ELF section header.\r\n"); + shdrs_offset = elf_shoff(*img_info); + if (elf_shnum(*img_info) == 0) { + *load_state = (*load_state & (~ELF_STATE_MASK)) | + ELF_STATE_HDRS_COMPLETE; + *nlen = 0; + return *load_state; + } + shdrs_size = elf_shnum(*img_info) * elf_shentsize(*img_info); + if (offset > shdrs_offset || + offset + len < shdrs_offset + shdrs_size) { + *noffset = shdrs_offset; + *nlen = shdrs_size; + return *load_state; + } + /* calculate the sections headers offset to the image_data */ + shdrs_offset -= offset; + img_shdrs = (const char *)img_data + shdrs_offset; + shdrs = elf_shtable_ptr(*img_info); + *shdrs = metal_allocate_memory(shdrs_size); + if (!*shdrs) + return -RPROC_ENOMEM; + memcpy(*shdrs, img_shdrs, shdrs_size); + *load_state = (*load_state & (~ELF_STATE_MASK)) | + ELF_STATE_WAIT_FOR_SHSTRTAB; + metal_log(METAL_LOG_DEBUG, + "Loading ELF section header complete.\r\n"); + } + /* Get ELF SHSTRTAB section */ + if ((*load_state & ELF_STATE_WAIT_FOR_SHSTRTAB) != 0) { + size_t shstrtab_size; + size_t shstrtab_offset; + int shstrndx; + void *shdr; + void **shstrtab; + + metal_log(METAL_LOG_DEBUG, "Loading ELF shstrtab.\r\n"); + shstrndx = elf_shstrndx(*img_info); + shdr = elf_get_section_from_index(*img_info, shstrndx); + if (!shdr) + return -RPROC_EINVAL; + elf_parse_section(*img_info, shdr, NULL, NULL, + NULL, &shstrtab_offset, + &shstrtab_size, NULL, NULL, + NULL, NULL); + if (offset > shstrtab_offset || + offset + len < shstrtab_offset + shstrtab_size) { + *noffset = shstrtab_offset; + *nlen = shstrtab_size; + return *load_state; + } + /* Calculate shstrtab section offset to the input image data */ + shstrtab_offset -= offset; + shstrtab = elf_shstrtab_ptr(*img_info); + *shstrtab = metal_allocate_memory(shstrtab_size); + if (!*shstrtab) + return -RPROC_ENOMEM; + memcpy(*shstrtab, + (const char *)img_data + shstrtab_offset, + shstrtab_size); + *load_state = (*load_state & (~ELF_STATE_MASK)) | + ELF_STATE_HDRS_COMPLETE; + *nlen = 0; + return *load_state; + } + return last_load_state; +} + +int elf_load(struct remoteproc *rproc, + const void *img_data, size_t offset, size_t len, + void **img_info, int last_load_state, + metal_phys_addr_t *da, + size_t *noffset, size_t *nlen, + unsigned char *padding, size_t *nmemsize) +{ + int *load_state; + const void *phdr; + + (void)rproc; + metal_assert(da); + metal_assert(noffset); + metal_assert(nlen); + if ((last_load_state & RPROC_LOADER_MASK) == RPROC_LOADER_NOT_READY) { + metal_log(METAL_LOG_DEBUG, + "needs to load header first\r\n"); + last_load_state = elf_load_header(img_data, offset, len, + img_info, last_load_state, + noffset, nlen); + if ((last_load_state & RPROC_LOADER_MASK) == + RPROC_LOADER_NOT_READY) { + *da = RPROC_LOAD_ANYADDR; + return last_load_state; + } + } + metal_assert(img_info && *img_info); + load_state = elf_load_state(*img_info); + /* For ELF, segment padding value is 0 */ + if (padding) + *padding = 0; + if ((*load_state & RPROC_LOADER_READY_TO_LOAD) != 0) { + int nsegment; + size_t nsegmsize = 0; + size_t nsize = 0; + int phnums = 0; + + nsegment = *load_state & ELF_NEXT_SEGMENT_MASK; + phdr = elf_next_load_segment(*img_info, &nsegment, da, + noffset, &nsize, &nsegmsize); + if (!phdr) { + metal_log(METAL_LOG_DEBUG, "cannot find more segment\r\n"); + *load_state = (*load_state & (~ELF_NEXT_SEGMENT_MASK)) | + (nsegment & ELF_NEXT_SEGMENT_MASK); + return *load_state; + } + *nlen = nsize; + *nmemsize = nsegmsize; + phnums = elf_phnum(*img_info); + metal_log(METAL_LOG_DEBUG, "segment: %d, total segs %d\r\n", + nsegment, phnums); + if (nsegment == phnums) { + *load_state = (*load_state & (~RPROC_LOADER_MASK)) | + RPROC_LOADER_POST_DATA_LOAD; + } + *load_state = (*load_state & (~ELF_NEXT_SEGMENT_MASK)) | + (nsegment & ELF_NEXT_SEGMENT_MASK); + } else if ((*load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) { + if ((*load_state & ELF_STATE_HDRS_COMPLETE) == 0) { + last_load_state = elf_load_header(img_data, offset, + len, img_info, + last_load_state, + noffset, nlen); + if (last_load_state < 0) + return last_load_state; + if ((last_load_state & ELF_STATE_HDRS_COMPLETE) != 0) { + *load_state = (*load_state & + (~RPROC_LOADER_MASK)) | + RPROC_LOADER_LOAD_COMPLETE; + *nlen = 0; + } + *da = RPROC_LOAD_ANYADDR; + } else { + /* TODO: will handle relocate later */ + *nlen = 0; + *load_state = (*load_state & + (~RPROC_LOADER_MASK)) | + RPROC_LOADER_LOAD_COMPLETE; + } + } + return *load_state; +} + +void elf_release(void *img_info) +{ + if (!img_info) + return; + if (elf_is_64(img_info) == 0) { + struct elf32_info *elf_info = img_info; + + if (elf_info->phdrs) + metal_free_memory(elf_info->phdrs); + if (elf_info->shdrs) + metal_free_memory(elf_info->shdrs); + if (elf_info->shstrtab) + metal_free_memory(elf_info->shstrtab); + metal_free_memory(img_info); + + } else { + struct elf64_info *elf_info = img_info; + + if (elf_info->phdrs) + metal_free_memory(elf_info->phdrs); + if (elf_info->shdrs) + metal_free_memory(elf_info->shdrs); + if (elf_info->shstrtab) + metal_free_memory(elf_info->shstrtab); + metal_free_memory(img_info); + } +} + +metal_phys_addr_t elf_get_entry(void *elf_info) +{ + if (!elf_info) + return METAL_BAD_PHYS; + + if (elf_is_64(elf_info) == 0) { + Elf32_Ehdr *elf_ehdr = elf_info; + Elf32_Addr e_entry; + + e_entry = elf_ehdr->e_entry; + return (metal_phys_addr_t)e_entry; + } else { + Elf64_Ehdr *elf_ehdr = elf_info; + Elf64_Addr e_entry; + + e_entry = elf_ehdr->e_entry; + return (metal_phys_addr_t)e_entry; + } +} + +int elf_locate_rsc_table(void *elf_info, metal_phys_addr_t *da, + size_t *offset, size_t *size) +{ + char *sect_name = ".resource_table"; + void *shdr; + int *load_state; + + if (!elf_info) + return -RPROC_EINVAL; + + load_state = elf_load_state(elf_info); + if ((*load_state & ELF_STATE_HDRS_COMPLETE) == 0) + return -RPROC_ERR_LOADER_STATE; + shdr = elf_get_section_from_name(elf_info, sect_name); + if (!shdr) { + metal_assert(size); + *size = 0; + return 0; + } + elf_parse_section(elf_info, shdr, NULL, NULL, + da, offset, size, + NULL, NULL, NULL, NULL); + return 0; +} + +int elf_get_load_state(void *img_info) +{ + int *load_state; + + if (!img_info) + return -RPROC_EINVAL; + load_state = elf_load_state(img_info); + return *load_state; +} + +struct loader_ops elf_ops = { + .load_header = elf_load_header, + .load_data = elf_load, + .locate_rsc_table = elf_locate_rsc_table, + .release = elf_release, + .get_entry = elf_get_entry, + .get_load_state = elf_get_load_state, +}; diff --git a/lib/remoteproc/elf_loader.o b/lib/remoteproc/elf_loader.o new file mode 100644 index 0000000..6bbace4 Binary files /dev/null and b/lib/remoteproc/elf_loader.o differ diff --git a/lib/remoteproc/remoteproc.c b/lib/remoteproc/remoteproc.c new file mode 100644 index 0000000..57e8ee6 --- /dev/null +++ b/lib/remoteproc/remoteproc.c @@ -0,0 +1,981 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2015 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * static functions + *****************************************************************************/ +static struct loader_ops * +remoteproc_check_fw_format(const void *img_data, size_t img_len) +{ + if (img_len <= 0) + return NULL; + else if (elf_identify(img_data, img_len) == 0) + return &elf_ops; + else + return NULL; +} + +static struct remoteproc_mem * +remoteproc_get_mem(struct remoteproc *rproc, const char *name, + metal_phys_addr_t pa, metal_phys_addr_t da, + void *va, size_t size) +{ + struct metal_list *node; + struct remoteproc_mem *mem; + + /* + * Check name length to avoid overflow. This test has to be kept for + * MISRA compliance + */ + if (name && strlen(name) > RPROC_MAX_NAME_LEN) + return NULL; + + metal_list_for_each(&rproc->mems, node) { + mem = metal_container_of(node, struct remoteproc_mem, node); + if (name) { + if (!strncmp(name, mem->name, strlen(name))) + return mem; + } else if (pa != METAL_BAD_PHYS) { + metal_phys_addr_t pa_start, pa_end; + + pa_start = mem->pa; + pa_end = pa_start + mem->size; + if (pa >= pa_start && (pa + size) <= pa_end) + return mem; + } else if (da != METAL_BAD_PHYS) { + metal_phys_addr_t da_start, da_end; + + da_start = mem->da; + da_end = da_start + mem->size; + if (da >= da_start && (da + size) <= da_end) + return mem; + } else if (va) { + if (metal_io_virt_to_offset(mem->io, va) != + METAL_BAD_OFFSET) + return mem; + + } else { + return NULL; + } + } + return NULL; +} + +static metal_phys_addr_t +remoteproc_datopa(struct remoteproc_mem *mem, metal_phys_addr_t da) +{ + metal_phys_addr_t pa; + + pa = mem->pa + da - mem->da; + return pa; +} + +static metal_phys_addr_t +remoteproc_patoda(struct remoteproc_mem *mem, metal_phys_addr_t pa) +{ + metal_phys_addr_t da; + + da = mem->da + pa - mem->pa; + return da; +} + +static void *remoteproc_get_rsc_table(struct remoteproc *rproc, + void *store, + struct image_store_ops *store_ops, + size_t offset, + size_t len) +{ + int ret; + void *rsc_table = NULL; + const void *img_data; + + /* Copy the resource table to local memory, + * the caller should be responsible to release the memory + */ + rsc_table = metal_allocate_memory(len); + if (!rsc_table) { + return RPROC_ERR_PTR(-RPROC_ENOMEM); + } + ret = store_ops->load(store, offset, len, &img_data, RPROC_LOAD_ANYADDR, + NULL, 1); + if (ret < 0 || ret < (int)len || !img_data) { + metal_log(METAL_LOG_ERROR, + "get rsc failed: 0x%llx, 0x%llx\r\n", offset, len); + rsc_table = RPROC_ERR_PTR(-RPROC_EINVAL); + goto error; + } + memcpy(rsc_table, img_data, len); + + ret = handle_rsc_table(rproc, rsc_table, len, NULL); + if (ret < 0) { + rsc_table = RPROC_ERR_PTR(ret); + goto error; + } + return rsc_table; + +error: + metal_free_memory(rsc_table); + return rsc_table; +} + +static int remoteproc_parse_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, + size_t rsc_size) +{ + struct metal_io_region *io; + + io = remoteproc_get_io_with_va(rproc, rsc_table); + return handle_rsc_table(rproc, rsc_table, rsc_size, io); +} + +int remoteproc_set_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, + size_t rsc_size) +{ + int ret; + struct metal_io_region *io; + + io = remoteproc_get_io_with_va(rproc, rsc_table); + if (!io) + return -RPROC_EINVAL; + ret = remoteproc_parse_rsc_table(rproc, rsc_table, rsc_size); + if (!ret) { + rproc->rsc_table = rsc_table; + rproc->rsc_len = rsc_size; + rproc->rsc_io = io; + } + return ret; +} + +struct remoteproc *remoteproc_init(struct remoteproc *rproc, + struct remoteproc_ops *ops, void *priv) +{ + if (rproc) { + memset(rproc, 0, sizeof(*rproc)); + rproc->state = RPROC_OFFLINE; + metal_mutex_init(&rproc->lock); + metal_list_init(&rproc->mems); + metal_list_init(&rproc->vdevs); + } + rproc = ops->init(rproc, ops, priv); + return rproc; +} + +int remoteproc_remove(struct remoteproc *rproc) +{ + int ret = 0; + + if (rproc) { + metal_mutex_acquire(&rproc->lock); + if (rproc->state == RPROC_OFFLINE) + rproc->ops->remove(rproc); + else + ret = -RPROC_EAGAIN; + metal_mutex_release(&rproc->lock); + metal_mutex_deinit(&rproc->lock); + } else { + ret = -RPROC_EINVAL; + } + return ret; +} + +int remoteproc_config(struct remoteproc *rproc, void *data) +{ + int ret = -RPROC_ENODEV; + + if (rproc) { + metal_mutex_acquire(&rproc->lock); + if (rproc->state == RPROC_OFFLINE) { + /* configure operation is allowed if the state is + * offline or ready. This function can be called + * multiple times before start the remote. + */ + if (rproc->ops->config) + ret = rproc->ops->config(rproc, data); + else + ret = 0; + rproc->state = RPROC_READY; + } else { + ret = -RPROC_EINVAL; + } + metal_mutex_release(&rproc->lock); + } + return ret; +} + +int remoteproc_start(struct remoteproc *rproc) +{ + int ret = -RPROC_ENODEV; + + if (rproc) { + metal_mutex_acquire(&rproc->lock); + if (rproc->state == RPROC_READY) { + ret = rproc->ops->start(rproc); + rproc->state = RPROC_RUNNING; + } else { + ret = -RPROC_EINVAL; + } + metal_mutex_release(&rproc->lock); + } + return ret; +} + +int remoteproc_stop(struct remoteproc *rproc) +{ + int ret = -RPROC_ENODEV; + + if (rproc) { + metal_mutex_acquire(&rproc->lock); + if (rproc->state != RPROC_STOPPED && + rproc->state != RPROC_OFFLINE) { + if (rproc->ops->stop) + ret = rproc->ops->stop(rproc); + rproc->state = RPROC_STOPPED; + } else { + ret = 0; + } + metal_mutex_release(&rproc->lock); + } + return ret; +} + +int remoteproc_shutdown(struct remoteproc *rproc) +{ + int ret = -RPROC_ENODEV; + + if (rproc) { + ret = 0; + metal_mutex_acquire(&rproc->lock); + if (rproc->state != RPROC_OFFLINE) { + if (rproc->state != RPROC_STOPPED) { + if (rproc->ops->stop) + ret = rproc->ops->stop(rproc); + } + if (!ret) { + if (rproc->ops->shutdown) + ret = rproc->ops->shutdown(rproc); + if (!ret) { + rproc->state = RPROC_OFFLINE; + } + } + } + metal_mutex_release(&rproc->lock); + } + return ret; +} + +struct metal_io_region * +remoteproc_get_io_with_name(struct remoteproc *rproc, + const char *name) +{ + struct remoteproc_mem *mem; + + mem = remoteproc_get_mem(rproc, name, + METAL_BAD_PHYS, METAL_BAD_PHYS, NULL, 0); + if (mem) + return mem->io; + else + return NULL; +} + +struct metal_io_region * +remoteproc_get_io_with_pa(struct remoteproc *rproc, + metal_phys_addr_t pa) +{ + struct remoteproc_mem *mem; + + mem = remoteproc_get_mem(rproc, NULL, pa, METAL_BAD_PHYS, NULL, 0); + if (mem) + return mem->io; + else + return NULL; +} + +struct metal_io_region * +remoteproc_get_io_with_da(struct remoteproc *rproc, + metal_phys_addr_t da, + unsigned long *offset) +{ + struct remoteproc_mem *mem; + + mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, da, NULL, 0); + if (mem) { + struct metal_io_region *io; + metal_phys_addr_t pa; + + io = mem->io; + pa = remoteproc_datopa(mem, da); + *offset = metal_io_phys_to_offset(io, pa); + return io; + } else { + return NULL; + } +} + +struct metal_io_region * +remoteproc_get_io_with_va(struct remoteproc *rproc, void *va) +{ + struct remoteproc_mem *mem; + + mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, METAL_BAD_PHYS, + va, 0); + if (mem) + return mem->io; + else + return NULL; +} + +void *remoteproc_mmap(struct remoteproc *rproc, + metal_phys_addr_t *pa, metal_phys_addr_t *da, + size_t size, unsigned int attribute, + struct metal_io_region **io) +{ + void *va = NULL; + metal_phys_addr_t lpa, lda; + struct remoteproc_mem *mem; + + if (!rproc) + return NULL; + else if (!pa && !da) + return NULL; + if (pa) + lpa = *pa; + else + lpa = METAL_BAD_PHYS; + if (da) + lda = *da; + else + lda = METAL_BAD_PHYS; + mem = remoteproc_get_mem(rproc, NULL, lpa, lda, NULL, size); + if (mem) { + if (lpa != METAL_BAD_PHYS) + lda = remoteproc_patoda(mem, lpa); + else if (lda != METAL_BAD_PHYS) + lpa = remoteproc_datopa(mem, lda); + if (io) + *io = mem->io; + va = metal_io_phys_to_virt(mem->io, lpa); + } else if (rproc->ops->mmap) { + va = rproc->ops->mmap(rproc, &lpa, &lda, size, attribute, io); + } + + if (pa) + *pa = lpa; + if (da) + *da = lda; + return va; +} + +int remoteproc_load(struct remoteproc *rproc, const char *path, + void *store, struct image_store_ops *store_ops, + void **img_info) +{ + int ret; + struct loader_ops *loader; + const void *img_data; + void *limg_info = NULL; + size_t offset, noffset; + size_t len, nlen; + int last_load_state; + metal_phys_addr_t da, rsc_da; + size_t rsc_size = 0; + void *rsc_table = NULL; + struct metal_io_region *io = NULL; + + if (!rproc) + return -RPROC_ENODEV; + + metal_mutex_acquire(&rproc->lock); + metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status\r\n", __func__); + /* If remoteproc is not in ready state, cannot load executable */ + if (rproc->state != RPROC_READY && rproc->state != RPROC_CONFIGURED) { + metal_log(METAL_LOG_ERROR, + "load failure: invalid rproc state %d.\r\n", + rproc->state); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + + if (!store_ops) { + metal_log(METAL_LOG_ERROR, + "load failure: loader ops is not set.\r\n"); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + + /* Open executable to get ready to parse */ + metal_log(METAL_LOG_DEBUG, "%s: open executable image\r\n", __func__); + ret = store_ops->open(store, path, &img_data); + if (ret <= 0) { + metal_log(METAL_LOG_ERROR, + "load failure: failed to open firmware %d.\r\n", + ret); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + len = ret; + metal_assert(img_data); + + /* Check executable format to select a parser */ + loader = rproc->loader; + if (!loader) { + metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__); + loader = remoteproc_check_fw_format(img_data, len); + if (!loader) { + metal_log(METAL_LOG_ERROR, + "load failure: failed to get store ops.\r\n"); + ret = -RPROC_EINVAL; + goto error1; + } + rproc->loader = loader; + } + + /* Load executable headers */ + metal_log(METAL_LOG_DEBUG, "%s: loading headers\r\n", __func__); + offset = 0; + last_load_state = RPROC_LOADER_NOT_READY; + while (1) { + ret = loader->load_header(img_data, offset, len, + &limg_info, last_load_state, + &noffset, &nlen); + last_load_state = ret; + metal_log(METAL_LOG_DEBUG, + "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", + __func__, offset, len, noffset, nlen); + if (ret < 0) { + metal_log(METAL_LOG_ERROR, + "load header failed 0x%lx,%d.\r\n", + offset, len); + + goto error2; + } else if ((ret & RPROC_LOADER_READY_TO_LOAD) != 0) { + if (nlen == 0) + break; + else if ((noffset > (offset + len)) && + (store_ops->features & SUPPORT_SEEK) == 0) { + /* Required data is not continued, however + * seek is not supported, stop to load + * headers such as ELF section headers which + * is usually located to the end of image. + * Continue to load binary data to target + * memory. + */ + break; + } + } + /* Continue to load headers image data */ + img_data = NULL; + ret = store_ops->load(store, noffset, nlen, + &img_data, + RPROC_LOAD_ANYADDR, + NULL, 1); + if (ret < (int)nlen) { + metal_log(METAL_LOG_ERROR, + "load image data failed 0x%x,%d\r\n", + noffset, nlen); + goto error2; + } + offset = noffset; + len = nlen; + } + ret = loader->locate_rsc_table(limg_info, &rsc_da, &offset, &rsc_size); + if (ret == 0 && rsc_size > 0) { + /* parse resource table */ + rsc_table = remoteproc_get_rsc_table(rproc, store, store_ops, + offset, rsc_size); + } + + /* load executable data */ + metal_log(METAL_LOG_DEBUG, "%s: load executable data\r\n", __func__); + offset = 0; + len = 0; + while (1) { + unsigned char padding; + size_t nmemsize; + metal_phys_addr_t pa; + + da = RPROC_LOAD_ANYADDR; + nlen = 0; + nmemsize = 0; + noffset = 0; + ret = loader->load_data(rproc, img_data, offset, len, + &limg_info, last_load_state, &da, + &noffset, &nlen, &padding, &nmemsize); + if (ret < 0) { + metal_log(METAL_LOG_ERROR, + "load data failed,0x%lx,%d\r\n", + noffset, nlen); + goto error3; + } + metal_log(METAL_LOG_DEBUG, + "load data: da 0x%lx, offset 0x%lx, len = 0x%lx, memsize = 0x%lx, state 0x%x\r\n", + da, noffset, nlen, nmemsize, ret); + last_load_state = ret; + if (da != RPROC_LOAD_ANYADDR) { + /* Data is supposed to be loaded to target memory */ + img_data = NULL; + /* get the I/O region from remoteproc */ + pa = METAL_BAD_PHYS; + (void)remoteproc_mmap(rproc, &pa, &da, nmemsize, 0, + &io); + if (pa == METAL_BAD_PHYS || !io) { + metal_log(METAL_LOG_ERROR, + "load failed, no mapping for 0x%llx.\r\n", + da); + ret = -RPROC_EINVAL; + goto error3; + } + if (nlen > 0) { + ret = store_ops->load(store, noffset, nlen, + &img_data, pa, io, 1); + if (ret != (int)nlen) { + metal_log(METAL_LOG_ERROR, + "load data failed 0x%lx, 0x%lx, 0x%x\r\n", + pa, noffset, nlen); + ret = -RPROC_EINVAL; + goto error3; + } + } + if (nmemsize > nlen) { + size_t tmpoffset; + + tmpoffset = metal_io_phys_to_offset(io, + pa + nlen); + metal_io_block_set(io, tmpoffset, + padding, (nmemsize - nlen)); + } + } else if (nlen != 0) { + ret = store_ops->load(store, noffset, nlen, + &img_data, + RPROC_LOAD_ANYADDR, + NULL, 1); + if (ret < (int)nlen) { + if ((last_load_state & + RPROC_LOADER_POST_DATA_LOAD) != 0) { + metal_log(METAL_LOG_WARNING, + "not all the headers are loaded\r\n"); + break; + } + metal_log(METAL_LOG_ERROR, + "post-load image data failed 0x%x,%d\r\n", + noffset, nlen); + goto error3; + } + offset = noffset; + len = nlen; + } else { + /* (last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0 */ + break; + } + } + + if (rsc_size == 0) { + ret = loader->locate_rsc_table(limg_info, &rsc_da, + &offset, &rsc_size); + if (ret == 0 && rsc_size > 0) { + /* parse resource table */ + rsc_table = remoteproc_get_rsc_table(rproc, store, + store_ops, + offset, + rsc_size); + } + } + + /* Update resource table */ + if (rsc_table) { + void *rsc_table_cp = rsc_table; + + metal_log(METAL_LOG_DEBUG, + "%s, update resource table\r\n", __func__); + rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, + rsc_size, 0, &io); + if (rsc_table) { + size_t rsc_io_offset; + + /* Update resource table */ + rsc_io_offset = metal_io_virt_to_offset(io, rsc_table); + ret = metal_io_block_write(io, rsc_io_offset, + rsc_table_cp, rsc_size); + if (ret != (int)rsc_size) { + metal_log(METAL_LOG_WARNING, + "load: failed to update rsc\r\n"); + } + rproc->rsc_table = rsc_table; + rproc->rsc_len = rsc_size; + rproc->rsc_io = io; + } else { + metal_log(METAL_LOG_WARNING, + "load: not able to update rsc table.\r\n"); + } + metal_free_memory(rsc_table_cp); + /* So that the rsc_table will not get released */ + rsc_table = NULL; + } + + metal_log(METAL_LOG_DEBUG, "%s: successfully load firmware\r\n", + __func__); + /* get entry point from the firmware */ + rproc->bootaddr = loader->get_entry(limg_info); + rproc->state = RPROC_READY; + + metal_mutex_release(&rproc->lock); + if (img_info) + *img_info = limg_info; + else + loader->release(limg_info); + store_ops->close(store); + return 0; + +error3: + if (rsc_table) + metal_free_memory(rsc_table); +error2: + loader->release(limg_info); +error1: + store_ops->close(store); + metal_mutex_release(&rproc->lock); + return ret; +} + +int remoteproc_load_noblock(struct remoteproc *rproc, + const void *img_data, size_t offset, size_t len, + void **img_info, + metal_phys_addr_t *pa, struct metal_io_region **io, + size_t *noffset, size_t *nlen, + size_t *nmlen, unsigned char *padding) +{ + int ret; + struct loader_ops *loader; + void *limg_info = NULL; + int last_load_state; + metal_phys_addr_t da, rsc_da; + size_t rsc_size; + void *rsc_table = NULL, *lrsc_table = NULL; + + if (!rproc) + return -RPROC_ENODEV; + + metal_assert(pa); + metal_assert(io); + metal_assert(noffset); + metal_assert(nlen); + metal_assert(nmlen); + metal_assert(padding); + + metal_mutex_acquire(&rproc->lock); + metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status\r\n", __func__); + /* If remoteproc is not in ready state, cannot load executable */ + if (rproc->state != RPROC_READY) { + metal_log(METAL_LOG_ERROR, + "load failure: invalid rproc state %d.\r\n", + rproc->state); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + + /* Check executable format to select a parser */ + loader = rproc->loader; + if (!loader) { + metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__); + if (!img_data || offset != 0 || len == 0) { + metal_log(METAL_LOG_ERROR, + "load failure, invalid inputs, not able to identify image.\r\n"); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + loader = remoteproc_check_fw_format(img_data, len); + if (!loader) { + metal_log(METAL_LOG_ERROR, + "load failure: failed to identify image.\r\n"); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + rproc->loader = loader; + } + if (!img_info || !*img_info) { + last_load_state = 0; + } else { + limg_info = *img_info; + last_load_state = loader->get_load_state(limg_info); + if (last_load_state < 0) { + metal_log(METAL_LOG_ERROR, + "load failure, not able get load state.\r\n"); + metal_mutex_release(&rproc->lock); + return -RPROC_EINVAL; + } + } + da = RPROC_LOAD_ANYADDR; + *nlen = 0; + if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0 && + (last_load_state & RPROC_LOADER_LOAD_COMPLETE) == 0) { + /* Get the mandatory executable headers */ + ret = loader->load_header(img_data, offset, len, + &limg_info, last_load_state, + noffset, nlen); + metal_log(METAL_LOG_DEBUG, + "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", + __func__, offset, len, *noffset, *nlen); + if (ret < 0) { + metal_log(METAL_LOG_ERROR, + "load header failed 0x%lx,%d.\r\n", + offset, len); + goto error1; + } + last_load_state = ret; + if (*nlen != 0 && + (last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0) + goto out; + } + if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) != 0 || + (last_load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) { + /* Enough information to know which target memory for + * which data. + */ + ret = loader->load_data(rproc, img_data, offset, len, + &limg_info, last_load_state, &da, + noffset, nlen, padding, nmlen); + metal_log(METAL_LOG_DEBUG, + "%s, load data 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", + __func__, offset, len, *noffset, *nlen); + if (ret < 0) { + metal_log(METAL_LOG_ERROR, + "load data failed,0x%lx,%d\r\n", + offset, len); + goto error1; + } + last_load_state = ret; + if (da != RPROC_LOAD_ANYADDR) { + /* get the I/O region from remoteproc */ + *pa = METAL_BAD_PHYS; + (void)remoteproc_mmap(rproc, pa, &da, *nmlen, 0, io); + if (*pa == METAL_BAD_PHYS || !io) { + metal_log(METAL_LOG_ERROR, + "load failed, no mapping for 0x%llx.\r\n", + da); + ret = -RPROC_EINVAL; + goto error1; + } + } + if (*nlen != 0) + goto out; + } + if ((last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0) { + /* Get resource table */ + size_t rsc_offset; + size_t rsc_io_offset; + + ret = loader->locate_rsc_table(limg_info, &rsc_da, + &rsc_offset, &rsc_size); + if (ret == 0 && rsc_size > 0) { + lrsc_table = metal_allocate_memory(rsc_size); + if (!lrsc_table) { + ret = -RPROC_ENOMEM; + goto error1; + } + rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, + rsc_size, 0, io); + if (!*io) { + metal_log(METAL_LOG_ERROR, + "load failed: failed to mmap rsc\r\n"); + metal_free_memory(lrsc_table); + goto error1; + } + rsc_io_offset = metal_io_virt_to_offset(*io, rsc_table); + ret = metal_io_block_read(*io, rsc_io_offset, + lrsc_table, rsc_size); + if (ret != (int)rsc_size) { + metal_log(METAL_LOG_ERROR, + "load failed: failed to get rsc\r\n"); + metal_free_memory(lrsc_table); + goto error1; + } + /* parse resource table */ + ret = remoteproc_parse_rsc_table(rproc, lrsc_table, + rsc_size); + if (ret < 0) { + metal_log(METAL_LOG_ERROR, + "load failed: failed to parse rsc\r\n"); + metal_free_memory(lrsc_table); + goto error1; + } + /* Update resource table */ + ret = metal_io_block_write(*io, rsc_io_offset, + lrsc_table, rsc_size); + if (ret != (int)rsc_size) { + metal_log(METAL_LOG_WARNING, + "load executable, failed to update rsc\r\n"); + } + rproc->rsc_table = rsc_table; + rproc->rsc_len = rsc_size; + metal_free_memory(lrsc_table); + } + + /* get entry point from the firmware */ + rproc->bootaddr = loader->get_entry(limg_info); + } +out: + if (img_info) + *img_info = limg_info; + else + loader->release(limg_info); + metal_mutex_release(&rproc->lock); + return 0; + +error1: + loader->release(limg_info); + metal_mutex_release(&rproc->lock); + return ret; +} + +unsigned int remoteproc_allocate_id(struct remoteproc *rproc, + unsigned int start, + unsigned int end) +{ + unsigned int notifyid; + + if (start == RSC_NOTIFY_ID_ANY) + start = 0; + if (end == 0) + end = METAL_BITS_PER_ULONG; + + notifyid = metal_bitmap_next_clear_bit(&rproc->bitmap, + start, end); + if (notifyid != end) + metal_bitmap_set_bit(&rproc->bitmap, notifyid); + else + notifyid = RSC_NOTIFY_ID_ANY; + + return notifyid; +} + +static int remoteproc_virtio_notify(void *priv, uint32_t id) +{ + struct remoteproc *rproc = priv; + + return rproc->ops->notify(rproc, id); +} + +struct virtio_device * +remoteproc_create_virtio(struct remoteproc *rproc, + int vdev_id, unsigned int role, + void (*rst_cb)(struct virtio_device *vdev)) +{ + char *rsc_table; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *vdev_rsc_io; + struct virtio_device *vdev; + struct remoteproc_virtio *rpvdev; + size_t vdev_rsc_offset; + unsigned int notifyid; + unsigned int num_vrings, i; + struct metal_list *node; + + metal_assert(rproc); + metal_mutex_acquire(&rproc->lock); + rsc_table = rproc->rsc_table; + vdev_rsc_io = rproc->rsc_io; + vdev_rsc_offset = find_rsc(rsc_table, RSC_VDEV, vdev_id); + if (!vdev_rsc_offset) { + metal_mutex_release(&rproc->lock); + return NULL; + } + vdev_rsc = (struct fw_rsc_vdev *)(rsc_table + vdev_rsc_offset); + notifyid = vdev_rsc->notifyid; + /* Check if the virtio device is already created */ + metal_list_for_each(&rproc->vdevs, node) { + rpvdev = metal_container_of(node, struct remoteproc_virtio, + node); + if (rpvdev->vdev.notifyid == notifyid) { + metal_mutex_release(&rproc->lock); + return &rpvdev->vdev; + } + } + vdev = rproc_virtio_create_vdev(role, notifyid, + vdev_rsc, vdev_rsc_io, rproc, + remoteproc_virtio_notify, + rst_cb); + if (!vdev) { + metal_mutex_release(&rproc->lock); + return NULL; + } + + rproc_virtio_wait_remote_ready(vdev); + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + metal_list_add_tail(&rproc->vdevs, &rpvdev->node); + num_vrings = vdev_rsc->num_of_vrings; + + /* set the notification id for vrings */ + for (i = 0; i < num_vrings; i++) { + struct fw_rsc_vdev_vring *vring_rsc; + metal_phys_addr_t da; + unsigned int num_descs, align; + struct metal_io_region *io; + void *va; + size_t size; + int ret; + + vring_rsc = &vdev_rsc->vring[i]; + notifyid = vring_rsc->notifyid; + da = vring_rsc->da; + num_descs = vring_rsc->num; + align = vring_rsc->align; + size = vring_size(num_descs, align); + va = remoteproc_mmap(rproc, NULL, &da, size, 0, &io); + if (!va) + goto err1; + ret = rproc_virtio_init_vring(vdev, i, notifyid, + va, io, num_descs, align); + if (ret) + goto err1; + } + metal_mutex_release(&rproc->lock); + return vdev; + +err1: + remoteproc_remove_virtio(rproc, vdev); + metal_mutex_release(&rproc->lock); + return NULL; +} + +void remoteproc_remove_virtio(struct remoteproc *rproc, + struct virtio_device *vdev) +{ + struct remoteproc_virtio *rpvdev; + + (void)rproc; + metal_assert(vdev); + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + metal_list_del(&rpvdev->node); + rproc_virtio_remove_vdev(&rpvdev->vdev); +} + +int remoteproc_get_notification(struct remoteproc *rproc, uint32_t notifyid) +{ + struct remoteproc_virtio *rpvdev; + struct metal_list *node; + int ret; + + metal_list_for_each(&rproc->vdevs, node) { + rpvdev = metal_container_of(node, struct remoteproc_virtio, + node); + ret = rproc_virtio_notified(&rpvdev->vdev, notifyid); + if (ret) + return ret; + } + return 0; +} diff --git a/lib/remoteproc/remoteproc.o b/lib/remoteproc/remoteproc.o new file mode 100644 index 0000000..98522c1 Binary files /dev/null and b/lib/remoteproc/remoteproc.o differ diff --git a/lib/remoteproc/remoteproc_virtio.c b/lib/remoteproc/remoteproc_virtio.c new file mode 100644 index 0000000..ed9f33c --- /dev/null +++ b/lib/remoteproc/remoteproc_virtio.c @@ -0,0 +1,352 @@ +/* + * Remoteproc Virtio Framework Implementation + * + * Copyright(c) 2018 Xilinx Ltd. + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +static void rproc_virtio_virtqueue_notify(struct virtqueue *vq) +{ + struct remoteproc_virtio *rpvdev; + struct virtio_vring_info *vring_info; + struct virtio_device *vdev; + unsigned int vq_id = vq->vq_queue_index; + + vdev = vq->vq_dev; + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + metal_assert(vq_id < vdev->vrings_num); + vring_info = &vdev->vrings_info[vq_id]; + rpvdev->notify(rpvdev->priv, vring_info->notifyid); +} + +static unsigned char rproc_virtio_get_status(struct virtio_device *vdev) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + char status; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + io = rpvdev->vdev_rsc_io; + status = metal_io_read8(io, + metal_io_virt_to_offset(io, &vdev_rsc->status)); + return status; +} + +#ifndef VIRTIO_SLAVE_ONLY +static void rproc_virtio_set_status(struct virtio_device *vdev, + unsigned char status) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + io = rpvdev->vdev_rsc_io; + metal_io_write8(io, + metal_io_virt_to_offset(io, &vdev_rsc->status), + status); + rpvdev->notify(rpvdev->priv, vdev->notifyid); +} +#endif + +static uint32_t rproc_virtio_get_dfeatures(struct virtio_device *vdev) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + uint32_t features; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + io = rpvdev->vdev_rsc_io; + features = metal_io_read32(io, + metal_io_virt_to_offset(io, &vdev_rsc->dfeatures)); + + return features; +} + +static uint32_t rproc_virtio_get_features(struct virtio_device *vdev) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + uint32_t gfeatures; + uint32_t dfeatures; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + io = rpvdev->vdev_rsc_io; + gfeatures = metal_io_read32(io, + metal_io_virt_to_offset(io, &vdev_rsc->gfeatures)); + dfeatures = rproc_virtio_get_dfeatures(vdev); + + return dfeatures & gfeatures; +} + +#ifndef VIRTIO_SLAVE_ONLY +static void rproc_virtio_set_features(struct virtio_device *vdev, + uint32_t features) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + io = rpvdev->vdev_rsc_io; + metal_io_write32(io, + metal_io_virt_to_offset(io, &vdev_rsc->gfeatures), + features); + rpvdev->notify(rpvdev->priv, vdev->notifyid); +} + +static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev, + uint32_t features) +{ + uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev); + + rproc_virtio_set_features(vdev, dfeatures & features); + + return 0; +} +#endif + +static void rproc_virtio_read_config(struct virtio_device *vdev, + uint32_t offset, void *dst, int length) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + char *config; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + config = (char *)(&vdev_rsc->vring[vdev->vrings_num]); + io = rpvdev->vdev_rsc_io; + + if (offset + length <= vdev_rsc->config_len) + metal_io_block_read(io, + metal_io_virt_to_offset(io, config + offset), + dst, length); +} + +#ifndef VIRTIO_SLAVE_ONLY +static void rproc_virtio_write_config(struct virtio_device *vdev, + uint32_t offset, void *src, int length) +{ + struct remoteproc_virtio *rpvdev; + struct fw_rsc_vdev *vdev_rsc; + struct metal_io_region *io; + char *config; + + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + vdev_rsc = rpvdev->vdev_rsc; + config = (char *)(&vdev_rsc->vring[vdev->vrings_num]); + io = rpvdev->vdev_rsc_io; + + if (offset + length <= vdev_rsc->config_len) { + metal_io_block_write(io, + metal_io_virt_to_offset(io, config + offset), + src, length); + rpvdev->notify(rpvdev->priv, vdev->notifyid); + } +} + +static void rproc_virtio_reset_device(struct virtio_device *vdev) +{ + if (vdev->role == VIRTIO_DEV_MASTER) + rproc_virtio_set_status(vdev, + VIRTIO_CONFIG_STATUS_NEEDS_RESET); +} +#endif + +static const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = { + .get_status = rproc_virtio_get_status, + .get_features = rproc_virtio_get_features, + .read_config = rproc_virtio_read_config, + .notify = rproc_virtio_virtqueue_notify, +#ifndef VIRTIO_SLAVE_ONLY + /* + * We suppose here that the vdev is in a shared memory so that can + * be access only by one core: the master. In this case salve core has + * only read access right. + */ + .set_status = rproc_virtio_set_status, + .set_features = rproc_virtio_set_features, + .negotiate_features = rproc_virtio_negotiate_features, + .write_config = rproc_virtio_write_config, + .reset_device = rproc_virtio_reset_device, +#endif +}; + +struct virtio_device * +rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid, + void *rsc, struct metal_io_region *rsc_io, + void *priv, + rpvdev_notify_func notify, + virtio_dev_reset_cb rst_cb) +{ + struct remoteproc_virtio *rpvdev; + struct virtio_vring_info *vrings_info; + struct fw_rsc_vdev *vdev_rsc = rsc; + struct virtio_device *vdev; + unsigned int num_vrings = vdev_rsc->num_of_vrings; + unsigned int i; + + rpvdev = metal_allocate_memory(sizeof(*rpvdev)); + if (!rpvdev) + return NULL; + vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings); + if (!vrings_info) + goto err0; + memset(rpvdev, 0, sizeof(*rpvdev)); + memset(vrings_info, 0, sizeof(*vrings_info)); + vdev = &rpvdev->vdev; + + for (i = 0; i < num_vrings; i++) { + struct virtqueue *vq; + struct fw_rsc_vdev_vring *vring_rsc; + unsigned int num_extra_desc = 0; + + vring_rsc = &vdev_rsc->vring[i]; + if (role == VIRTIO_DEV_MASTER) { + num_extra_desc = vring_rsc->num; + } + vq = virtqueue_allocate(num_extra_desc); + if (!vq) + goto err1; + vrings_info[i].vq = vq; + } + + rpvdev->notify = notify; + rpvdev->priv = priv; + vdev->vrings_info = vrings_info; + /* Assuming the shared memory has been mapped and registered if + * necessary + */ + rpvdev->vdev_rsc = vdev_rsc; + rpvdev->vdev_rsc_io = rsc_io; + + vdev->notifyid = notifyid; + vdev->role = role; + vdev->reset_cb = rst_cb; + vdev->vrings_num = num_vrings; + vdev->func = &remoteproc_virtio_dispatch_funcs; + +#ifndef VIRTIO_SLAVE_ONLY + if (role == VIRTIO_DEV_MASTER) { + uint32_t dfeatures = rproc_virtio_get_dfeatures(vdev); + /* Assume the master support all slave features */ + rproc_virtio_negotiate_features(vdev, dfeatures); + } +#endif + + return &rpvdev->vdev; + +err1: + for (i = 0; i < num_vrings; i++) { + if (vrings_info[i].vq) + metal_free_memory(vrings_info[i].vq); + } + metal_free_memory(vrings_info); +err0: + metal_free_memory(rpvdev); + return NULL; +} + +void rproc_virtio_remove_vdev(struct virtio_device *vdev) +{ + struct remoteproc_virtio *rpvdev; + unsigned int i; + + if (!vdev) + return; + rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); + for (i = 0; i < vdev->vrings_num; i++) { + struct virtqueue *vq; + + vq = vdev->vrings_info[i].vq; + if (vq) + metal_free_memory(vq); + } + metal_free_memory(vdev->vrings_info); + metal_free_memory(rpvdev); +} + +int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index, + unsigned int notifyid, void *va, + struct metal_io_region *io, + unsigned int num_descs, unsigned int align) +{ + struct virtio_vring_info *vring_info; + unsigned int num_vrings; + + num_vrings = vdev->vrings_num; + if (index >= num_vrings) + return -RPROC_EINVAL; + vring_info = &vdev->vrings_info[index]; + vring_info->io = io; + vring_info->notifyid = notifyid; + vring_info->info.vaddr = va; + vring_info->info.num_descs = num_descs; + vring_info->info.align = align; + + return 0; +} + +int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid) +{ + unsigned int num_vrings, i; + struct virtio_vring_info *vring_info; + struct virtqueue *vq; + + if (!vdev) + return -RPROC_EINVAL; + /* We do nothing for vdev notification in this implementation */ + if (vdev->notifyid == notifyid) + return 0; + num_vrings = vdev->vrings_num; + for (i = 0; i < num_vrings; i++) { + vring_info = &vdev->vrings_info[i]; + if (vring_info->notifyid == notifyid || + notifyid == RSC_NOTIFY_ID_ANY) { + vq = vring_info->vq; + virtqueue_notification(vq); + } + } + return 0; +} + +void rproc_virtio_wait_remote_ready(struct virtio_device *vdev) +{ + uint8_t status; + + /* + * No status available for slave. As Master has not to wait + * slave action, we can return. Behavior should be updated + * in future if a slave status is added. + */ + if (vdev->role == VIRTIO_DEV_MASTER) + return; + + while (1) { + status = rproc_virtio_get_status(vdev); + if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) + return; + metal_cpu_yield(); + } +} diff --git a/lib/remoteproc/remoteproc_virtio.o b/lib/remoteproc/remoteproc_virtio.o new file mode 100644 index 0000000..cd539b6 Binary files /dev/null and b/lib/remoteproc/remoteproc_virtio.o differ diff --git a/lib/remoteproc/rsc_table_parser.c b/lib/remoteproc/rsc_table_parser.c new file mode 100644 index 0000000..e43fa72 --- /dev/null +++ b/lib/remoteproc/rsc_table_parser.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * Copyright (c) 2018, Xilinx Inc. + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc); + +/* Resources handler */ +static const rsc_handler rsc_handler_table[] = { + handle_carve_out_rsc, /**< carved out resource */ + handle_dummy_rsc, /**< IOMMU dev mem resource */ + handle_trace_rsc, /**< trace buffer resource */ + handle_vdev_rsc, /**< virtio resource */ +}; + +int handle_rsc_table(struct remoteproc *rproc, + struct resource_table *rsc_table, size_t size, + struct metal_io_region *io) +{ + char *rsc_start; + unsigned int rsc_type; + unsigned int idx, offset; + int 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 */ + offset = sizeof(struct resource_table) + + rsc_table->num * sizeof(rsc_table->offset[0]); + + if (offset > 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; + } + + /* Loop through the offset array and parse each resource entry */ + for (idx = 0; idx < rsc_table->num; idx++) { + rsc_start = (char *)rsc_table; + rsc_start += rsc_table->offset[idx]; + if (io && + metal_io_virt_to_offset(io, rsc_start) == METAL_BAD_OFFSET) + return -RPROC_ERR_RSC_TAB_TRUNC; + rsc_type = *((uint32_t *)rsc_start); + if (rsc_type < RSC_LAST) + status = rsc_handler_table[rsc_type](rproc, + rsc_start); + else if (rsc_type >= RSC_VENDOR_START && + rsc_type <= RSC_VENDOR_END) + status = handle_vendor_rsc(rproc, rsc_start); + if (status == -RPROC_ERR_RSC_TAB_NS) { + status = 0; + continue; + } else if (status) { + break; + } + } + + return status; +} + +/** + * handle_carve_out_rsc + * + * Carveout resource handler. + * + * @param rproc - pointer to remote remoteproc + * @param rsc - pointer to carveout resource + * + * @returns - 0 for success, or negative value for failure + * + */ +int handle_carve_out_rsc(struct remoteproc *rproc, void *rsc) +{ + struct fw_rsc_carveout *carve_rsc = rsc; + metal_phys_addr_t da; + metal_phys_addr_t pa; + size_t size; + unsigned int attribute; + + /* Validate resource fields */ + if (!carve_rsc) { + return -RPROC_ERR_RSC_TAB_NP; + } + + if (carve_rsc->reserved) { + return -RPROC_ERR_RSC_TAB_RSVD; + } + pa = carve_rsc->pa; + da = carve_rsc->da; + size = carve_rsc->len; + attribute = carve_rsc->flags; + if (remoteproc_mmap(rproc, &pa, &da, size, attribute, NULL)) + return 0; + else + return -RPROC_EINVAL; +} + +int handle_vendor_rsc(struct remoteproc *rproc, void *rsc) +{ + if (rproc && rproc->ops->handle_rsc) { + struct fw_rsc_vendor *vend_rsc = rsc; + size_t len = vend_rsc->len; + + return rproc->ops->handle_rsc(rproc, rsc, len); + } + return -RPROC_ERR_RSC_TAB_NS; +} + +int handle_vdev_rsc(struct remoteproc *rproc, void *rsc) +{ + struct fw_rsc_vdev *vdev_rsc = rsc; + unsigned int notifyid, i, num_vrings; + + /* only assign notification IDs but do not initialize vdev */ + notifyid = vdev_rsc->notifyid; + notifyid = remoteproc_allocate_id(rproc, + notifyid, notifyid + 1); + if (notifyid != RSC_NOTIFY_ID_ANY) + vdev_rsc->notifyid = notifyid; + + num_vrings = vdev_rsc->num_of_vrings; + for (i = 0; i < num_vrings; i++) { + struct fw_rsc_vdev_vring *vring_rsc; + + vring_rsc = &vdev_rsc->vring[i]; + notifyid = vring_rsc->notifyid; + notifyid = remoteproc_allocate_id(rproc, + notifyid, + notifyid + 1); + if (notifyid != RSC_NOTIFY_ID_ANY) + vdev_rsc->notifyid = notifyid; + } + + return 0; +} + +/** + * handle_trace_rsc + * + * trace resource handler. + * + * @param rproc - pointer to remote remoteproc + * @param rsc - pointer to trace resource + * + * @returns - no service error + * + */ +int handle_trace_rsc(struct remoteproc *rproc, void *rsc) +{ + struct fw_rsc_trace *vdev_rsc = rsc; + (void)rproc; + + if (vdev_rsc->da != FW_RSC_U32_ADDR_ANY && vdev_rsc->len != 0) + return 0; + /* FIXME: master should allocated a memory used by slave */ + + return -RPROC_ERR_RSC_TAB_NS; +} + +/** + * handle_dummy_rsc + * + * dummy resource handler. + * + * @param rproc - pointer to remote remoteproc + * @param rsc - pointer to trace resource + * + * @returns - no service error + * + */ +static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc) +{ + (void)rproc; + (void)rsc; + + return -RPROC_ERR_RSC_TAB_NS; +} + +size_t find_rsc(void *rsc_table, unsigned int rsc_type, unsigned int index) +{ + struct resource_table *r_table = rsc_table; + unsigned int i, rsc_index; + unsigned int lrsc_type; + char *rsc_start; + + metal_assert(r_table); + /* Loop through the offset array and parse each resource entry */ + rsc_index = 0; + for (i = 0; i < r_table->num; i++) { + rsc_start = (char *)r_table; + rsc_start += r_table->offset[i]; + lrsc_type = *((uint32_t *)rsc_start); + if (lrsc_type == rsc_type) { + if (rsc_index++ == index) + return r_table->offset[i]; + } + } + return 0; +} diff --git a/lib/remoteproc/rsc_table_parser.o b/lib/remoteproc/rsc_table_parser.o new file mode 100644 index 0000000..336fce7 Binary files /dev/null and b/lib/remoteproc/rsc_table_parser.o differ diff --git a/lib/rpmsg/CMakeLists.txt b/lib/rpmsg/CMakeLists.txt new file mode 100644 index 0000000..e2bd8d8 --- /dev/null +++ b/lib/rpmsg/CMakeLists.txt @@ -0,0 +1,2 @@ +collect (PROJECT_LIB_SOURCES rpmsg.c) +collect (PROJECT_LIB_SOURCES rpmsg_virtio.c) diff --git a/lib/rpmsg/rpmsg.c b/lib/rpmsg/rpmsg.c new file mode 100644 index 0000000..94ac52e --- /dev/null +++ b/lib/rpmsg/rpmsg.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved. + * Copyright (c) 2018 Linaro, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "rpmsg_internal.h" + +/** + * 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 + */ +static uint32_t rpmsg_get_address(unsigned long *bitmap, int size) +{ + unsigned int addr = RPMSG_ADDR_ANY; + unsigned int nextbit; + + nextbit = metal_bitmap_next_clear_bit(bitmap, 0, size); + if (nextbit < (uint32_t)size) { + addr = RPMSG_RESERVED_ADDRESSES + nextbit; + metal_bitmap_set_bit(bitmap, nextbit); + } + + 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 + */ +static void rpmsg_release_address(unsigned long *bitmap, int size, + int addr) +{ + addr -= RPMSG_RESERVED_ADDRESSES; + if (addr >= 0 && addr < size) + metal_bitmap_clear_bit(bitmap, addr); +} + +/** + * 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 + */ +static int rpmsg_is_address_set(unsigned long *bitmap, int size, int addr) +{ + addr -= RPMSG_RESERVED_ADDRESSES; + if (addr >= 0 && addr < size) + return metal_bitmap_is_bit_set(bitmap, addr); + else + return RPMSG_ERR_PARAM; +} + +/** + * 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 + */ +static int rpmsg_set_address(unsigned long *bitmap, int size, int addr) +{ + addr -= RPMSG_RESERVED_ADDRESSES; + if (addr >= 0 && addr < size) { + metal_bitmap_set_bit(bitmap, addr); + return RPMSG_SUCCESS; + } else { + return RPMSG_ERR_PARAM; + } +} + +/** + * This function sends rpmsg "message" to remote device. + * + * @param ept - pointer to end point + * @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 - size of data sent or negative value for failure. + * + */ +int rpmsg_send_offchannel_raw(struct rpmsg_endpoint *ept, uint32_t src, + uint32_t dst, const void *data, int size, + int wait) +{ + struct rpmsg_device *rdev; + + if (!ept || !ept->rdev || !data || dst == RPMSG_ADDR_ANY) + return RPMSG_ERR_PARAM; + + rdev = ept->rdev; + + if (rdev->ops.send_offchannel_raw) + return rdev->ops.send_offchannel_raw(rdev, src, dst, data, + size, wait); + + return RPMSG_ERR_PARAM; +} + +int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags) +{ + struct rpmsg_ns_msg ns_msg; + int ret; + + ns_msg.flags = flags; + ns_msg.addr = ept->addr; + strncpy(ns_msg.name, ept->name, sizeof(ns_msg.name)); + ret = rpmsg_send_offchannel_raw(ept, ept->addr, + RPMSG_NS_EPT_ADDR, + &ns_msg, sizeof(ns_msg), true); + if (ret < 0) + return ret; + else + return RPMSG_SUCCESS; +} + +struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rdev, + const char *name, uint32_t addr, + uint32_t dest_addr) +{ + struct metal_list *node; + struct rpmsg_endpoint *ept; + + metal_list_for_each(&rdev->endpoints, node) { + int name_match = 0; + + ept = metal_container_of(node, struct rpmsg_endpoint, node); + /* try to get by local address only */ + if (addr != RPMSG_ADDR_ANY && ept->addr == addr) + return ept; + /* try to find match on local end remote address */ + if (addr == ept->addr && dest_addr == ept->dest_addr) + return ept; + /* else use name service and destination address */ + if (name) + name_match = !strncmp(ept->name, name, + sizeof(ept->name)); + if (!name || !name_match) + continue; + /* destination address is known, equal to ept remote address */ + if (dest_addr != RPMSG_ADDR_ANY && ept->dest_addr == dest_addr) + return ept; + /* ept is registered but not associated to remote ept */ + if (addr == RPMSG_ADDR_ANY && ept->dest_addr == RPMSG_ADDR_ANY) + return ept; + } + return NULL; +} + +static void rpmsg_unregister_endpoint(struct rpmsg_endpoint *ept) +{ + struct rpmsg_device *rdev = ept->rdev; + + metal_mutex_acquire(&rdev->lock); + if (ept->addr != RPMSG_ADDR_ANY) + rpmsg_release_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, + ept->addr); + metal_list_del(&ept->node); + ept->rdev = NULL; + metal_mutex_release(&rdev->lock); +} + +void rpmsg_register_endpoint(struct rpmsg_device *rdev, + struct rpmsg_endpoint *ept) +{ + ept->rdev = rdev; + metal_list_add_tail(&rdev->endpoints, &ept->node); +} + +int rpmsg_create_ept(struct rpmsg_endpoint *ept, struct rpmsg_device *rdev, + const char *name, uint32_t src, uint32_t dest, + rpmsg_ept_cb cb, rpmsg_ns_unbind_cb unbind_cb) +{ + int status = RPMSG_SUCCESS; + uint32_t addr = src; + + if (!ept) + return RPMSG_ERR_PARAM; + + metal_mutex_acquire(&rdev->lock); + if (src == RPMSG_ADDR_ANY) { + addr = rpmsg_get_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE); + if (addr == RPMSG_ADDR_ANY) { + status = RPMSG_ERR_ADDR; + goto ret_status; + } + } else if (src >= RPMSG_RESERVED_ADDRESSES) { + status = rpmsg_is_address_set(rdev->bitmap, + RPMSG_ADDR_BMP_SIZE, src); + if (!status) { + /* Mark the address as used in the address bitmap. */ + rpmsg_set_address(rdev->bitmap, RPMSG_ADDR_BMP_SIZE, + src); + } else if (status > 0) { + status = RPMSG_ERR_ADDR; + goto ret_status; + } else { + goto ret_status; + } + } else { + /* Skip check the address duplication in 0-1023: + * 1.Trust the author of predefined service + * 2.Simplify the tracking implementation + */ + } + + rpmsg_initialize_ept(ept, name, addr, dest, cb, unbind_cb); + rpmsg_register_endpoint(rdev, ept); + metal_mutex_release(&rdev->lock); + + /* Send NS announcement to remote processor */ + if (ept->name[0] && rdev->support_ns && + ept->dest_addr == RPMSG_ADDR_ANY) + status = rpmsg_send_ns_message(ept, RPMSG_NS_CREATE); + + if (status) + rpmsg_unregister_endpoint(ept); + return status; + +ret_status: + metal_mutex_release(&rdev->lock); + return status; +} + +/** + * rpmsg_destroy_ept + * + * This function deletes rpmsg endpoint and performs cleanup. + * + * @param ept - pointer to endpoint to destroy + * + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) +{ + struct rpmsg_device *rdev; + + if (!ept) + return; + + rdev = ept->rdev; + if (!rdev) + return; + + if (ept->name[0] && rdev->support_ns && + ept->addr >= RPMSG_RESERVED_ADDRESSES) + (void)rpmsg_send_ns_message(ept, RPMSG_NS_DESTROY); + rpmsg_unregister_endpoint(ept); +} diff --git a/lib/rpmsg/rpmsg.o b/lib/rpmsg/rpmsg.o new file mode 100644 index 0000000..66cb302 Binary files /dev/null and b/lib/rpmsg/rpmsg.o differ diff --git a/lib/rpmsg/rpmsg_internal.h b/lib/rpmsg/rpmsg_internal.h new file mode 100644 index 0000000..b381cb7 --- /dev/null +++ b/lib/rpmsg/rpmsg_internal.h @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * $FreeBSD$ + */ + +#ifndef _RPMSG_INTERNAL_H_ +#define _RPMSG_INTERNAL_H_ + +#include +#include + +#if defined __cplusplus +extern "C" { +#endif + +#ifdef RPMSG_DEBUG +#include + +#define RPMSG_ASSERT(_exp, _msg) do { \ + if (!(_exp)) { \ + metal_log(METAL_LOG_EMERGENCY, \ + "FATAL: %s - "_msg, __func__); \ + while (1) { \ + ; \ + } \ + } \ + } while (0) +#else +#define RPMSG_ASSERT(_exp, _msg) do { \ + if (!(_exp)) \ + while (1) { \ + ; \ + } \ + } while (0) +#endif + +#define RPMSG_LOCATE_DATA(p) ((unsigned char *)(p) + sizeof(struct rpmsg_hdr)) +/** + * 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 + * @RPMSG_NS_CREATE_WITH_ACK: a new remote service was just created waiting + * acknowledgment. + */ +enum rpmsg_ns_flags { + RPMSG_NS_CREATE = 0, + RPMSG_NS_DESTROY = 1, +}; + +/** + * 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 + * + * Every message sent(/received) on the rpmsg bus begins with this header. + */ +METAL_PACKED_BEGIN +struct rpmsg_hdr { + uint32_t src; + uint32_t dst; + uint32_t reserved; + uint16_t len; + uint16_t flags; +} METAL_PACKED_END; + +/** + * 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). + */ +METAL_PACKED_BEGIN +struct rpmsg_ns_msg { + char name[RPMSG_NAME_SIZE]; + uint32_t addr; + uint32_t flags; +} METAL_PACKED_END; + +/** + * rpmsg_initialize_ept - initialize rpmsg endpoint + * + * Initialize an RPMsg endpoint with a name, source address, + * remoteproc address, endpoint callback, and destroy endpoint callback. + * + * @ept: pointer to rpmsg endpoint + * @name: service name associated to the endpoint + * @src: local address of the endpoint + * @dest: target address of the endpoint + * @cb: endpoint callback + * @ns_unbind_cb: end point service unbind callback, called when remote ept is + * destroyed. + */ +static inline void rpmsg_initialize_ept(struct rpmsg_endpoint *ept, + const char *name, + uint32_t src, uint32_t dest, + rpmsg_ept_cb cb, + rpmsg_ns_unbind_cb ns_unbind_cb) +{ + strncpy(ept->name, name ? name : "", sizeof(ept->name)); + ept->addr = src; + ept->dest_addr = dest; + ept->cb = cb; + ept->ns_unbind_cb = ns_unbind_cb; +} + +int rpmsg_send_ns_message(struct rpmsg_endpoint *ept, unsigned long flags); + +struct rpmsg_endpoint *rpmsg_get_endpoint(struct rpmsg_device *rvdev, + const char *name, uint32_t addr, + uint32_t dest_addr); +void rpmsg_register_endpoint(struct rpmsg_device *rdev, + struct rpmsg_endpoint *ept); + +static inline struct rpmsg_endpoint * +rpmsg_get_ept_from_addr(struct rpmsg_device *rdev, uint32_t addr) +{ + return rpmsg_get_endpoint(rdev, NULL, addr, RPMSG_ADDR_ANY); +} + +#if defined __cplusplus +} +#endif + +#endif /* _RPMSG_INTERNAL_H_ */ diff --git a/lib/rpmsg/rpmsg_virtio.c b/lib/rpmsg/rpmsg_virtio.c new file mode 100644 index 0000000..f09eae2 --- /dev/null +++ b/lib/rpmsg/rpmsg_virtio.c @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2014, Mentor Graphics Corporation + * All rights reserved. + * Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved. + * Copyright (c) 2018 Linaro, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include "rpmsg_internal.h" + +#define RPMSG_NUM_VRINGS 2 + +/* Total tick count for 15secs - 1usec tick. */ +#define RPMSG_TICK_COUNT 15000000 + +/* Time to wait - In multiple of 1 msecs. */ +#define RPMSG_TICKS_PER_INTERVAL 1000 + +#ifndef VIRTIO_SLAVE_ONLY +metal_weak void * +rpmsg_virtio_shm_pool_get_buffer(struct rpmsg_virtio_shm_pool *shpool, + size_t size) +{ + void *buffer; + + if (shpool->avail < size) + return NULL; + buffer = (char *)shpool->base + shpool->size - shpool->avail; + shpool->avail -= size; + + return buffer; +} +#endif /*!VIRTIO_SLAVE_ONLY*/ + +void rpmsg_virtio_init_shm_pool(struct rpmsg_virtio_shm_pool *shpool, + void *shb, size_t size) +{ + if (!shpool) + return; + shpool->base = shb; + shpool->size = size; + shpool->avail = size; +} + +/** + * rpmsg_virtio_return_buffer + * + * Places the used buffer back on the virtqueue. + * + * @param rvdev - pointer to remote core + * @param buffer - buffer pointer + * @param len - buffer length + * @param idx - buffer index + * + */ +static void rpmsg_virtio_return_buffer(struct rpmsg_virtio_device *rvdev, + void *buffer, uint32_t len, + uint16_t idx) +{ + unsigned int role = rpmsg_virtio_get_role(rvdev); +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + struct virtqueue_buf vqbuf; + + (void)idx; + /* Initialize buffer node */ + vqbuf.buf = buffer; + vqbuf.len = len; + virtqueue_add_buffer(rvdev->rvq, &vqbuf, 0, 1, buffer); + } +#endif /*VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + (void)buffer; + virtqueue_add_consumed_buffer(rvdev->rvq, idx, len); + } +#endif /*VIRTIO_MASTER_ONLY*/ +} + +/** + * rpmsg_virtio_enqueue_buffer + * + * Places buffer on the virtqueue for consumption by the other side. + * + * @param rvdev - pointer to rpmsg virtio + * @param buffer - buffer pointer + * @param len - buffer length + * @param idx - buffer index + * + * @return - status of function execution + */ +static int rpmsg_virtio_enqueue_buffer(struct rpmsg_virtio_device *rvdev, + void *buffer, uint32_t len, + uint16_t idx) +{ + unsigned int role = rpmsg_virtio_get_role(rvdev); +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + struct virtqueue_buf vqbuf; + (void)idx; + + /* Initialize buffer node */ + vqbuf.buf = buffer; + vqbuf.len = len; + return virtqueue_add_buffer(rvdev->svq, &vqbuf, 1, 0, buffer); + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + (void)buffer; + return virtqueue_add_consumed_buffer(rvdev->svq, idx, len); + } +#endif /*!VIRTIO_MASTER_ONLY*/ + return 0; +} + +/** + * rpmsg_virtio_get_tx_buffer + * + * Provides buffer to transmit messages. + * + * @param rvdev - pointer to rpmsg device + * @param len - length of returned buffer + * @param idx - buffer index + * + * return - pointer to buffer. + */ +static void *rpmsg_virtio_get_tx_buffer(struct rpmsg_virtio_device *rvdev, + uint32_t *len, uint16_t *idx) +{ + unsigned int role = rpmsg_virtio_get_role(rvdev); + void *data = NULL; + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + data = virtqueue_get_buffer(rvdev->svq, len, idx); + if (!data) { + data = rpmsg_virtio_shm_pool_get_buffer(rvdev->shpool, + RPMSG_BUFFER_SIZE); + *len = RPMSG_BUFFER_SIZE; + } + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + data = virtqueue_get_available_buffer(rvdev->svq, idx, len); + } +#endif /*!VIRTIO_MASTER_ONLY*/ + + return data; +} + +/** + * rpmsg_virtio_get_rx_buffer + * + * Retrieves the received buffer from the virtqueue. + * + * @param rvdev - pointer to rpmsg device + * @param len - size of received buffer + * @param idx - index of buffer + * + * @return - pointer to received buffer + * + */ +static void *rpmsg_virtio_get_rx_buffer(struct rpmsg_virtio_device *rvdev, + uint32_t *len, uint16_t *idx) +{ + unsigned int role = rpmsg_virtio_get_role(rvdev); + void *data = NULL; + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + data = virtqueue_get_buffer(rvdev->rvq, len, idx); + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + data = + virtqueue_get_available_buffer(rvdev->rvq, idx, len); + } +#endif /*!VIRTIO_MASTER_ONLY*/ + + return data; +} + +#ifndef VIRTIO_MASTER_ONLY +/** + * check if the remote is ready to start RPMsg communication + */ +static int rpmsg_virtio_wait_remote_ready(struct rpmsg_virtio_device *rvdev) +{ + uint8_t status; + + while (1) { + status = rpmsg_virtio_get_status(rvdev); + /* Busy wait until the remote is ready */ + if (status & VIRTIO_CONFIG_STATUS_NEEDS_RESET) { + rpmsg_virtio_set_status(rvdev, 0); + /* TODO notify remote processor */ + } else if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) { + return true; + } + /* TODO: clarify metal_cpu_yield usage*/ + metal_cpu_yield(); + } +} +#endif /*!VIRTIO_MASTER_ONLY*/ + +/** + * _rpmsg_virtio_get_buffer_size + * + * Returns buffer size available for sending messages. + * + * @param rvdev - pointer to rpmsg device + * + * @return - buffer size + * + */ +static int _rpmsg_virtio_get_buffer_size(struct rpmsg_virtio_device *rvdev) +{ + unsigned int role = rpmsg_virtio_get_role(rvdev); + int length = 0; + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + /* + * If device role is Master then buffers are provided by us, + * so just provide the macro. + */ + length = RPMSG_BUFFER_SIZE - sizeof(struct rpmsg_hdr); + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + /* + * 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(rvdev->svq) - + sizeof(struct rpmsg_hdr); + if (length < 0) { + length = 0; + } + } +#endif /*!VIRTIO_MASTER_ONLY*/ + + return length; +} + +/** + * This function sends rpmsg "message" to remote device. + * + * @param rdev - pointer to rpmsg device + * @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 - size of data sent or negative value for failure. + * + */ +static int rpmsg_virtio_send_offchannel_raw(struct rpmsg_device *rdev, + uint32_t src, uint32_t dst, + const void *data, + int size, int wait) +{ + struct rpmsg_virtio_device *rvdev; + struct rpmsg_hdr rp_hdr; + void *buffer = NULL; + uint16_t idx; + int tick_count; + uint32_t buff_len; + int status; + struct metal_io_region *io; + + /* Get the associated remote device for channel. */ + rvdev = metal_container_of(rdev, struct rpmsg_virtio_device, rdev); + + status = rpmsg_virtio_get_status(rvdev); + /* Validate device state */ + if (!(status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) { + return RPMSG_ERR_DEV_STATE; + } + + if (wait) + tick_count = RPMSG_TICK_COUNT / RPMSG_TICKS_PER_INTERVAL; + else + tick_count = 0; + + while (1) { + int avail_size; + + /* Lock the device to enable exclusive access to virtqueues */ + metal_mutex_acquire(&rdev->lock); + avail_size = _rpmsg_virtio_get_buffer_size(rvdev); + if (avail_size && size > avail_size) { + metal_mutex_release(&rdev->lock); + return RPMSG_ERR_BUFF_SIZE; + } + + buffer = rpmsg_virtio_get_tx_buffer(rvdev, &buff_len, &idx); + metal_mutex_release(&rdev->lock); + if (buffer || !tick_count) + break; + if (avail_size != 0) + return RPMSG_ERR_BUFF_SIZE; + metal_sleep_usec(RPMSG_TICKS_PER_INTERVAL); + tick_count--; + } + if (!buffer) + return RPMSG_ERR_NO_BUFF; + + /* Initialize RPMSG header. */ + rp_hdr.dst = dst; + rp_hdr.src = src; + rp_hdr.len = size; + rp_hdr.reserved = 0; + + /* Copy data to rpmsg buffer. */ + io = rvdev->shbuf_io; + status = metal_io_block_write(io, metal_io_virt_to_offset(io, buffer), + &rp_hdr, sizeof(rp_hdr)); + RPMSG_ASSERT(status == sizeof(rp_hdr), "failed to write header\r\n"); + + status = metal_io_block_write(io, + metal_io_virt_to_offset(io, + RPMSG_LOCATE_DATA(buffer)), + data, size); + RPMSG_ASSERT(status == size, "failed to write buffer\r\n"); + metal_mutex_acquire(&rdev->lock); + + /* Enqueue buffer on virtqueue. */ + status = rpmsg_virtio_enqueue_buffer(rvdev, buffer, buff_len, idx); + RPMSG_ASSERT(status == VQUEUE_SUCCESS, "failed to enqueue buffer\r\n"); + /* Let the other side know that there is a job to process. */ + virtqueue_kick(rvdev->svq); + + metal_mutex_release(&rdev->lock); + + return size; +} + +/** + * rpmsg_virtio_tx_callback + * + * Tx callback function. + * + * @param vq - pointer to virtqueue on which Tx is has been + * completed. + * + */ +static void rpmsg_virtio_tx_callback(struct virtqueue *vq) +{ + (void)vq; +} + +/** + * rpmsg_virtio_rx_callback + * + * Rx callback function. + * + * @param vq - pointer to virtqueue on which messages is received + * + */ +static void rpmsg_virtio_rx_callback(struct virtqueue *vq) +{ + struct virtio_device *vdev = vq->vq_dev; + struct rpmsg_virtio_device *rvdev = vdev->priv; + struct rpmsg_device *rdev = &rvdev->rdev; + struct rpmsg_endpoint *ept; + struct rpmsg_hdr *rp_hdr; + uint32_t len; + uint16_t idx; + int status; + + metal_mutex_acquire(&rdev->lock); + + /* Process the received data from remote node */ + rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx); + + metal_mutex_release(&rdev->lock); + + while (rp_hdr) { + /* Get the channel node from the remote device channels list. */ + metal_mutex_acquire(&rdev->lock); + ept = rpmsg_get_ept_from_addr(rdev, rp_hdr->dst); + metal_mutex_release(&rdev->lock); + + if (ept) { + if (ept->dest_addr == RPMSG_ADDR_ANY) { + /* + * First message received from the remote side, + * update channel destination address + */ + ept->dest_addr = rp_hdr->src; + } + status = ept->cb(ept, RPMSG_LOCATE_DATA(rp_hdr), + rp_hdr->len, rp_hdr->src, ept->priv); + + RPMSG_ASSERT(status >= 0, + "unexpected callback status\r\n"); + } + + metal_mutex_acquire(&rdev->lock); + + /* Return used buffers. */ + rpmsg_virtio_return_buffer(rvdev, rp_hdr, len, idx); + + rp_hdr = rpmsg_virtio_get_rx_buffer(rvdev, &len, &idx); + if (!rp_hdr) { + /* tell peer we return some rx buffer */ + virtqueue_kick(rvdev->rvq); + } + metal_mutex_release(&rdev->lock); + } +} + +/** + * rpmsg_virtio_ns_callback + * + * This callback handles name service announcement from the remote device + * and creates/deletes rpmsg channels. + * + * @param ept - 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 - rpmag endpoint callback handled + */ +static int rpmsg_virtio_ns_callback(struct rpmsg_endpoint *ept, void *data, + size_t len, uint32_t src, void *priv) +{ + struct rpmsg_device *rdev = ept->rdev; + struct rpmsg_virtio_device *rvdev = (struct rpmsg_virtio_device *)rdev; + struct metal_io_region *io = rvdev->shbuf_io; + struct rpmsg_endpoint *_ept; + struct rpmsg_ns_msg *ns_msg; + uint32_t dest; + char name[RPMSG_NAME_SIZE]; + + (void)priv; + (void)src; + + ns_msg = data; + if (len != sizeof(*ns_msg)) + /* Returns as the message is corrupted */ + return RPMSG_SUCCESS; + metal_io_block_read(io, + metal_io_virt_to_offset(io, ns_msg->name), + &name, sizeof(name)); + dest = ns_msg->addr; + + /* check if a Ept has been locally registered */ + metal_mutex_acquire(&rdev->lock); + _ept = rpmsg_get_endpoint(rdev, name, RPMSG_ADDR_ANY, dest); + + if (ns_msg->flags & RPMSG_NS_DESTROY) { + if (_ept) + _ept->dest_addr = RPMSG_ADDR_ANY; + metal_mutex_release(&rdev->lock); + if (_ept && _ept->ns_unbind_cb) + _ept->ns_unbind_cb(ept); + } else { + if (!_ept) { + /* + * send callback to application, that can + * - create the associated endpoints. + * - store information for future use. + * - just ignore the request as service not supported. + */ + metal_mutex_release(&rdev->lock); + if (rdev->ns_bind_cb) + rdev->ns_bind_cb(rdev, name, dest); + } else { + _ept->dest_addr = dest; + metal_mutex_release(&rdev->lock); + } + } + + return RPMSG_SUCCESS; +} + +int rpmsg_virtio_get_buffer_size(struct rpmsg_device *rdev) +{ + int size; + struct rpmsg_virtio_device *rvdev; + + if (!rdev) + return RPMSG_ERR_PARAM; + metal_mutex_acquire(&rdev->lock); + rvdev = (struct rpmsg_virtio_device *)rdev; + size = _rpmsg_virtio_get_buffer_size(rvdev); + metal_mutex_release(&rdev->lock); + return size; +} + +int rpmsg_init_vdev(struct rpmsg_virtio_device *rvdev, + struct virtio_device *vdev, + rpmsg_ns_bind_cb ns_bind_cb, + struct metal_io_region *shm_io, + struct rpmsg_virtio_shm_pool *shpool) +{ + struct rpmsg_device *rdev; + const char *vq_names[RPMSG_NUM_VRINGS]; + vq_callback callback[RPMSG_NUM_VRINGS]; + int status; + unsigned int i, role; + + rdev = &rvdev->rdev; + memset(rdev, 0, sizeof(*rdev)); + metal_mutex_init(&rdev->lock); + rvdev->vdev = vdev; + rdev->ns_bind_cb = ns_bind_cb; + vdev->priv = rvdev; + rdev->ops.send_offchannel_raw = rpmsg_virtio_send_offchannel_raw; + role = rpmsg_virtio_get_role(rvdev); + +#ifndef VIRTIO_MASTER_ONLY + if (role == RPMSG_REMOTE) { + /* wait synchro with the master */ + rpmsg_virtio_wait_remote_ready(rvdev); + } +#endif /*!VIRTIO_MASTER_ONLY*/ + vdev->features = rpmsg_virtio_get_features(rvdev); + rdev->support_ns = !!(vdev->features & (1 << VIRTIO_RPMSG_F_NS)); + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + /* + * Since device is RPMSG Remote so we need to manage the + * shared buffers. Create shared memory pool to handle buffers. + */ + if (!shpool) + return RPMSG_ERR_PARAM; + if (!shpool->size) + return RPMSG_ERR_NO_BUFF; + rvdev->shpool = shpool; + + vq_names[0] = "rx_vq"; + vq_names[1] = "tx_vq"; + callback[0] = rpmsg_virtio_rx_callback; + callback[1] = rpmsg_virtio_tx_callback; + rvdev->rvq = vdev->vrings_info[0].vq; + rvdev->svq = vdev->vrings_info[1].vq; + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + +#ifndef VIRTIO_MASTER_ONLY + (void)shpool; + if (role == RPMSG_REMOTE) { + vq_names[0] = "tx_vq"; + vq_names[1] = "rx_vq"; + callback[0] = rpmsg_virtio_tx_callback; + callback[1] = rpmsg_virtio_rx_callback; + rvdev->rvq = vdev->vrings_info[1].vq; + rvdev->svq = vdev->vrings_info[0].vq; + } +#endif /*!VIRTIO_MASTER_ONLY*/ + rvdev->shbuf_io = shm_io; + + /* Create virtqueues for remote device */ + status = rpmsg_virtio_create_virtqueues(rvdev, 0, RPMSG_NUM_VRINGS, + vq_names, callback); + if (status != RPMSG_SUCCESS) + return status; + + /* + * Suppress "tx-complete" interrupts + * since send method use busy loop when buffer pool exhaust + */ + virtqueue_disable_cb(rvdev->svq); + + /* TODO: can have a virtio function to set the shared memory I/O */ + for (i = 0; i < RPMSG_NUM_VRINGS; i++) { + struct virtqueue *vq; + + vq = vdev->vrings_info[i].vq; + vq->shm_io = shm_io; + } + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) { + struct virtqueue_buf vqbuf; + unsigned int idx; + void *buffer; + + vqbuf.len = RPMSG_BUFFER_SIZE; + for (idx = 0; idx < rvdev->rvq->vq_nentries; idx++) { + /* Initialize TX virtqueue buffers for remote device */ + buffer = rpmsg_virtio_shm_pool_get_buffer(shpool, + RPMSG_BUFFER_SIZE); + + if (!buffer) { + return RPMSG_ERR_NO_BUFF; + } + + vqbuf.buf = buffer; + + metal_io_block_set(shm_io, + metal_io_virt_to_offset(shm_io, + buffer), + 0x00, RPMSG_BUFFER_SIZE); + status = + virtqueue_add_buffer(rvdev->rvq, &vqbuf, 0, 1, + buffer); + + if (status != RPMSG_SUCCESS) { + return status; + } + } + } +#endif /*!VIRTIO_SLAVE_ONLY*/ + + /* Initialize channels and endpoints list */ + metal_list_init(&rdev->endpoints); + + /* + * Create name service announcement endpoint if device supports name + * service announcement feature. + */ + if (rdev->support_ns) { + rpmsg_initialize_ept(&rdev->ns_ept, "NS", + RPMSG_NS_EPT_ADDR, RPMSG_NS_EPT_ADDR, + rpmsg_virtio_ns_callback, NULL); + rpmsg_register_endpoint(rdev, &rdev->ns_ept); + } + +#ifndef VIRTIO_SLAVE_ONLY + if (role == RPMSG_MASTER) + rpmsg_virtio_set_status(rvdev, VIRTIO_CONFIG_STATUS_DRIVER_OK); +#endif /*!VIRTIO_SLAVE_ONLY*/ + + return status; +} + +void rpmsg_deinit_vdev(struct rpmsg_virtio_device *rvdev) +{ + struct metal_list *node; + struct rpmsg_device *rdev; + struct rpmsg_endpoint *ept; + + rdev = &rvdev->rdev; + while (!metal_list_is_empty(&rdev->endpoints)) { + node = rdev->endpoints.next; + ept = metal_container_of(node, struct rpmsg_endpoint, node); + rpmsg_destroy_ept(ept); + } + + rvdev->rvq = 0; + rvdev->svq = 0; + + metal_mutex_deinit(&rdev->lock); +} diff --git a/lib/rpmsg/rpmsg_virtio.o b/lib/rpmsg/rpmsg_virtio.o new file mode 100644 index 0000000..021d342 Binary files /dev/null and b/lib/rpmsg/rpmsg_virtio.o differ diff --git a/lib/virtio/CMakeLists.txt b/lib/virtio/CMakeLists.txt new file mode 100644 index 0000000..0497514 --- /dev/null +++ b/lib/virtio/CMakeLists.txt @@ -0,0 +1,2 @@ +collect (PROJECT_LIB_SOURCES virtio.c) +collect (PROJECT_LIB_SOURCES virtqueue.c) diff --git a/lib/virtio/virtio.c b/lib/virtio/virtio.c new file mode 100644 index 0000000..c57985e --- /dev/null +++ b/lib/virtio/virtio.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include + +static const char *virtio_feature_name(unsigned long feature, + const struct virtio_feature_desc *); + +/* + * TODO : + * This structure may change depending on the types of devices we support. + */ +static const 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 const 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) +{ + const struct virtio_ident *ident; + + for (ident = virtio_ident_table; ident->name; ident++) { + if (ident->devid == devid) + return ident->name; + } + + return NULL; +} + +static const char *virtio_feature_name(unsigned long val, + const struct virtio_feature_desc *desc) +{ + int i, j; + const struct virtio_feature_desc *descs[2] = { desc, + virtio_common_feature_desc + }; + + for (i = 0; i < 2; i++) { + if (!descs[i]) + 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) +{ + (void)dev; + (void)msg; + (void)features; + + /* TODO: Not used currently - keeping it for future use*/ + virtio_feature_name(0, desc); +} + +int virtio_create_virtqueues(struct virtio_device *vdev, unsigned int flags, + unsigned int nvqs, const char *names[], + vq_callback callbacks[]) +{ + struct virtio_vring_info *vring_info; + struct vring_alloc_info *vring_alloc; + unsigned int num_vrings, i; + int ret; + (void)flags; + + num_vrings = vdev->vrings_num; + if (nvqs > num_vrings) + return ERROR_VQUEUE_INVLD_PARAM; + /* Initialize virtqueue for each vring */ + for (i = 0; i < nvqs; i++) { + vring_info = &vdev->vrings_info[i]; + + vring_alloc = &vring_info->info; +#ifndef VIRTIO_SLAVE_ONLY + if (vdev->role == VIRTIO_DEV_MASTER) { + size_t offset; + struct metal_io_region *io = vring_info->io; + + offset = metal_io_virt_to_offset(io, + vring_alloc->vaddr); + metal_io_block_set(io, offset, 0, + vring_size(vring_alloc->num_descs, + vring_alloc->align)); + } +#endif + ret = virtqueue_create(vdev, i, names[i], vring_alloc, + callbacks[i], vdev->func->notify, + vring_info->vq); + if (ret) + return ret; + } + return 0; +} + diff --git a/lib/virtio/virtio.o b/lib/virtio/virtio.o new file mode 100644 index 0000000..6224229 Binary files /dev/null and b/lib/virtio/virtio.o differ diff --git a/lib/virtio/virtqueue.c b/lib/virtio/virtqueue.c new file mode 100644 index 0000000..c7edf58 --- /dev/null +++ b/lib/virtio/virtqueue.c @@ -0,0 +1,717 @@ +/*- + * Copyright (c) 2011, Bryan Venteicher + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +/* Prototype for internal functions. */ +static void vq_ring_init(struct virtqueue *, void *, int); +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 virtqueue_buf *, 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(struct virtqueue *vq); +static void vq_ring_notify(struct virtqueue *vq); +#ifndef VIRTIO_SLAVE_ONLY +static int virtqueue_nused(struct virtqueue *vq); +#endif +#ifndef VIRTIO_MASTER_ONLY +static int virtqueue_navail(struct virtqueue *vq); +#endif + +/* Default implementation of P2V based on libmetal */ +static inline void *virtqueue_phys_to_virt(struct virtqueue *vq, + metal_phys_addr_t phys) +{ + struct metal_io_region *io = vq->shm_io; + + return metal_io_phys_to_virt(io, phys); +} + +/* Default implementation of V2P based on libmetal */ +static inline metal_phys_addr_t virtqueue_virt_to_phys(struct virtqueue *vq, + void *buf) +{ + struct metal_io_region *io = vq->shm_io; + + return metal_io_virt_to_phys(io, buf); +} + +/** + * 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 vq - Created VirtIO queue. + * + * @return - Function status + */ +int virtqueue_create(struct virtio_device *virt_dev, unsigned short id, + const char *name, struct vring_alloc_info *ring, + void (*callback)(struct virtqueue *vq), + void (*notify)(struct virtqueue *vq), + struct virtqueue *vq) +{ + int status = VQUEUE_SUCCESS; + + VQ_PARAM_CHK(ring == 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); + VQ_PARAM_CHK(vq == NULL, status, ERROR_NO_MEM); + + if (status == VQUEUE_SUCCESS) { + vq->vq_dev = virt_dev; + vq->vq_name = name; + vq->vq_queue_index = id; + vq->vq_nentries = ring->num_descs; + vq->vq_free_cnt = vq->vq_nentries; + vq->callback = callback; + vq->notify = notify; + + /* Initialize vring control block in virtqueue. */ + vq_ring_init(vq, ring->vaddr, ring->align); + } + + 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 buf_list - Pointer to a list of virtqueue buffers. + * @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 virtqueue_buf *buf_list, + int readable, int writable, void *cookie) +{ + struct vq_desc_extra *dxp = NULL; + int status = VQUEUE_SUCCESS; + uint16_t head_idx; + uint16_t idx; + int needed; + + needed = readable + writable; + + VQ_PARAM_CHK(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); + + VQUEUE_BUSY(vq); + + if (status == VQUEUE_SUCCESS) { + VQASSERT(vq, cookie != 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 == 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, + buf_list, 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_get_buffer - Returns used buffers from VirtIO queue + * + * @param vq - Pointer to VirtIO queue control block + * @param len - Length of conumed buffer + * @param idx - index of the buffer + * + * @return - Pointer to used buffer + */ +void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx) +{ + struct vring_used_elem *uep; + void *cookie; + uint16_t used_idx, desc_idx; + + if (!vq || vq->vq_used_cons_idx == vq->vq_ring.used->idx) + return NULL; + + VQUEUE_BUSY(vq); + + used_idx = vq->vq_used_cons_idx++ & (vq->vq_nentries - 1); + uep = &vq->vq_ring.used->ring[used_idx]; + + atomic_thread_fence(memory_order_seq_cst); + + desc_idx = (uint16_t)uep->id; + if (len) + *len = uep->len; + + vq_ring_free_chain(vq, desc_idx); + + cookie = vq->vq_descx[desc_idx].cookie; + vq->vq_descx[desc_idx].cookie = NULL; + + if (idx) + *idx = used_idx; + VQUEUE_IDLE(vq); + + return cookie; +} + +uint32_t virtqueue_get_buffer_length(struct virtqueue *vq, uint16_t idx) +{ + return vq->vq_ring.desc[idx].len; +} + +/** + * virtqueue_free - Frees VirtIO queue resources + * + * @param vq - Pointer to VirtIO queue control block + * + */ +void virtqueue_free(struct virtqueue *vq) +{ + if (vq) { + if (vq->vq_free_cnt != vq->vq_nentries) { + metal_log(METAL_LOG_WARNING, + "%s: freeing non-empty virtqueue\r\n", + vq->vq_name); + } + + metal_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; + + atomic_thread_fence(memory_order_seq_cst); + if (vq->vq_available_idx == vq->vq_ring.avail->idx) { + return NULL; + } + + VQUEUE_BUSY(vq); + + head_idx = vq->vq_available_idx++ & (vq->vq_nentries - 1); + *avail_idx = vq->vq_ring.avail->ring[head_idx]; + + buffer = virtqueue_phys_to_virt(vq, 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, + uint32_t len) +{ + struct vring_used_elem *used_desc = NULL; + uint16_t used_idx; + + if (head_idx > vq->vq_nentries) { + 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; + + atomic_thread_fence(memory_order_seq_cst); + + vq->vq_ring.used->idx++; + + /* Keep pending count until virtqueue_notify(). */ + vq->vq_queued_cnt++; + + 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_disable_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_dev->features & VIRTIO_RING_F_EVENT_IDX) { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) { + vring_used_event(&vq->vq_ring) = + vq->vq_used_cons_idx - vq->vq_nentries - 1; + } +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) { + vring_avail_event(&vq->vq_ring) = + vq->vq_available_idx - vq->vq_nentries - 1; + } +#endif /*VIRTIO_MASTER_ONLY*/ + } else { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) + vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) + vq->vq_ring.used->flags |= VRING_USED_F_NO_NOTIFY; +#endif /*VIRTIO_MASTER_ONLY*/ + } + + 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. */ + atomic_thread_fence(memory_order_seq_cst); + + if (vq_ring_must_notify(vq)) + vq_ring_notify(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) + return; + + metal_log(METAL_LOG_DEBUG, + "VQ: %s - size=%d; free=%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, + 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 0; + } + + 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 virtqueue_buf *buf_list, int readable, + int writable) +{ + struct vring_desc *dp; + int i, needed; + uint16_t idx; + + (void)vq; + + needed = readable + writable; + + for (i = 0, idx = head_idx; i < needed; i++, idx = dp->next) { + VQASSERT(vq, idx != VQ_RING_DESC_CHAIN_END, + "premature end of free desc chain"); + + dp = &desc[idx]; + dp->addr = virtqueue_virt_to_phys(vq, buf_list[i].buf); + dp->len = buf_list[i].len; + 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, void *ring_mem, int alignment) +{ + struct vring *vr; + int size; + + size = vq->vq_nentries; + vr = &vq->vq_ring; + + vring_init(vr, size, ring_mem, alignment); + +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) { + int i; + + for (i = 0; i < size - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = VQ_RING_DESC_CHAIN_END; + } +#endif /*VIRTIO_SLAVE_ONLY*/ +} + +/** + * + * 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; + + atomic_thread_fence(memory_order_seq_cst); + + 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_dev->features & VIRTIO_RING_F_EVENT_IDX) { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) + vring_used_event(&vq->vq_ring) = + vq->vq_used_cons_idx + ndesc; +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) + vring_avail_event(&vq->vq_ring) = + vq->vq_available_idx + ndesc; +#endif /*VIRTIO_MASTER_ONLY*/ + } else { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) + vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) + vq->vq_ring.used->flags &= ~VRING_USED_F_NO_NOTIFY; +#endif /*VIRTIO_MASTER_ONLY*/ + } + + atomic_thread_fence(memory_order_seq_cst); + + /* + * 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. + */ +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) { + if (virtqueue_nused(vq) > ndesc) { + return 1; + } + } +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) { + if (virtqueue_navail(vq) > ndesc) { + return 1; + } + } +#endif /*VIRTIO_MASTER_ONLY*/ + + return 0; +} + +/** + * + * virtqueue_interrupt + * + */ +void virtqueue_notification(struct virtqueue *vq) +{ + atomic_thread_fence(memory_order_seq_cst); + if (vq->callback) + vq->callback(vq); +} + +/** + * + * vq_ring_must_notify + * + */ +static int vq_ring_must_notify(struct virtqueue *vq) +{ + uint16_t new_idx, prev_idx, event_idx; + + if (vq->vq_dev->features & VIRTIO_RING_F_EVENT_IDX) { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) { + 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; + } +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) { + new_idx = vq->vq_ring.used->idx; + prev_idx = new_idx - vq->vq_queued_cnt; + event_idx = vring_used_event(&vq->vq_ring); + return vring_need_event(event_idx, new_idx, + prev_idx) != 0; + } +#endif /*VIRTIO_MASTER_ONLY*/ + } else { +#ifndef VIRTIO_SLAVE_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_MASTER) + return (vq->vq_ring.used->flags & + VRING_USED_F_NO_NOTIFY) == 0; +#endif /*VIRTIO_SLAVE_ONLY*/ +#ifndef VIRTIO_MASTER_ONLY + if (vq->vq_dev->role == VIRTIO_DEV_SLAVE) + return (vq->vq_ring.avail->flags & + VRING_AVAIL_F_NO_INTERRUPT) == 0; +#endif /*VIRTIO_MASTER_ONLY*/ + } + + return 0; +} + +/** + * + * vq_ring_notify + * + */ +static void vq_ring_notify(struct virtqueue *vq) +{ + if (vq->notify) + vq->notify(vq); +} + +/** + * + * virtqueue_nused + * + */ +#ifndef VIRTIO_SLAVE_ONLY +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; +} +#endif /*VIRTIO_SLAVE_ONLY*/ + +/** + * + * virtqueue_navail + * + */ +#ifndef VIRTIO_MASTER_ONLY +static int virtqueue_navail(struct virtqueue *vq) +{ + uint16_t avail_idx, navail; + + avail_idx = vq->vq_ring.avail->idx; + + navail = (uint16_t)(avail_idx - vq->vq_available_idx); + VQASSERT(vq, navail <= vq->vq_nentries, "avail more than available"); + + return navail; +} +#endif /*VIRTIO_MASTER_ONLY*/ diff --git a/lib/virtio/virtqueue.o b/lib/virtio/virtqueue.o new file mode 100644 index 0000000..3489ff5 Binary files /dev/null and b/lib/virtio/virtqueue.o differ diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl new file mode 100755 index 0000000..0e1e915 --- /dev/null +++ b/scripts/checkpatch.pl @@ -0,0 +1,6494 @@ +#!/usr/bin/env perl +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp (the ugly bit) +# (c) 2007,2008, Andy Whitcroft (new conditions, test suite) +# (c) 2008-2010 Andy Whitcroft +# Licensed under the terms of the GNU GPL License version 2 + +use strict; +use warnings; +use POSIX; +use File::Basename; +use Cwd 'abs_path'; +use Term::ANSIColor qw(:constants); + +my $P = $0; +my $D = dirname(abs_path($P)); + +my $V = '0.32'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $showfile = 0; +my $file = 0; +my $git = 0; +my %git_commits = (); +my $check = 0; +my $check_orig = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $show_types = 0; +my $list_types = 0; +my $fix = 0; +my $fix_inplace = 0; +my $root; +my %debug; +my %camelcase = (); +my %use_type = (); +my @use = (); +my %ignore_type = (); +my @ignore = (); +my @exclude = (); +my $help = 0; +my $configuration_file = ".checkpatch.conf"; +my $max_line_length = 80; +my $ignore_perl_version = 0; +my $minimum_perl_version = 5.10.0; +my $min_conf_desc_length = 4; +my $spelling_file = "$D/spelling.txt"; +my $codespell = 0; +my $codespellfile = "/usr/share/codespell/dictionary.txt"; +my $conststructsfile = "$D/const_structs.checkpatch"; +my $typedefsfile = ""; +my $color = "auto"; +my $allow_c99_comments = 0; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + --showfile emit diffed file position, not input file position + -g, --git treat FILE as a single commit or git revision range + single git commit with: + + ^ + ~n + multiple git commits with: + .. + ... + - + git merges are ignored + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --list-types list the possible message types + --types TYPE(,TYPE2...) show only these comma separated message types + --ignore TYPE(,TYPE2...) ignore various comma separated message types + --exclude DIR(,DIR22...) exclude directories + --show-types show the specific message type in the output + --max-line-length=n set the maximum line length, if exceeded, warn + --min-conf-desc-length=n set the min description length, if shorter, warn + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + ".EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style + --fix-inplace EXPERIMENTAL - may create horrible results + Is the same as --fix, but overwrites the input + file. It's your fault if there's no backup or git + --ignore-perl-version override checking of perl version. expect + runtime errors. + --codespell Use the codespell dictionary for spelling/typos + (default:/usr/share/codespell/dictionary.txt) + --codespellfile Use this codespell dictionary + --typedefsfile Read additional types from this file + --color[=WHEN] Use colors 'always', 'never', or only when output + is a terminal ('auto'). Default is 'auto'. + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +sub uniq { + my %seen; + return grep { !$seen{$_}++ } @_; +} + +sub list_types { + my ($exitcode) = @_; + + my $count = 0; + + local $/ = undef; + + open(my $script, '<', abs_path($P)) or + die "$P: Can't read '$P' $!\n"; + + my $text = <$script>; + close($script); + + my @types = (); + # Also catch when type or level is passed through a variable + for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { + push (@types, $_); + } + @types = sort(uniq(@types)); + print("#\tMessage type\n\n"); + foreach my $type (@types) { + print(++$count . "\t" . $type . "\n"); + } + + exit($exitcode); +} + +my $conf = which_conf($configuration_file); +if (-f $conf) { + my @conf_args; + open(my $conffile, '<', "$conf") + or warn "$P: Can't find a readable $configuration_file file $!\n"; + + while (<$conffile>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + $line =~ s/\s+/ /g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my @words = split(" ", $line); + foreach my $word (@words) { + last if ($word =~ m/^#/); + push (@conf_args, $word); + } + } + close($conffile); + unshift(@ARGV, @conf_args) if @conf_args; +} + +# Perl's Getopt::Long allows options to take optional arguments after a space. +# Prevent --color by itself from consuming other arguments +foreach (@ARGV) { + if ($_ eq "--color" || $_ eq "-color") { + $_ = "--color=$color"; + } +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'showfile!' => \$showfile, + 'f|file!' => \$file, + 'g|git!' => \$git, + 'subjective!' => \$check, + 'strict!' => \$check, + 'ignore=s' => \@ignore, + 'exclude=s' => \@exclude, + 'types=s' => \@use, + 'show-types!' => \$show_types, + 'list-types!' => \$list_types, + 'max-line-length=i' => \$max_line_length, + 'min-conf-desc-length=i' => \$min_conf_desc_length, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + 'fix!' => \$fix, + 'fix-inplace!' => \$fix_inplace, + 'ignore-perl-version!' => \$ignore_perl_version, + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'codespell!' => \$codespell, + 'codespellfile=s' => \$codespellfile, + 'typedefsfile=s' => \$typedefsfile, + 'color=s' => \$color, + 'no-color' => \$color, #keep old behaviors of -nocolor + 'nocolor' => \$color, #keep old behaviors of -nocolor + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +list_types(0) if ($list_types); + +$fix = 1 if ($fix_inplace); +$check_orig = $check; + +my $exit = 0; + +if ($^V && $^V lt $minimum_perl_version) { + printf "$P: requires at least perl version %vd\n", $minimum_perl_version; + if (!$ignore_perl_version) { + exit(1); + } +} + +#if no filenames are given, push '-' to read patch from stdin +if ($#ARGV < 0) { + push(@ARGV, '-'); +} + +if ($color =~ /^[01]$/) { + $color = !$color; +} elsif ($color =~ /^always$/i) { + $color = 1; +} elsif ($color =~ /^never$/i) { + $color = 0; +} elsif ($color =~ /^auto$/i) { + $color = (-t STDOUT); +} else { + die "Invalid color mode: $color\n"; +} + +sub hash_save_array_words { + my ($hashRef, $arrayRef) = @_; + + my @array = split(/,/, join(',', @$arrayRef)); + foreach my $word (@array) { + $word =~ s/\s*\n?$//g; + $word =~ s/^\s*//g; + $word =~ s/\s+/ /g; + $word =~ tr/[a-z]/[A-Z]/; + + next if ($word =~ m/^\s*#/); + next if ($word =~ m/^\s*$/); + + $hashRef->{$word}++; + } +} + +sub hash_show_words { + my ($hashRef, $prefix) = @_; + + if (keys %$hashRef) { + print "\nNOTE: $prefix message types:"; + foreach my $word (sort keys %$hashRef) { + print " $word"; + } + print "\n"; + } +} + +hash_save_array_words(\%ignore_type, \@ignore); +hash_save_array_words(\%use_type, \@use); + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +my $rpt_cleaners = 0; + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __force| + __iomem| + __must_check| + __init_refok| + __kprobes| + __ref| + __rcu| + __private + }x; +our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; +our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; +our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; +our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; +our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __percpu| + __nocast| + __safe| + __bitwise| + __packed__| + __packed2__| + __naked| + __maybe_unused| + __always_unused| + __noreturn| + __used| + __cold| + __pure| + __noclone| + __deprecated| + __read_mostly| + __kprobes| + $InitAttribute| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak| + __syscall| + __syscall_inline + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; +our $Octal = qr{0[0-7]+$Int_type?}; +our $String = qr{"[X\t]*"}; +our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; +our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; +our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; +our $Float = qr{$Float_hex|$Float_dec|$Float_int}; +our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; +our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; +our $Compare = qr{<=|>=|==|!=|<|(?}; +our $Arithmetic = qr{\+|-|\*|\/|%}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic + }x; + +our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; + +our $BasicType; +our $NonptrType; +our $NonptrTypeMisordered; +our $NonptrTypeWithAttr; +our $Type; +our $TypeMisordered; +our $Declare; +our $DeclareMisordered; + +our $NON_ASCII_UTF8 = qr{ + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $UTF8 = qr{ + [\x09\x0A\x0D\x20-\x7E] # ASCII + | $NON_ASCII_UTF8 +}x; + +our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; +our $typeOtherOSTypedefs = qr{(?x: + u_(?:char|short|int|long) | # bsd + u(?:nchar|short|int|long) # sysv +)}; +our $typeKernelTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)_t| + atomic_t +)}; +our $typeTypedefs = qr{(?x: + $typeC99Typedefs\b| + $typeOtherOSTypedefs\b| + $typeKernelTypedefs\b +)}; + +our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; + +our $logFunctions = qr{(?x: + printk(?:_ratelimited|_once|_deferred_once|_deferred|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + WARN(?:_RATELIMIT|_ONCE|)| + panic| + MODULE_[A-Z_]+| + seq_vprintf|seq_printf|seq_puts +)}; + +our $signature_tags = qr{(?xi: + Signed-off-by:| + Acked-by:| + Tested-by:| + Reviewed-by:| + Reported-by:| + Suggested-by:| + To:| + Cc: +)}; + +our @typeListMisordered = ( + qr{char\s+(?:un)?signed}, + qr{int\s+(?:(?:un)?signed\s+)?short\s}, + qr{int\s+short(?:\s+(?:un)?signed)}, + qr{short\s+int(?:\s+(?:un)?signed)}, + qr{(?:un)?signed\s+int\s+short}, + qr{short\s+(?:un)?signed}, + qr{long\s+int\s+(?:un)?signed}, + qr{int\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed\s+int}, + qr{int\s+(?:un)?signed\s+long}, + qr{int\s+(?:un)?signed}, + qr{int\s+long\s+long\s+(?:un)?signed}, + qr{long\s+long\s+int\s+(?:un)?signed}, + qr{long\s+long\s+(?:un)?signed\s+int}, + qr{long\s+long\s+(?:un)?signed}, + qr{long\s+(?:un)?signed}, +); + +our @typeList = ( + qr{void}, + qr{(?:(?:un)?signed\s+)?char}, + qr{(?:(?:un)?signed\s+)?short\s+int}, + qr{(?:(?:un)?signed\s+)?short}, + qr{(?:(?:un)?signed\s+)?int}, + qr{(?:(?:un)?signed\s+)?long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, + qr{(?:(?:un)?signed\s+)?long\s+long}, + qr{(?:(?:un)?signed\s+)?long}, + qr{(?:un)?signed}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, + @typeListMisordered, +); + +our $C90_int_types = qr{(?x: + long\s+long\s+int\s+(?:un)?signed| + long\s+long\s+(?:un)?signed\s+int| + long\s+long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+long\s+int| + (?:(?:un)?signed\s+)?long\s+long| + int\s+long\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long\s+long| + + long\s+int\s+(?:un)?signed| + long\s+(?:un)?signed\s+int| + long\s+(?:un)?signed| + (?:(?:un)?signed\s+)?long\s+int| + (?:(?:un)?signed\s+)?long| + int\s+long\s+(?:un)?signed| + int\s+(?:(?:un)?signed\s+)?long| + + int\s+(?:un)?signed| + (?:(?:un)?signed\s+)?int +)}; + +our @typeListFile = (); +our @typeListWithAttr = ( + @typeList, + qr{struct\s+$InitAttribute\s+$Ident}, + qr{union\s+$InitAttribute\s+$Ident}, +); + +our @modifierList = ( + qr{fastcall}, +); +our @modifierListFile = (); + +our @mode_permission_funcs = ( + ["module_param", 3], + ["module_param_(?:array|named|string)", 4], + ["module_param_array_named", 5], + ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], + ["proc_create(?:_data|)", 2], + ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], + ["IIO_DEV_ATTR_[A-Z_]+", 1], + ["SENSOR_(?:DEVICE_|)ATTR_2", 2], + ["SENSOR_TEMPLATE(?:_2|)", 3], + ["__ATTR", 2], +); + +#Create a search pattern for all these functions to speed up a loop below +our $mode_perms_search = ""; +foreach my $entry (@mode_permission_funcs) { + $mode_perms_search .= '|' if ($mode_perms_search ne ""); + $mode_perms_search .= $entry->[0]; +} + +our $mode_perms_world_writable = qr{ + S_IWUGO | + S_IWOTH | + S_IRWXUGO | + S_IALLUGO | + 0[0-7][0-7][2367] +}x; + +our %mode_permission_string_types = ( + "S_IRWXU" => 0700, + "S_IRUSR" => 0400, + "S_IWUSR" => 0200, + "S_IXUSR" => 0100, + "S_IRWXG" => 0070, + "S_IRGRP" => 0040, + "S_IWGRP" => 0020, + "S_IXGRP" => 0010, + "S_IRWXO" => 0007, + "S_IROTH" => 0004, + "S_IWOTH" => 0002, + "S_IXOTH" => 0001, + "S_IRWXUGO" => 0777, + "S_IRUGO" => 0444, + "S_IWUGO" => 0222, + "S_IXUGO" => 0111, +); + +#Create a search pattern for all these strings to speed up a loop below +our $mode_perms_string_search = ""; +foreach my $entry (keys %mode_permission_string_types) { + $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); + $mode_perms_string_search .= $entry; +} + +our $allowed_asm_includes = qr{(?x: + irq| + memory| + time| + reboot +)}; +# memory.h: ARM has a custom one + +# Load common spelling mistakes and build regular expression list. +my $misspellings; +my %spelling_fix; + +if (open(my $spelling, '<', $spelling_file)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my ($suspect, $fix) = split(/\|\|/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); +} else { + warn "No typos will be found - file '$spelling_file': $!\n"; +} + +if ($codespell) { + if (open(my $spelling, '<', $codespellfile)) { + while (<$spelling>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + next if ($line =~ m/, disabled/i); + + $line =~ s/,.*$//; + + my ($suspect, $fix) = split(/->/, $line); + + $spelling_fix{$suspect} = $fix; + } + close($spelling); + } else { + warn "No codespell typos will be found - file '$codespellfile': $!\n"; + } +} + +$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; + +sub read_words { + my ($wordsRef, $file) = @_; + + if (open(my $words, '<', $file)) { + while (<$words>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + if ($line =~ /\s/) { + print("$file: '$line' invalid - ignored\n"); + next; + } + + $$wordsRef .= '|' if ($$wordsRef ne ""); + $$wordsRef .= $line; + } + close($file); + return 1; + } + + return 0; +} + +my $const_structs = ""; +#read_words(\$const_structs, $conststructsfile) +# or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; + +my $typeOtherTypedefs = ""; +if (length($typedefsfile)) { + read_words(\$typeOtherTypedefs, $typedefsfile) + or warn "No additional types will be considered - file '$typedefsfile': $!\n"; +} +$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; + my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; + my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; + my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $BasicType = qr{ + (?:$typeTypedefs\b)| + (?:${all}\b) + }x; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeMisordered = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:${Misordered}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $NonptrTypeWithAttr = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${allWithAttr}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $TypeMisordered = qr{ + $NonptrTypeMisordered + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; + $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; +} +build_types(); + +our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; + +# Using $balanced_parens, $LvalOrFunc, or $FuncArg +# requires at least perl version v5.10.0 +# Any use must be runtime checked with $^V + +our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; +our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; +our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; + +our $declaration_macros = qr{(?x: + (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| + (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| + (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( +)}; + +sub deparenthesize { + my ($string) = @_; + return "" if (!defined($string)); + + while ($string =~ /^\s*\(.*\)\s*$/) { + $string =~ s@^\s*\(\s*@@; + $string =~ s@\s*\)\s*$@@; + } + + $string =~ s@\s+@ @g; + + return $string; +} + +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { + $camelcase{$1} = 1; + } + } +} + +sub is_maintained_obsolete { + my ($filename) = @_; + + return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); + + my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; + + return $status =~ /obsolete/i; +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + my $camelcase_cache = ""; + my @include_files = (); + + $camelcase_seeded = 1; + + if (-e ".git") { + my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } else { + my $last_mod_date = 0; + $files = `find $root/include -name "*.h"`; + @include_files = split('\n', $files); + foreach my $file (@include_files) { + my $date = POSIX::strftime("%Y%m%d%H%M", + localtime((stat $file)[9])); + $last_mod_date = $date if ($last_mod_date < $date); + } + $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; + } + + if ($camelcase_cache ne "" && -f $camelcase_cache) { + open(my $camelcase_file, '<', "$camelcase_cache") + or warn "$P: Can't read '$camelcase_cache' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } + + if (-e ".git") { + $files = `git ls-files "include/*.h"`; + @include_files = split('\n', $files); + } + + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + + if ($camelcase_cache ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_cache") + or warn "$P: Can't write '$camelcase_cache' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } +} + +sub git_commit_info { + my ($commit, $id, $desc) = @_; + + return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); + + my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; + $output =~ s/^\s*//gm; + my @lines = split("\n", $output); + + return ($id, $desc) if ($#lines < 0); + + if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { +# Maybe one day convert this block of bash into something that returns +# all matching commit ids, but it's very slow... +# +# echo "checking commits $1..." +# git rev-list --remotes | grep -i "^$1" | +# while read line ; do +# git log --format='%H %s' -1 $line | +# echo "commit $(cut -c 1-12,41-)" +# done + } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + $id = undef; + } else { + $id = substr($lines[0], 0, 12); + $desc = substr($lines[0], 41); + } + + return ($id, $desc); +} + +$chk_signoff = 0 if ($file); + +my @rawlines = (); +my @lines = (); +my @fixed = (); +my @fixed_inserted = (); +my @fixed_deleted = (); +my $fixlinenr = -1; + +# If input is git commits, extract all commits from the commit expressions. +# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. +die "$P: No git repository found\n" if ($git && !-e ".git"); + +if ($git) { + my @commits = (); + foreach my $commit_expr (@ARGV) { + my $git_range; + if ($commit_expr =~ m/^(.*)-(\d+)$/) { + $git_range = "-$2 $1"; + } elsif ($commit_expr =~ m/\.\./) { + $git_range = "$commit_expr"; + } else { + $git_range = "-1 $commit_expr"; + } + my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`; + foreach my $line (split(/\n/, $lines)) { + $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; + next if (!defined($1) || !defined($2)); + my $sha1 = $1; + my $subject = $2; + unshift(@commits, $sha1); + $git_commits{$sha1} = $subject; + } + } + die "$P: no git commits after extraction!\n" if (@commits == 0); + @ARGV = @commits; +} + +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($git) { + open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || + die "$P: $filename: git format-patch failed - $!\n"; + } elsif ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } elsif ($git) { + $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + + if ($#ARGV > 0 && $quiet == 0) { + print '-' x length($vname) . "\n"; + print "$vname\n"; + print '-' x length($vname) . "\n"; + } + + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); + @fixed = (); + @fixed_inserted = (); + @fixed_deleted = (); + $fixlinenr = -1; + @modifierListFile = (); + @typeListFile = (); + build_types(); +} + +if (!$quiet) { + hash_show_words(\%use_type, "Used"); + hash_show_words(\%ignore_type, "Ignored"); + + if ($^V lt 5.10.0) { + print << "EOM" + +NOTE: perl $^V is not modern enough to detect all possible issues. + An upgrade to at least perl v5.10.0 is suggested. +EOM + } + if ($exit) { + print << "EOM" + +NOTE: If any of the errors are false positives, please report + them to the maintainers. +EOM + } +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "LICENSE", "CODEOWNERS", "Kconfig", "Makefile", + "README.rst", "doc", "arch", "include", "drivers", + "boards", "kernel", "lib", "scripts", + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub parse_email { + my ($formatted_email) = @_; + + my $name = ""; + my $address = ""; + my $comment = ""; + + if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { + $name = $1; + $address = $2; + $comment = $3 if defined $3; + } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { + $address = $1; + $comment = $2 if defined $2; + $formatted_email =~ s/$address.*$//; + $name = $formatted_email; + $name = trim($name); + $name =~ s/^\"|\"$//g; + # If there's a name left after stripping spaces and + # leading quotes, and the address doesn't have both + # leading and trailing angle brackets, the address + # is invalid. ie: + # "joe smith joe@smith.com" bad + # "joe smith ]+>$/) { + $name = ""; + $address = ""; + $comment = ""; + } + } + + $name = trim($name); + $name =~ s/^\"|\"$//g; + $address = trim($address); + $address =~ s/^\<|\>$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?"; + } + + return $formatted_email; +} + +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + +sub which_conf { + my ($conf) = @_; + + foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (-e "$path/$conf") { + return "$path/$conf"; + } + } + + return ""; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + if ($allow_c99_comments && $res =~ m@(//.*$)@) { + my $match = $1; + $res =~ s/\Q$match\E/"$;" x length($match)/e; + } + + return $res; +} + +sub get_quoted_string { + my ($line, $rawline) = @_; + + return "" if ($line !~ m/($String)/g); + return substr($rawline, $-[0], $+[0] - $-[0]); +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { + $level++; + $type = '#'; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + if (substr($blk, $off + 1, 1) eq ';') { + $off++; + } + last; + } + } + # Preprocessor commands end at the newline unless escaped. + if ($type eq '#' && $c eq "\n" && $p ne "\\") { + $level--; + $type = ''; + $off++; + last; + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $lines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { + print "CAST($1)\n" if ($dbg_values > 1); + push(@av_paren_type, $type); + $type = 'c'; + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do| + \#| + \#\#| + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierListFile, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeListFile, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub show_type { + my ($type) = @_; + + $type =~ tr/[a-z]/[A-Z]/; + + return defined $use_type{$type} if (scalar keys %use_type > 0); + + return !defined $ignore_type{$type}; +} + +sub report { + my ($level, $type, $msg) = @_; + + if (!show_type($type) || + (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { + return 0; + } + my $output = ''; + if ($color) { + if ($level eq 'ERROR') { + $output .= RED; + } elsif ($level eq 'WARNING') { + $output .= YELLOW; + } else { + $output .= GREEN; + } + } + $output .= $prefix . $level . ':'; + if ($show_types) { + $output .= BLUE if ($color); + $output .= "$type:"; + } + $output .= RESET if ($color); + $output .= ' ' . $msg . "\n"; + + if ($showfile) { + my @lines = split("\n", $output, -1); + splice(@lines, 1, 1); + $output = join("\n", @lines); + } + $output = (split('\n', $output))[0] . "\n" if ($terse); + + push(our @report, $output); + + return 1; +} + +sub report_dump { + our @report; +} + +sub fixup_current_range { + my ($lineRef, $offset, $length) = @_; + + if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { + my $o = $1; + my $l = $2; + my $no = $o + $offset; + my $nl = $l + $length; + $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; + } +} + +sub fix_inserted_deleted_lines { + my ($linesRef, $insertedRef, $deletedRef) = @_; + + my $range_last_linenr = 0; + my $delta_offset = 0; + + my $old_linenr = 0; + my $new_linenr = 0; + + my $next_insert = 0; + my $next_delete = 0; + + my @lines = (); + + my $inserted = @{$insertedRef}[$next_insert++]; + my $deleted = @{$deletedRef}[$next_delete++]; + + foreach my $old_line (@{$linesRef}) { + my $save_line = 1; + my $line = $old_line; #don't modify the array + if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename + $delta_offset = 0; + } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk + $range_last_linenr = $new_linenr; + fixup_current_range(\$line, $delta_offset, 0); + } + + while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { + $deleted = @{$deletedRef}[$next_delete++]; + $save_line = 0; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); + } + + while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { + push(@lines, ${$inserted}{'LINE'}); + $inserted = @{$insertedRef}[$next_insert++]; + $new_linenr++; + fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); + } + + if ($save_line) { + push(@lines, $line); + $new_linenr++; + } + + $old_linenr++; + } + + return @lines; +} + +sub fix_insert_line { + my ($linenr, $line) = @_; + + my $inserted = { + LINENR => $linenr, + LINE => $line, + }; + push(@fixed_inserted, $inserted); +} + +sub fix_delete_line { + my ($linenr, $line) = @_; + + my $deleted = { + LINENR => $linenr, + LINE => $line, + }; + + push(@fixed_deleted, $deleted); +} + +sub ERROR { + my ($type, $msg) = @_; + + if (report("ERROR", $type, $msg)) { + our $clean = 0; + our $cnt_error++; + return 1; + } + return 0; +} +sub WARN { + my ($type, $msg) = @_; + + if (report("WARNING", $type, $msg)) { + our $clean = 0; + our $cnt_warn++; + return 1; + } + return 0; +} +sub CHK { + my ($type, $msg) = @_; + + if ($check && report("CHECK", $type, $msg)) { + our $clean = 0; + our $cnt_chk++; + return 1; + } + return 0; +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("USE_RELATIVE_PATH", + "use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub trim { + my ($string) = @_; + + $string =~ s/^\s+|\s+$//g; + + return $string; +} + +sub ltrim { + my ($string) = @_; + + $string =~ s/^\s+//; + + return $string; +} + +sub rtrim { + my ($string) = @_; + + $string =~ s/\s+$//; + + return $string; +} + +sub string_find_replace { + my ($string, $find, $replace) = @_; + + $string =~ s/$find/$replace/g; + + return $string; +} + +sub tabify { + my ($leading) = @_; + + my $source_indent = 8; + my $max_spaces_before_tab = $source_indent - 1; + my $spaces_to_tab = " " x $source_indent; + + #convert leading spaces to tabs + 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; + #Remove spaces before a tab + 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; + + return "$leading"; +} + +sub pos_last_openparen { + my ($line) = @_; + + my $pos = 0; + + my $opens = $line =~ tr/\(/\(/; + my $closes = $line =~ tr/\)/\)/; + + my $last_openparen = 0; + + if (($opens == 0) || ($closes >= $opens)) { + return -1; + } + + my $len = length($line); + + for ($pos = 0; $pos < $len; $pos++) { + my $string = substr($line, $pos); + if ($string =~ /^($FuncArg|$balanced_parens)/) { + $pos += length($1) - 1; + } elsif (substr($line, $pos, 1) eq '(') { + $last_openparen = $pos; + } elsif (index($string, '(') == -1) { + last; + } + } + + return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + my $in_header_lines = $file ? 0 : 1; + my $in_commit_log = 0; #Scanning lines before patch + my $has_commit_log = 0; #Encountered lines before patch + my $commit_log_possible_stack_dump = 0; + my $commit_log_long_line = 0; + my $commit_log_has_diff = 0; + my $reported_maintainer_file = 0; + my $non_utf8_charset = 0; + + my $last_blank_line = 0; + my $last_coalesced_string_linenr = -1; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $context_function; #undef'd unless there's a known function + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + my $suppress_statement = 0; + + my %signatures = (); + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + my $camelcase_file_seeded = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + push(@fixed, $rawline) if ($fix); + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + $fixlinenr = -1; + foreach my $line (@lines) { + $linenr++; + $fixlinenr++; + my $sline = $line; #copy of $line + $sline =~ s/$;/ /g; #with comments as spaces + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if (!$in_commit_log && + $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { + my $context = $4; + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + $suppress_statement = 0; + if ($context =~ /\b(\w+)\s*\(/) { + $context_function = $1; + } else { + undef $context_function; + } + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + my $found_file = 0; + # extract the filename as it passes + if ($line =~ /^diff --git.*?(\S+)$/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + $found_file = 1; + } elsif ($line =~ /^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@ if (!$file); + $in_commit_log = 0; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("PATCH_PREFIX", + "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("MODIFIED_INCLUDE_ASM", + "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); + } + $found_file = 1; + } + my $skipme = 0; + foreach (@exclude) { + if ($realfile =~ m@^(?:$_/)@) { + $skipme = 1; + } + } + if ($skipme) { + next; + } + +#make up the handle for any error we report on this line + if ($showfile) { + $prefix = "$realfile:$realline: " + } elsif ($emacs) { + if ($file) { + $prefix = "$filename:$realline: "; + } else { + $prefix = "$filename:$linenr: "; + } + } + + if ($found_file) { + if (is_maintained_obsolete($realfile)) { + WARN("OBSOLETE", + "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); + } + if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { + $check = 1; + } else { + $check = $check_orig; + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +# Check if the commit log has what seems like a diff which can confuse patch + if ($in_commit_log && !$commit_log_has_diff && + (($line =~ m@^\s+diff\b.*a/[\w/]+@ && + $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || + $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || + $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { + ERROR("DIFF_IN_COMMIT_MSG", + "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); + $commit_log_has_diff = 1; + } + +# Check for incorrect file permissions + if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { + my $permhere = $here . "FILE: $realfile\n"; + if ($realfile !~ m@scripts/@ && + $realfile !~ /\.(py|pl|awk|sh)$/) { + ERROR("EXECUTE_PERMISSIONS", + "do not set execute permissions for source files\n" . $permhere); + } + } + +# Check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + $signoff++; + $in_commit_log = 0; + } + +# Check if CODEOWNERS is being updated. If so, there's probably no need to +# emit the "does CODEOWNERS need updating?" message on file add/move/delete + if ($line =~ /^\s*CODEOWNERS\s*\|/) { + $reported_maintainer_file = 1; + } + +# Check signature styles + if (!$in_header_lines && + $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { + my $space_before = $1; + my $sign_off = $2; + my $space_after = $3; + my $email = $4; + my $ucfirst_sign_off = ucfirst(lc($sign_off)); + + if ($sign_off !~ /$signature_tags/) { + WARN("BAD_SIGN_OFF", + "Non-standard signature: $sign_off\n" . $herecurr); + } + if (defined $space_before && $space_before ne "") { + if (WARN("BAD_SIGN_OFF", + "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { + if (WARN("BAD_SIGN_OFF", + "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + + } + if (!defined $space_after || $space_after ne " ") { + if (WARN("BAD_SIGN_OFF", + "Use a single space after $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = + "$ucfirst_sign_off $email"; + } + } + + my ($email_name, $email_address, $comment) = parse_email($email); + my $suggested_email = format_email(($email_name, $email_address)); + if ($suggested_email eq "") { + ERROR("BAD_SIGN_OFF", + "Unrecognized email address: '$email'\n" . $herecurr); + } else { + my $dequoted = $suggested_email; + $dequoted =~ s/^"//; + $dequoted =~ s/" $comment" ne $email && + "$suggested_email$comment" ne $email) { + WARN("BAD_SIGN_OFF", + "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); + } + } + +# Check for duplicate signatures + my $sig_nospace = $line; + $sig_nospace =~ s/\s//g; + $sig_nospace = lc($sig_nospace); + if (defined $signatures{$sig_nospace}) { + WARN("BAD_SIGN_OFF", + "Duplicate signature\n" . $herecurr); + } else { + $signatures{$sig_nospace} = 1; + } + } + +# Check email subject for common tools that don't need to be mentioned + if ($in_header_lines && + $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { + WARN("EMAIL_SUBJECT", + "A patch subject line should describe the change not the tool that found it\n" . $herecurr); + } + +# Check for old stable address + if ($line =~ /^\s*cc:\s*.*?.*$/i) { + ERROR("STABLE_ADDRESS", + "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); + } + +# Check for unwanted Gerrit info + if ($in_commit_log && $line =~ /^\s*change-id:/i) { + ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); + } + +# Check if the commit log is in a possible stack dump + if ($in_commit_log && !$commit_log_possible_stack_dump && + ($line =~ /^\s*(?:WARNING:|BUG:)/ || + $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || + # timestamp + $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { + # stack dump address + $commit_log_possible_stack_dump = 1; + } + +# Check for line lengths > 75 in commit log, warn once + if ($in_commit_log && !$commit_log_long_line && + length($line) > 75 && + !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || + # file delta changes + $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || + # filename then : + $line =~ /^\s*(?:Fixes:|Link:)/i || + # A Fixes: or Link: line + $commit_log_possible_stack_dump)) { + WARN("COMMIT_LOG_LONG_LINE", + "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); + $commit_log_long_line = 1; + } + +# Reset possible stack dump if a blank line is found + if ($in_commit_log && $commit_log_possible_stack_dump && + $line =~ /^\s*$/) { + $commit_log_possible_stack_dump = 0; + } + +# Check for git id commit length and improperly formed commit descriptions + if ($in_commit_log && !$commit_log_possible_stack_dump && + $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i && + $line !~ /^This reverts commit [0-9a-f]{7,40}/ && + ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && + $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && + $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { + my $init_char = "c"; + my $orig_commit = ""; + my $short = 1; + my $long = 0; + my $case = 1; + my $space = 1; + my $hasdesc = 0; + my $hasparens = 0; + my $id = '0123456789ab'; + my $orig_desc = "commit description"; + my $description = ""; + + if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { + $init_char = $1; + $orig_commit = lc($2); + } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { + $orig_commit = lc($1); + } + + $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); + $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); + $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); + $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); + if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { + $orig_desc = $1; + $hasparens = 1; + } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && + defined $rawlines[$linenr] && + $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { + $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; + $orig_desc = $1; + $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; + $orig_desc .= " " . $1; + $hasparens = 1; + } + + ($id, $description) = git_commit_info($orig_commit, + $id, $orig_desc); + + if (defined($id) && + ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { + ERROR("GIT_COMMIT_ID", + "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); + } + } + +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $is_patch = 1; + $reported_maintainer_file = 1; + WARN("FILE_PATH_CHANGES", + "added, moved or deleted file(s), does CODEOWNERS need updating?\n" . $herecurr); + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("CORRUPTED_PATCH", + "patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + CHK("INVALID_UTF8", + "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# Check if it's the start of a commit log +# (not a header line and we haven't seen the patch filename) + if ($in_header_lines && $realfile =~ /^$/ && + !($rawline =~ /^\s+(?:\S|$)/ || + $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { + $in_header_lines = 0; + $in_commit_log = 1; + $has_commit_log = 1; + } + +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && + $rawline =~ /$NON_ASCII_UTF8/) { + WARN("UTF8_BEFORE_PATCH", + "8-bit UTF-8 used in possible commit log\n" . $herecurr); + } + +# Check for absolute kernel paths in commit message + if ($tree && $in_commit_log) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# Check for various typo / spelling mistakes + if (defined($misspellings) && + ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { + while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { + my $typo = $1; + my $typo_fix = $spelling_fix{lc($typo)}; + $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); + $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + if (&{$msg_level}("TYPO_SPELLING", + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; + } + } + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("DOS_LINE_ENDINGS", + "DOS line endings\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/[\s\015]+$//; + } + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (ERROR("TRAILING_WHITESPACE", + "trailing whitespace\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + + $rpt_cleaners = 1; + } + +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b675\s+Mass\s+Ave/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_level = \&ERROR; + $msg_level = \&CHK if ($file); + &{$msg_level}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + +# check for Kconfig help text having a real description +# Only applies when adding the entry originally, after that we do not have +# sufficient context to determine whether it is indeed long enough. + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*config\s+/) { + my $length = 0; + my $cnt = $realcnt; + my $ln = $linenr + 1; + my $f; + my $is_start = 0; + my $is_end = 0; + for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { + $f = $lines[$ln - 1]; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $is_end = $lines[$ln - 1] =~ /^\+/; + + next if ($f =~ /^-/); + last if (!$file && $f =~ /^\@\@/); + + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + $is_start = 1; + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { + $length = -1; + } + + $f =~ s/^.//; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + if ($f =~ /^\s*config\s/) { + $is_end = 1; + last; + } + $length++; + } + if ($is_start && $is_end && $length < $min_conf_desc_length) { + WARN("CONFIG_DESCRIPTION", + "please write a paragraph that describes the config symbol fully\n" . $herecurr); + } + #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; + } + +# check for MAINTAINERS entries that don't have the right form + if ($realfile =~ /^MAINTAINERS$/ && + $rawline =~ /^\+[A-Z]:/ && + $rawline !~ /^\+[A-Z]:\t\S/) { + if (WARN("MAINTAINERS_STYLE", + "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; + } + } + +# discourage the use of boolean for type definition attributes of Kconfig options + if ($realfile =~ /Kconfig/ && + $line =~ /^\+\s*\bboolean\b/) { + WARN("CONFIG_TYPE_BOOLEAN", + "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); + } + + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && + ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { + my $flag = $1; + my $replacement = { + 'EXTRA_AFLAGS' => 'asflags-y', + 'EXTRA_CFLAGS' => 'ccflags-y', + 'EXTRA_CPPFLAGS' => 'cppflags-y', + 'EXTRA_LDFLAGS' => 'ldflags-y', + }; + + WARN("DEPRECATED_VARIABLE", + "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); + } +# Kconfig use tabs and no spaces in line + if ($realfile =~ /Kconfig/ && $rawline =~ /^\+ /) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet); + } + +# check for DT compatible documentation + if (defined $root && + (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || + ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { + + my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; + + my $dt_path = $root . "/dts/bindings/"; + my $vp_file = $dt_path . "vendor-prefixes.txt"; + + foreach my $compat (@compats) { + my $compat2 = $compat; + $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; + my $compat3 = $compat; + $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; + `grep -Erq "$compat|$compat2|$compat3" $dt_path`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); + } + + next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; + my $vendor = $1; + `grep -Eq "^$vendor\\b" $vp_file`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); + } + } + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); + +# line length limit (with some exclusions) +# +# There are a few types of lines that may extend beyond $max_line_length: +# logging functions like pr_info that end in a string +# lines with a single string +# #defines that are a single string +# +# There are 3 different line length message types: +# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length +# LONG_LINE_STRING a string starts before but extends beyond $max_line_length +# LONG_LINE all other lines longer than $max_line_length +# +# if LONG_LINE is ignored, the other 2 types are also ignored +# + + if ($line =~ /^\+/ && $length > $max_line_length) { + my $msg_type = "LONG_LINE"; + + # Check the allowed long line types first + + # logging functions that end in a string that starts + # before $max_line_length + if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = ""; + + # lines with only strings (w/ possible termination) + # #defines with only strings + } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || + $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { + $msg_type = ""; + + # EFI_GUID is another special case + } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/) { + $msg_type = ""; + + # Otherwise set the alternate message types + + # a comment starts before $max_line_length + } elsif ($line =~ /($;[\s$;]*)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_COMMENT" + + # a quoted string starts before $max_line_length + } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && + length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { + $msg_type = "LONG_LINE_STRING" + } + + if ($msg_type ne "" && + (show_type("LONG_LINE") || show_type($msg_type))) { + WARN($msg_type, + "line over $max_line_length characters\n" . $herecurr); + } + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("MISSING_EOF_NEWLINE", + "adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("LO_MACRO", + "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("HI_MACRO", + "use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); + +# at the beginning of a line any tabs must come first and anything +# more than 8 must use tabs. + if ($rawline =~ /^\+\s* \t\s*\S/ || + $rawline =~ /^\+\s* \s*/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + $rpt_cleaners = 1; + if (ERROR("CODE_INDENT", + "code indent should use tabs where possible\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check for space before tabs. + if ($rawline =~ /^\+/ && $rawline =~ / \t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("SPACE_BEFORE_TAB", + "please, no space before tabs\n" . $herevet) && + $fix) { + while ($fixed[$fixlinenr] =~ + s/(^\+.*) {8,8}\t/$1\t\t/) {} + while ($fixed[$fixlinenr] =~ + s/(^\+.*) +\t/$1\t/) {} + } + } + +# check for && or || at the start of a line + if ($rawline =~ /^\+\s*(&&|\|\|)/) { + CHK("LOGICAL_CONTINUATIONS", + "Logical continuations should be on the previous line\n" . $hereprev); + } + +# check indentation starts on a tab stop + if ($^V && $^V ge 5.10.0 && + $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) { + my $indent = length($1); + if ($indent % 8) { + if (WARN("TABSTOP", + "Statements should start on a tabstop\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e; + } + } + } + +# check multi-line statement indentation matches previous line + if ($^V && $^V ge 5.10.0 && + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { + $prevline =~ /^\+(\t*)(.*)$/; + my $oldindent = $1; + my $rest = $2; + + my $pos = pos_last_openparen($rest); + if ($pos >= 0) { + $line =~ /^(\+| )([ \t]*)/; + my $newindent = $2; + + my $goodtabindent = $oldindent . + "\t" x ($pos / 8) . + " " x ($pos % 8); + my $goodspaceindent = $oldindent . " " x $pos; + + if ($newindent ne $goodtabindent && + $newindent ne $goodspaceindent) { + + if (CHK("PARENTHESIS_ALIGNMENT", + "Alignment should match open parenthesis\n" . $hereprev) && + $fix && $line =~ /^\+/) { + $fixed[$fixlinenr] =~ + s/^\+[ \t]*/\+$goodtabindent/; + } + } + } + } + +# check for space after cast like "(int) foo" or "(struct foo) bar" +# avoid checking a few false positives: +# "sizeof(<type>)" or "__alignof__(<type>)" +# function pointer declarations like "(*foo)(int) = bar;" +# structure definitions like "(struct foo) { 0 };" +# multiline macros that define functions +# known attributes or the __attribute__ keyword + if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && + (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { + if (CHK("SPACING", + "No space is necessary after a cast\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/(\(\s*$Type\s*\))[ \t]+/$1/; + } + } + +# Block comment styles +# Networking with an initial /* + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/ && + $realline > 2) { + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); + } + +# Block comments use * on subsequent lines + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $prevrawline =~ /^\+.*?\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("BLOCK_COMMENT_STYLE", + "Block comments use * on subsequent lines\n" . $hereprev); + } + +# Block comments use */ on trailing lines + if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ + WARN("BLOCK_COMMENT_STYLE", + "Block comments use a trailing */ on a separate line\n" . $herecurr); + } + +# Block comment * alignment + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $line =~ /^\+[ \t]*$;/ && #leading comment + $rawline =~ /^\+[ \t]*\*/ && #leading * + (($prevrawline =~ /^\+.*?\/\*/ && #leading /* + $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ + $prevrawline =~ /^\+[ \t]*\*/)) { #leading * + my $oldindent; + $prevrawline =~ m@^\+([ \t]*/?)\*@; + if (defined($1)) { + $oldindent = expand_tabs($1); + } else { + $prevrawline =~ m@^\+(.*/?)\*@; + $oldindent = expand_tabs($1); + } + $rawline =~ m@^\+([ \t]*)\*@; + my $newindent = $1; + $newindent = expand_tabs($newindent); + if (length($oldindent) ne length($newindent)) { + WARN("BLOCK_COMMENT_STYLE", + "Block comments should align the * on each line\n" . $hereprev); + } + } + +# check for missing blank lines after struct/union declarations +# with exceptions for various attributes and macros + if ($prevline =~ /^[\+ ]};?\s*$/ && + $line =~ /^\+/ && + !($line =~ /^\+\s*$/ || + $line =~ /^\+\s*EXPORT_SYMBOL/ || + $line =~ /^\+\s*MODULE_/i || + $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || + $line =~ /^\+[a-z_]*init/ || + $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || + $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*__setup/)) { + if (CHK("LINE_SPACING", + "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for multiple consecutive blank lines + if ($prevline =~ /^[\+ ]\s*$/ && + $line =~ /^\+\s*$/ && + $last_blank_line != ($linenr - 1)) { + if (CHK("LINE_SPACING", + "Please don't use multiple blank lines\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + + $last_blank_line = $linenr; + } + +# check for missing blank lines after declarations + if ($sline =~ /^\+\s+\S/ && #Not at char 1 + # actual declarations + ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $prevline =~ /^\+\s+$declaration_macros/) && + # for "else if" which can look like "$Ident $Ident" + !($prevline =~ /^\+\s+$c90_Keywords\b/ || + # other possible extensions of declaration lines + $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + # not starting a section or a macro "\" extended line + $prevline =~ /(?:\{\s*|\\)$/) && + # looks like a declaration + !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + # function pointer declarations + $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + # foo bar; where foo is some local typedef or #define + $sline =~ /^\+\s+(?:volatile\s+)?$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + # known declaration macros + $sline =~ /^\+\s+$declaration_macros/ || + # start of struct or union or enum + $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + # start or end of block or continuation of declaration + $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || + # bitfield continuation + $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || + # other possible extensions of declaration lines + $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && + # indentation of previous and current line are the same + (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { + if (WARN("LINE_SPACING", + "Missing a blank line after declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } + } + +# check for spaces at the beginning of a line. +# Exceptions: +# 1) within comments +# 2) indented preprocessor commands +# 3) hanging labels + if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + if (WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet) && + $fix) { + $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check if this appears to be the start function declaration, save the name + if ($sline =~ /^\+\{\s*$/ && + $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { + $context_function = $1; + } + +# check if this appears to be the end of function declaration + if ($sline =~ /^\+\}\s*$/) { + undef $context_function; + } + +# check indentation of any line with a bare else +# (but not if it is a multiple line "if (foo) return bar; else return baz;") +# if the previous line is a break or return and is indented 1 tab more... + if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { + my $tabs = length($1) + 1; + if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || + ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && + defined $lines[$linenr] && + $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { + WARN("UNNECESSARY_ELSE", + "else is not generally useful after a break or return\n" . $hereprev); + } + } + +# check indentation of a line with a break; +# if the previous line is a goto or return and is indented the same # of tabs + if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { + my $tabs = $1; + if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { + WARN("UNNECESSARY_BREAK", + "break is not useful after a goto or return\n" . $hereprev); + } + } + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS_KEYWORD", + "CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("CSYNC", + "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("SSYNC", + "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# check for old HOTPLUG __dev<foo> section markings + if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { + WARN("HOTPLUG_SECTION", + "Using $1 is unnecessary\n" . $herecurr); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); +#print "LINE<$line>\n"; + if ($linenr > $suppress_statement && + $realcnt && $sline =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + +#print "linenr<$linenr> <$stat>\n"; + # If this statement has no statement boundaries within + # it there is no point in retrying a statement scan + # until we hit end of it. + my $frag = $stat; $frag =~ s/;+\s*$//; + if ($frag !~ /(?:{|;)/) { +#print "skip<$line_nr_next>\n"; + $suppress_statement = $line_nr_next; + } + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("SWITCH_CASE_INDENT_LEVEL", + "switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + + if ($line =~ /^\+\t{6,}/) { + WARN("DEEP_INDENTATION", + "Too many leading tabs - consider code refactoring\n" . $herecurr); + } + + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("TRAILING_SEMICOLON", + "trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # remove inline comments + $s =~ s/$;/ /g; + $c =~ s/$;/ /g; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + while ($s =~ /\n\s+\\\n/) { + $cond_lines += $s =~ s/\n\s+\\\n/\n/g; + } + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && $s ne '' && + (($sindent % 8) != 0 || + ($sindent < $indent) || + ($sindent == $indent && + ($s !~ /^\s*(?:\}|\{|else\b)/)) || + ($sindent > $indent + 8))) { + WARN("SUSPECT_CODE_INDENT", + "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + next if ($line =~ /^[^\+]/); + +# check for dereferences that span multiple lines + if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && + $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { + $prevline =~ /($Lval\s*(?:\.|->))\s*$/; + my $ref = $1; + $line =~ /^.\s*($Lval)/; + $ref .= $1; + $ref =~ s/\s//g; + WARN("MULTILINE_DEREFERENCE", + "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); + } + +# check for declarations of signed or unsigned without int + while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { + my $type = $1; + my $var = $2; + $var = "" if (!defined $var); + if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { + my $sign = $1; + my $pointer = $2; + + $pointer = "" if (!defined $pointer); + + if (WARN("UNSPECIFIED_INT", + "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && + $fix) { + my $decl = trim($sign) . " int "; + my $comp_pointer = $pointer; + $comp_pointer =~ s/\s//g; + $decl .= $comp_pointer; + $decl = rtrim($decl) if ($var eq ""); + $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; + } + } + } + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST_TYPE", + "TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST_NOT_TYPE", + "TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST_ATTR", + "TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST_NOT_ATTR", + "TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + if (ERROR("OPEN_BRACE", + "that open brace { should be on the previous line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/\s*=\s*$/ = {/; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $line; + $fixedline =~ s/^(.\s*)\{\s*/$1/; + fix_insert_line($fixlinenr, $fixedline); + } + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("MALFORMED_INCLUDE", + "malformed #include filename\n" . $herecurr); + } + if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { + ERROR("UAPI_INCLUDE", + "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + if (ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$fixlinenr]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; + } + } + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + # Handle definitions which produce identifiers with + # a prefix: + # XXX(foo); + # EXPORT_SYMBOL(something_foo); + my $name = $1; + if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && + $name =~ /^${Ident}_$2/) { +#print "FOO C name<$name>\n"; + $suppress_export{$realline_next} = 1; + + } elsif ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL", + "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for global initialisers. + if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { + if (ERROR("GLOBAL_INITIALISERS", + "do not initialise globals to $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; + } + } +# check for static initialisers. + if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { + if (ERROR("INITIALISED_STATIC", + "do not initialise statics to $1\n" . + $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; + } + } + +# check for misordered declarations of char/short/int/long with signed/unsigned + while ($sline =~ m{(\b$TypeMisordered\b)}g) { + my $tmp = trim($1); + WARN("MISORDERED_TYPE", + "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); + } + +# check for static const char * arrays. + if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static const char * array should probably be static const char * const\n" . + $herecurr); + } + +# check for static char foo[] = "bar" declarations. + if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "static char array declaration should probably be static const char\n" . + $herecurr); + } + +# check for const <foo> const where <foo> is not a pointer or array type + if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { + my $found = $1; + if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { + WARN("CONST_CONST", + "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); + } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { + WARN("CONST_CONST", + "'const $found const' should probably be 'const $found'\n" . $herecurr); + } + } + +# check for non-global char *foo[] = {"bar", ...} declarations. + if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "char * array declaration might be better as static const\n" . + $herecurr); + } + +# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) + if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { + my $array = $1; + if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { + my $array_div = $1; + if (WARN("ARRAY_SIZE", + "Prefer ARRAY_SIZE($array)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; + } + } + } + +# check for function declarations without arguments like "int foo()" + if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { + if (ERROR("FUNCTION_WITHOUT_ARGS", + "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; + } + } + +# check for new typedefs, only function parameters and sparse annotations +# make sense. + if ($line =~ /\btypedef\s/ && + $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && + $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && + $line !~ /\b$typeTypedefs\b/ && + $line !~ /\b__bitwise\b/) { + WARN("NEW_TYPEDEFS", + "do not add new typedefs\n" . $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { + #print "AA<$1>\n"; + my ($ident, $from, $to) = ($1, $2, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + +## print "1: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to) { + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { + #print "BB<$1>\n"; + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + +## print "2: from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$fixlinenr] =~ + s@\Q$sub_from\E@$sub_to@; + } + } + } + +# avoid BUG() or BUG_ON() + if ($line =~ /\b(?:BUG|BUG_ON)\b/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}("AVOID_BUG", + "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); + } + +# avoid LINUX_VERSION_CODE + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE", + "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# check for uses of printk_ratelimit + if ($line =~ /\bprintk_ratelimit\s*\(/) { + WARN("PRINTK_RATELIMITED", + "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_ facility level\n" . $herecurr); + } + } + + if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + my $level2 = $level; + $level2 = "dbg" if ($level eq "debug"); + WARN("PREFER_PR_LEVEL", + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); + } + + if ($line =~ /\bpr_warning\s*\(/) { + if (WARN("PREFER_PR_LEVEL", + "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\bpr_warning\b/pr_warn/; + } + } + + if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { + my $orig = $1; + my $level = lc($orig); + $level = "warn" if ($level eq "warning"); + $level = "dbg" if ($level eq "debug"); + WARN("PREFER_DEV_LEVEL", + "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); + } + +# ENOSYS means "bad syscall nr" and nothing else. This will have a small +# number of false positives, but assembly files are not checked, so at +# least the arch entry code will not trigger this warning. + if ($line =~ /\bENOSYS\b/) { + WARN("ENOSYS", + "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and + !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { + if (ERROR("OPEN_BRACE", + "open brace '{' following function declarations go on the next line\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + my $fixed_line = $rawline; + $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; + my $line1 = $1; + my $line2 = $2; + fix_insert_line($fixlinenr, ltrim($line1)); + fix_insert_line($fixlinenr, "\+{"); + if ($line2 !~ /^\s*$/) { + fix_insert_line($fixlinenr, "\+\t" . trim($line2)); + } + } + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + if (ERROR("OPEN_BRACE", + "open brace '{' following $1 go on the same line\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = rtrim($prevrawline) . " {"; + fix_insert_line($fixlinenr, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)\{\s*/$1\t/; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +# missing space after union, struct or enum definition + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } + } + +# Function pointer declarations +# check spacing between type, funcptr, and args +# canonical declaration is "type (*funcptr)(args...)" + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { + my $declare = $1; + my $pre_pointer_space = $2; + my $post_pointer_space = $3; + my $funcname = $4; + my $post_funcname_space = $5; + my $pre_args_space = $6; + +# the $Declare variable will capture all spaces after the type +# so check it for a missing trailing missing space but pointer return types +# don't need a space so don't warn for those. + my $post_declare_space = ""; + if ($declare =~ /(\s+)$/) { + $post_declare_space = $1; + $declare = rtrim($declare); + } + if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { + WARN("SPACING", + "missing space after return type\n" . $herecurr); + $post_declare_space = " "; + } + +# unnecessary space "type (*funcptr)(args...)" +# This test is not currently implemented because these declarations are +# equivalent to +# int foo(int bar, ...) +# and this is form shouldn't/doesn't generate a checkpatch warning. +# +# elsif ($declare =~ /\s{2,}$/) { +# WARN("SPACING", +# "Multiple spaces after return type\n" . $herecurr); +# } + +# unnecessary space "type ( *funcptr)(args...)" + if (defined $pre_pointer_space && + $pre_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer open parenthesis\n" . $herecurr); + } + +# unnecessary space "type (* funcptr)(args...)" + if (defined $post_pointer_space && + $post_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr )(args...)" + if (defined $post_funcname_space && + $post_funcname_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr) (args...)" + if (defined $pre_args_space && + $pre_args_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer arguments\n" . $herecurr); + } + + if (show_type("SPACING") && $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; + } + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /[{,]\s+$/ && + $prefix !~ /:\s+$/) { + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(\+.*?)\s+\[/$1\[/; + } + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b$name\s+\(/$name\(/; + } + } + } + +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?:|\?|: + }x; + my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); + my $off = 0; + + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + + my $blank = copy_spacing($opline); + my $last_after = -1; + + for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + + $off += length($elements[$n]); + + # Pick up the preceding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + + # // is a comment + } elsif ($op eq '//') { + + # : when part of a bitfield + } elsif ($opv eq ':B') { + # skip the bitfield test for now + + # No spaces for: + # -> + } elsif ($op eq '->') { + if ($ctx =~ /Wx.|.xW/) { + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # , must not have a space before and must have a space on the right. + } elsif ($op eq ',') { + my $rtrim_before = 0; + my $space_after = 0; + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $rtrim_before = 1; + } + } + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ && $cc !~ /^\)/) { + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $line_fixed = 1; + $last_after = $n; + $space_after = 1; + } + } + if ($rtrim_before || $space_after) { + if ($rtrim_before) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + } else { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + } + if ($space_after) { + $good .= " "; + } + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + if ($n != $last_after + 2) { + $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + if ($ctx =~ /ExW/) { + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($check) { + if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { + if (CHK("SPACING", + "spaces preferred around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $fix_elements[$n + 2] =~ s/^\s+//; + $line_fixed = 1; + } + } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { + if (CHK("SPACING", + "space preferred before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses <foo@bar> + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # for asm volatile statements + # ignore a colon with another + # colon immediately before or after + if (($op eq ':') && + ($ca =~ /:$/ || $cc =~ /^:/)) { + $ok = 1; + } + + # messages are ERROR, but ?: are CHK + if ($ok == 0) { + my $msg_level = \&ERROR; + $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + + if (&{$msg_level}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } + } + } + $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; + } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { + $fixed[$fixlinenr] = $fixed_line; + } + + + } + +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;\s*$/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$fixlinenr] =~ + s/^(\+.*\S)\s+;/$1;/; + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("MULTIPLE_ASSIGNMENTS", + "multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("MULTIPLE_DECLARATION", +## "declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || + $line =~ /do\{/) { + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/; + } + } + +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + if (ERROR("SPACING", + "space required after that close brace '}'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/}((?!(?:,|;|\)))\S)/} $1/; + } + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\[\s+/\[/; + } + } + if ($line =~ /\s\]/) { + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\]/\]/; + } + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\(\s+/\(/; + } + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\s+\)/\)/; + } + } + +# check unnecessary parentheses around addressof/dereference single $Lvals +# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar + + while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { + my $var = $1; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around $var\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; + } + } + +# check for unnecessary parentheses around function pointer uses +# ie: (foo->bar)(); should be foo->bar(); +# but not "if (foo->bar) (" to avoid some false positives + if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { + my $var = $2; + if (CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around function pointer $var\n" . $herecurr) && + $fix) { + my $var2 = deparenthesize($var); + $var2 =~ s/\s//g; + $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; + } + } + +# check for unnecessary parentheses around comparisons in if uses + if ($^V && $^V ge 5.10.0 && defined($stat) && + $stat =~ /(^.\s*if\s*($balanced_parens))/) { + my $if_stat = $1; + my $test = substr($2, 1, -1); + my $herectx; + while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { + my $match = $1; + # avoid parentheses around potential macro args + next if ($match =~ /^\s*\w+\s*$/); + if (!defined($herectx)) { + $herectx = $here . "\n"; + my $cnt = statement_rawlines($if_stat); + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + last if $rl =~ /^[ \+].*\{/; + } + } + CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around '$match'\n" . $herectx); + } + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.)\s+/$1/; + } + } + +# return is not a function + if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { + my $spacing = $1; + if ($^V && $^V ge 5.10.0 && + $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { + my $value = $1; + $value = deparenthesize($value); + if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { + ERROR("RETURN_PARENTHESES", + "return is not a function, parentheses are not required\n" . $herecurr); + } + } elsif ($spacing !~ /\s+/) { + ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr); + } + } + +# unnecessary return in a void function +# at end-of-function, with the previous line a single leading tab, then return; +# and the line before that not a goto label target like "out:" + if ($sline =~ /^[ \+]}\s*$/ && + $prevline =~ /^\+\treturn\s*;\s*$/ && + $linenr >= 3 && + $lines[$linenr - 3] =~ /^[ +]/ && + $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { + WARN("RETURN_VOID", + "void function return statements are not generally useful\n" . $hereprev); + } + +# if statements using unnecessary parentheses - ie: if ((foo == bar)) + if ($^V && $^V ge 5.10.0 && + $line =~ /\bif\s*((?:\(\s*){2,})/) { + my $openparens = $1; + my $count = $openparens =~ tr@\(@\(@; + my $msg = ""; + if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { + my $comp = $4; #Not $1 because of $LvalOrFunc + $msg = " - maybe == should be = ?" if ($comp eq "=="); + WARN("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses$msg\n" . $herecurr); + } + } + +# comparisons with a constant or upper case identifier on the left +# avoid cases like "foo + BAR < baz" +# only fix matches surrounded by parentheses to avoid incorrect +# conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" + if ($^V && $^V ge 5.10.0 && + $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { + my $lead = $1; + my $const = $2; + my $comp = $3; + my $to = $4; + my $newcomp = $comp; + if ($lead !~ /(?:$Operators|\.)\s*$/ && + $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && + WARN("CONSTANT_COMPARISON", + "Comparisons should place the constant on the right side of the test\n" . $herecurr) && + $fix) { + if ($comp eq "<") { + $newcomp = ">"; + } elsif ($comp eq "<=") { + $newcomp = ">="; + } elsif ($comp eq ">") { + $newcomp = "<"; + } elsif ($comp eq ">=") { + $newcomp = "<="; + } + $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; + } + } + +# Return of what appears to be an errno should normally be negative + if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { + my $name = $1; + if ($name ne 'EOF' && $name ne 'ERROR') { + WARN("USE_NEGATIVE_ERRNO", + "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0) + if (!defined $stat); + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + defined($stat) && defined($cond) && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("ASSIGN_IN_IF", + "do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("HEXADECIMAL_BOOLEAN_TEST", + "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line (or did you mean 'else if'?)\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("TRAILING_STATEMENTS", + "trailing statements should be on next line\n" . $herecurr); + } + + # Check for }<nl>else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && + $previndent == $indent) { + if (ERROR("ELSE_AFTER_BRACE", + "else should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/}\s*$//; + if ($fixedline !~ /^\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $fixedline = $rawline; + $fixedline =~ s/^(.\s*)else/$1} else/; + fix_insert_line($fixlinenr, $fixedline); + } + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + if (ERROR("WHILE_AFTER_BRACE", + "while should follow close brace '}'\n" . $hereprev) && + $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + my $trailing = $rawline; + $trailing =~ s/^\+//; + $trailing = trim($trailing); + $fixedline =~ s/}\s*$/} $trailing/; + fix_insert_line($fixlinenr, $fixedline); + } + } + } + +#Specific variable tests + while ($line =~ m{($Constant|$Lval)}g) { + my $var = $1; + +#gcc binary extension + if ($var =~ /^$Binary$/) { + if (WARN("GCC_BINARY_CONSTANT", + "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && + $fix) { + my $hexval = sprintf("0x%x", oct($var)); + $fixed[$fixlinenr] =~ + s/\b$var\b/$hexval/; + } + } + +#CamelCase + if ($var !~ /^$Constant$/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page<foo> variants + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && +#Ignore some three character SI units explicitly, like MiB and KHz + $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { + while ($var =~ m{($Ident)}g) { + my $word = $1; + next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); + if ($check) { + seed_camelcase_includes(); + if (!$file && !$camelcase_file_seeded) { + seed_camelcase_file($realfile); + $camelcase_file_seeded = 1; + } + } + if (!defined $camelcase{$word}) { + $camelcase{$word} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$word>\n" . $herecurr); + } + } + } + } + +#no spaces allowed after \ in define + if ($line =~ /\#\s*define.*\\\s+$/) { + if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", + "Whitespace after \\ makes next lines useless\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+$//; + } + } + +# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes +# itself <asm/foo.h> (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 !~ /$allowed_asm_includes/) + { + my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; + if ($asminclude > 0) { + if ($realfile =~ m{^arch/}) { + CHK("ARCH_INCLUDE_LINUX", + "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } else { + WARN("INCLUDE_LINUX", + "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + my $has_flow_statement = 0; + my $has_arg_concat = 0; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); + $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); + + $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; + my $define_args = $1; + my $define_stmt = $dstat; + my @def_args = (); + + if (defined $define_args && $define_args ne "") { + $define_args = substr($define_args, 1, length($define_args) - 2); + $define_args =~ s/\s*//g; + @def_args = split(",", $define_args); + } + + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/.\[[^\[\]]*\]/1/) + { + } + + # Flatten any obvious string concatentation. + while ($dstat =~ s/($String)\s*$Ident/$1/ || + $dstat =~ s/$Ident\s*($String)/$1/) + { + } + + # Make asm volatile uses seem like a generic function + $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$| + ^\[ + }x; + #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + + $ctx =~ s/\n*$//; + my $herectx = $here . "\n"; + my $stmt_cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $stmt_cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), + $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz + $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && # .foo = + $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo + $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) + $dstat !~ /^for\s*$Constant$/ && # for (...) + $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() + $dstat !~ /^do\s*{/ && # do {... + $dstat !~ /^\(\{/ && # ({... + $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) + { + if ($dstat =~ /^\s*if\b/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); + } elsif ($dstat =~ /;/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); + } else { + WARN("COMPLEX_MACRO", + "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); + } + + } + + # Make $define_stmt single line, comment-free, etc + my @stmt_array = split('\n', $define_stmt); + my $first = 1; + $define_stmt = ""; + foreach my $l (@stmt_array) { + $l =~ s/\\$//; + if ($first) { + $define_stmt = $l; + $first = 0; + } elsif ($l =~ /^[\+ ]/) { + $define_stmt .= substr($l, 1); + } + } + $define_stmt =~ s/$;//g; + $define_stmt =~ s/\s+/ /g; + $define_stmt = trim($define_stmt); + +# check if any macro arguments are reused (ignore '...' and 'type') + foreach my $arg (@def_args) { + next if ($arg =~ /\.\.\./); + next if ($arg =~ /^type$/i); + my $tmp_stmt = $define_stmt; + $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; + $tmp_stmt =~ s/\#+\s*$arg\b//g; + $tmp_stmt =~ s/\b$arg\s*\#\#//g; + my $use_cnt = $tmp_stmt =~ s/\b$arg\b//g; + if ($use_cnt > 1) { + CHK("MACRO_ARG_REUSE", + "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); + } +# check if any macro arguments may have other precedence issues + if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && + ((defined($1) && $1 ne ',') || + (defined($2) && $2 ne ','))) { + CHK("MACRO_ARG_PRECEDENCE", + "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); + } + } + +# check for macros with flow control, but without ## concatenation +# ## concatenation is commonly a macro that defines a function so ignore those + if ($has_flow_statement && !$has_arg_concat) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($ctx); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("MACRO_WITH_FLOW_CONTROL", + "Macros with flow control statements should be avoided\n" . "$herectx"); + } + +# check for line continuations outside of #defines, preprocessor #, and asm + + } else { + if ($prevline !~ /^..*\\$/ && + $line !~ /^\+\s*\#.*\\$/ && # preprocessor + $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm + $line =~ /^\+.*\\$/) { + WARN("LINE_CONTINUATIONS", + "Avoid unnecessary line continuations\n" . $herecurr); + } + } + +# do {} while (0) macro tests: +# single-statement macros do not need to be enclosed in do while (0) loop, +# macro should not end with a semicolon + if ($^V && $^V ge 5.10.0 && + $realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $realcnt, 0); + $ctx = $dstat; + + $dstat =~ s/\\\n.//g; + $dstat =~ s/$;/ /g; + + if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { + my $stmts = $2; + my $semis = $3; + + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + if (($stmts =~ tr/;/;/) == 1 && + $stmts !~ /^\s*(if|while|for|switch)\b/) { + WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", + "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); + } + if (defined $semis && $semis ne "") { + WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", + "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); + } + } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { + $ctx =~ s/\n*$//; + my $cnt = statement_rawlines($ctx); + my $herectx = $here . "\n"; + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("TRAILING_SEMICOLON", + "macros should not use a trailing semicolon\n" . "$herectx"); + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("MISSING_VMLINUX_SYMBOL", + "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for redundant bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks > 0 && $level == 0) { + my @allowed = (); + my $allow = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + $allowed[$allow] = 0; + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed[$allow] = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed[$allow] = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed[$allow] = 1; + } + $allow++; + } + if ($seen) { + my $sum_allowed = 0; + foreach (@allowed) { + $sum_allowed += $_; + } + if ($sum_allowed == 0) { + WARN("BRACES", + "braces {} are not necessary for any arm of this statement\n" . $herectx); + } elsif ($sum_allowed != $allow && + $seen != $allow) { + CHK("BRACES", + "braces {} should be used on all arms of this statement\n" . $herectx); + } + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("BRACES", + "braces {} are not necessary for single statement blocks\n" . $herectx); + } + } + +# check for single line unbalanced braces + if ($sline =~ /^.\s*\}\s*else\s*$/ || + $sline =~ /^.\s*else\s*\{\s*$/) { + CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); + } + +# check for unnecessary blank lines around braces + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + fix_delete_line($fixlinenr - 1, $prevrawline); + } + } + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (CHK("BRACES", + "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("VOLATILE", + "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); + } + +# Check for user-visible strings broken across lines, which breaks the ability +# to grep for the string. Make exceptions when the previous string ends in a +# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' +# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value + if ($line =~ /^\+\s*$String/ && + $prevline =~ /"\s*$/ && + $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { + if (WARN("SPLIT_STRING", + "quoted string split across lines\n" . $hereprev) && + $fix && + $prevrawline =~ /^\+.*"\s*$/ && + $last_coalesced_string_linenr != $linenr - 1) { + my $extracted_string = get_quoted_string($line, $rawline); + my $comma_close = ""; + if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { + $comma_close = $1; + } + + fix_delete_line($fixlinenr - 1, $prevrawline); + fix_delete_line($fixlinenr, $rawline); + my $fixedline = $prevrawline; + $fixedline =~ s/"\s*$//; + $fixedline .= substr($extracted_string, 1) . trim($comma_close); + fix_insert_line($fixlinenr - 1, $fixedline); + $fixedline = $rawline; + $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; + if ($fixedline !~ /\+\s*$/) { + fix_insert_line($fixlinenr, $fixedline); + } + $last_coalesced_string_linenr = $linenr; + } + } + +# check for missing a space in a string concatenation + if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { + WARN('MISSING_SPACE', + "break quoted strings at a space character\n" . $hereprev); + } + +# check for an embedded function name in a string when the function is known +# This does not work very well for -f --file checking as it depends on patch +# context providing the function name or a single line form for in-file +# function declarations + if ($line =~ /^\+.*$String/ && + defined($context_function) && + get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && + length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { + WARN("EMBEDDED_FUNCTION_NAME", + "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", + "unnecessary whitespace before a quoted newline\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; + } + + } + +# concatenated string without spaces between elements + if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { + CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr); + } + +# uncoalesced string fragments + if ($line =~ /$String\s*"/) { + WARN("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr); + } + +# check for non-standard and hex prefixed decimal printf formats + my $show_L = 1; #don't show the same defect twice + my $show_Z = 1; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + my $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + # check for %L + if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { + WARN("PRINTF_L", + "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); + $show_L = 0; + } + # check for %Z + if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { + WARN("PRINTF_Z", + "%Z$1 is non-standard C, use %z$1\n" . $herecurr); + $show_Z = 0; + } + # check for 0x<decimal> + if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { + ERROR("PRINTF_0XDECIMAL", + "Prefixing 0x with decimal output is defective\n" . $herecurr); + } + } + +# check for line continuations in quoted strings with odd counts of " + if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { + WARN("LINE_CONTINUATIONS", + "Avoid line continuations in quoted strings\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("REDUNDANT_CODE", + "if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless "if (<foo>) fn(<foo>)" uses + if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { + my $tested = quotemeta($1); + my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; + if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { + my $func = $1; + if (WARN('NEEDLESS_IF', + "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && + $fix) { + my $do_fix = 1; + my $leading_tabs = ""; + my $new_leading_tabs = ""; + if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { + $leading_tabs = $1; + } else { + $do_fix = 0; + } + if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { + $new_leading_tabs = $1; + if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { + $do_fix = 0; + } + } else { + $do_fix = 0; + } + if ($do_fix) { + fix_delete_line($fixlinenr - 1, $prevrawline); + $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; + } + } + } + } + +# check for unnecessary "Out of Memory" messages + if ($line =~ /^\+.*\b$logFunctions\s*\(/ && + $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && + (defined $1 || defined $3) && + $linenr > 3) { + my $testval = $2; + my $testline = $lines[$linenr - 3]; + + my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); +# print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); + + if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) { + WARN("OOM_MESSAGE", + "Possible unnecessary 'out of memory' message\n" . $hereprev); + } + } + +# check for logging functions with KERN_<LEVEL> + if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && + $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { + my $level = $1; + if (WARN("UNNECESSARY_KERN_LEVEL", + "Possible unnecessary $level\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s*$level\s*//; + } + } + +# check for logging continuations + if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { + WARN("LOGGING_CONTINUATION", + "Avoid logging continuation uses where feasible\n" . $herecurr); + } + +# check for mask then right shift without a parentheses + if ($^V && $^V ge 5.10.0 && + $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && + $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so + WARN("MASK_THEN_SHIFT", + "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); + } + +# check for pointer comparisons to NULL + if ($^V && $^V ge 5.10.0) { + while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { + my $val = $1; + my $equal = "!"; + $equal = "" if ($4 eq "!="); + if (CHK("COMPARISON_TO_NULL", + "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; + } + } + } + +# check for bad placement of section $InitAttribute (e.g.: __initdata) + if ($line =~ /(\b$InitAttribute\b)/) { + my $attr = $1; + if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { + my $ptr = $1; + my $var = $2; + if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && + ERROR("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr)) || + ($ptr !~ /\b(union|struct)\s+$attr\b/ && + WARN("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr))) && + $fix) { + $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; + } + } + } + +# check for $InitAttributeData (ie: __initdata) with const + if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { + my $attr = $1; + $attr =~ /($InitAttributePrefix)(.*)/; + my $attr_prefix = $1; + my $attr_type = $2; + if (ERROR("INIT_ATTRIBUTE", + "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/$InitAttributeData/${attr_prefix}initconst/; + } + } + +# check for $InitAttributeConst (ie: __initconst) without const + if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { + my $attr = $1; + if (ERROR("INIT_ATTRIBUTE", + "Use of $attr requires a separate use of const\n" . $herecurr) && + $fix) { + my $lead = $fixed[$fixlinenr] =~ + /(^\+\s*(?:static\s+))/; + $lead = rtrim($1); + $lead = "$lead " if ($lead !~ /^\+$/); + $lead = "${lead}const "; + $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; + } + } + +# check for __read_mostly with const non-pointer (should just be const) + if ($line =~ /\b__read_mostly\b/ && + $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { + if (ERROR("CONST_READ_MOSTLY", + "Invalid use of __read_mostly with const type\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; + } + } + +# don't use __constant_<foo> functions outside of include/uapi/ + if ($realfile !~ m@^include/uapi/@ && + $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { + my $constant_func = $1; + my $func = $constant_func; + $func =~ s/^__constant_//; + if (WARN("CONSTANT_CONVERSION", + "$constant_func should be $func\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; + } + } + +# prefer usleep_range over udelay + if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + my $delay = $1; + # ignore udelay's < 10, however + if (! ($delay < 10) ) { + CHK("USLEEP_RANGE", + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + if ($delay > 2000) { + WARN("LONG_UDELAY", + "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); + } + } + +# warn about unexpectedly long msleep's + if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if ($1 < 20) { + WARN("MSLEEP", + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + } + +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("UNCOMMENTED_DEFINITION", + "$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + + my $barriers = qr{ + mb| + rmb| + wmb| + read_barrier_depends + }x; + my $barrier_stems = qr{ + mb__before_atomic| + mb__after_atomic| + store_release| + load_acquire| + store_mb| + (?:$barriers) + }x; + my $all_barriers = qr{ + (?:$barriers)| + smp_(?:$barrier_stems)| + virt_(?:$barrier_stems) + }x; + + if ($line =~ /\b(?:$all_barriers)\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); + } + } + + my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; + + if ($realfile !~ m@^include/asm-generic/@ && + $realfile !~ m@/barrier\.h$@ && + $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && + $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { + WARN("MEMORY_BARRIER", + "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); + } + +# check for waitqueue_active without a comment. + if ($line =~ /\bwaitqueue_active\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("WAITQUEUE_ACTIVE", + "waitqueue_active without comment\n" . $herecurr); + } + } + +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("ARCH_DEFINES", + "architecture specific defines should be avoided\n" . $herecurr); + } + +# check that the storage class is not after a type + if ($line =~ /\b($Type)\s+($Storage)\b/) { + WARN("STORAGE_CLASS", + "storage class '$2' should be located before type '$1'\n" . $herecurr); + } +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && + $line !~ /^.\s*$Storage/ && + $line =~ /^.\s*(.+?)\$Storage\s/ && + $1 !~ /[\,\)]\s*$/) { + WARN("STORAGE_CLASS", + "storage class should be at the beginning of the declaration\n" . $herecurr); + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("INLINE_LOCATION", + "inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b(__inline__|__inline)\b/) { + if (WARN("INLINE", + "plain inline is preferred over $1\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; + + } + } + +# Check for __attribute__ packed, prefer __packed + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { + WARN("PREFER_PACKED", + "__packed is preferred over __attribute__((packed))\n" . $herecurr); + } + +# Check for __attribute__ aligned, prefer __aligned + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { + WARN("PREFER_ALIGNED", + "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); + } + +# Check for __attribute__ format(printf, prefer __printf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { + if (WARN("PREFER_PRINTF", + "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; + + } + } + +# Check for __attribute__ format(scanf, prefer __scanf + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + if (WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; + } + } + +# Check for __attribute__ weak, or __weak declarations (may have link issues) + if ($^V && $^V ge 5.10.0 && + $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && + ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || + $line =~ /\b__weak\b/)) { + ERROR("WEAK_DECLARATION", + "Using weak declarations can have unintended link defects\n" . $herecurr); + } + +# check for c99 types like uint8_t + if ($line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { + my $type = $1; + if ($type =~ /\b($typeC99Typedefs)\b/) { + $type = $1; + my $kernel_type = 'u'; + $kernel_type = 's' if ($type =~ /^_*[si]/); + $type =~ /(\d+)/; + $kernel_type .= $1.'_t'; + WARN("PREFER_KERNEL_TYPES", + "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) + } + } + +# check for cast of C90 native int or longer types constants + if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { + my $cast = $1; + my $const = $2; + if (WARN("TYPECAST_INT_CONSTANT", + "Unnecessary typecast of c90 int constant\n" . $herecurr) && + $fix) { + my $suffix = ""; + my $newconst = $const; + $newconst =~ s/${Int_type}$//; + $suffix .= 'U' if ($cast =~ /\bunsigned\b/); + if ($cast =~ /\blong\s+long\b/) { + $suffix .= 'LL'; + } elsif ($cast =~ /\blong\b/) { + $suffix .= 'L'; + } + $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; + } + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("SIZEOF_ADDRESS", + "sizeof(& should be avoided\n" . $herecurr); + } + +# check for sizeof without parenthesis + if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { + if (WARN("SIZEOF_PARENTHESIS", + "sizeof $1 should be sizeof($1)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; + } + } + +# check for struct spinlock declarations + if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { + WARN("USE_SPINLOCK_T", + "struct spinlock should be spinlock_t\n" . $herecurr); + } + +# check for seq_printf uses that could be seq_puts + if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { + my $fmt = get_quoted_string($line, $rawline); + $fmt =~ s/%%//g; + if ($fmt !~ /%/) { + if (WARN("PREFER_SEQ_PUTS", + "Prefer seq_puts to seq_printf\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; + } + } + } + + # check for vsprintf extension %p<foo> misuses + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && + $1 !~ /^_*volatile_*$/) { + my $bad_extension = ""; + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + for (my $count = $linenr; $count <= $lc; $count++) { + my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); + $fmt =~ s/%%//g; + if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNO]).)/) { + $bad_extension = $1; + last; + } + } + if ($bad_extension ne "") { + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + WARN("VSPRINTF_POINTER_EXTENSION", + "Invalid vsprintf pointer extension '$bad_extension'\n" . "$here\n$stat_real\n"); + } + } + +# Check for misused memsets + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { + + my $ms_addr = $2; + my $ms_val = $7; + my $ms_size = $12; + + if ($ms_size =~ /^(0x|)0$/i) { + ERROR("MEMSET", + "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); + } elsif ($ms_size =~ /^(0x|)1$/i) { + WARN("MEMSET", + "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); + } + } + +# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) +# if ($^V && $^V ge 5.10.0 && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# if (WARN("PREFER_ETHER_ADDR_COPY", +# "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; +# } +# } + +# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) +# if ($^V && $^V ge 5.10.0 && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# WARN("PREFER_ETHER_ADDR_EQUAL", +# "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") +# } + +# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr +# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr +# if ($^V && $^V ge 5.10.0 && +# defined $stat && +# $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { +# +# my $ms_val = $7; +# +# if ($ms_val =~ /^(?:0x|)0+$/i) { +# if (WARN("PREFER_ETH_ZERO_ADDR", +# "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; +# } +# } elsif ($ms_val =~ /^(?:0xff|255)$/i) { +# if (WARN("PREFER_ETH_BROADCAST_ADDR", +# "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && +# $fix) { +# $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; +# } +# } +# } + +# typecasts on min/max could be min_t/max_t + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { + if (defined $2 || defined $7) { + my $call = $1; + my $cast1 = deparenthesize($2); + my $arg1 = $3; + my $cast2 = deparenthesize($7); + my $arg2 = $8; + my $cast; + + if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { + $cast = "$cast1 or $cast2"; + } elsif ($cast1 ne "") { + $cast = $cast1; + } else { + $cast = $cast2; + } + WARN("MINMAX", + "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); + } + } + +# check usleep_range arguments + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { + my $min = $1; + my $max = $7; + if ($min eq $max) { + WARN("USLEEP_RANGE", + "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && + $min > $max) { + WARN("USLEEP_RANGE", + "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + } + } + +# check for naked sscanf + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/ && + ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && + $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && + $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + WARN("NAKED_SSCANF", + "unchecked sscanf return value\n" . "$here\n$stat_real\n"); + } + +# check for simple sscanf that should be kstrto<foo> + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /\bsscanf\b/) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { + my $format = $6; + my $count = $format =~ tr@%@%@; + if ($count == 1 && + $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { + WARN("SSCANF_TO_KSTRTO", + "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); + } + } + } + +# check for new externs in .h files. + if ($realfile =~ /\.h$/ && + $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { + if (CHK("AVOID_EXTERNS", + "extern prototypes should be avoided in .h files\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; + } + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("FUNCTION_ARGUMENTS", + "arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("AVOID_EXTERNS", + "externs should be avoided in .c files\n" . $herecurr); + } + +# check for function declarations that have arguments without identifier names + if (defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s*$Ident\s*\(\s*([^{]+)\s*\)\s*;/s && + $1 ne "void") { + my $args = trim($1); + while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { + my $arg = trim($1); + if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) { + WARN("FUNCTION_ARGUMENTS", + "function definition argument '$arg' should also have an identifier name\n" . $herecurr); + } + } + } + +# check for function definitions + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { + $context_function = $1; + +# check for multiline function definition with misplaced open brace + my $ok = 0; + my $cnt = statement_rawlines($stat); + my $herectx = $here . "\n"; + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + $ok = 1 if ($rl =~ /^[ \+]\{/); + $ok = 1 if ($rl =~ /\{/ && $n == 0); + last if $rl =~ /^[ \+].*\{/; + } + if (!$ok) { + ERROR("OPEN_BRACE", + "open brace '{' following function definitions go on the next line\n" . $herectx); + } + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("UNDOCUMENTED_SETUP", + "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { + WARN("UNNECESSARY_CASTS", + "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + +# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { + my $oldfunc = $3; + my $a1 = $4; + my $a2 = $10; + my $newfunc = "kmalloc_array"; + $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); + my $r1 = $a1; + my $r2 = $a2; + if ($a1 =~ /^sizeof\s*\S/) { + $r1 = $a2; + $r2 = $a1; + } + if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && + !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { + my $ctx = ''; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($stat); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + if (WARN("ALLOC_WITH_MULTIPLY", + "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && + $cnt == 1 && + $fix) { + $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; + } + } + } + +# check for krealloc arg reuse + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + WARN("KREALLOC_ARG_REUSE", + "Reusing the krealloc arg is almost always a bug\n" . $herecurr); + } + +# check for alloc argument mismatch + if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { + WARN("ALLOC_ARRAY_ARGS", + "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); + } + +# check for multiple semicolons + if ($line =~ /;\s*;\s*$/) { + if (WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; + } + } + +# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi + if ($realfile !~ m@^include/uapi/@ && + $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { + my $ull = ""; + $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); + if (CHK("BIT_MACRO", + "Prefer using the BIT$ull macro\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; + } + } + +# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE + if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { + my $config = $1; + if (WARN("PREFER_IS_ENABLED", + "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; + } + } + +# check for case / default statements not preceded by break/fallthrough/switch + if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { + my $has_break = 0; + my $has_statement = 0; + my $count = 0; + my $prevline = $linenr; + while ($prevline > 1 && ($file || $count < 3) && !$has_break) { + $prevline--; + my $rline = $rawlines[$prevline - 1]; + my $fline = $lines[$prevline - 1]; + last if ($fline =~ /^\@\@/); + next if ($fline =~ /^\-/); + next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); + $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); + next if ($fline =~ /^.[\s$;]*$/); + $has_statement = 1; + $count++; + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + } + if (!$has_break && $has_statement) { + WARN("MISSING_BREAK", + "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr); + } + } + +# check for switch/default statements without a break; + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { + my $ctx = ''; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($stat); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("DEFAULT_NO_BREAK", + "switch default: should use break\n" . $herectx); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /\b__FUNCTION__\b/) { + if (WARN("USE_FUNC", + "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; + } + } + +# check for uses of __DATE__, __TIME__, __TIMESTAMP__ + while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { + ERROR("DATE_TIME", + "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); + } + +# check for use of yield() + if ($line =~ /\byield\s*\(\s*\)/) { + WARN("YIELD", + "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); + } + +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + +# check for semaphores initialized locked + if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { + WARN("CONSIDER_COMPLETION", + "consider using a completion\n" . $herecurr); + } + +# recommend kstrto* over simple_strto* and strict_strto* + if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { + WARN("CONSIDER_KSTRTO", + "$1 is obsolete, use k$3 instead\n" . $herecurr); + } + +# check for __initcall(), use device_initcall() explicitly or more appropriate function please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("USE_DEVICE_INITCALL", + "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); + } + +# check for various structs that are normally const (ops, kgdb, device_tree) +# and avoid what seem like struct definitions 'struct foo {' + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { + WARN("CONST_STRUCT", + "struct $1 should normally be const\n" . $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("NR_CPUS", + "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. + if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { + ERROR("DEFINE_ARCH_HAS", + "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); + } + +# likely/unlikely comparisons similar to "(likely(foo) > 0)" + if ($^V && $^V ge 5.10.0 && + $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { + WARN("LIKELY_MISUSE", + "Using $1 should generally have parentheses around the comparison\n" . $herecurr); + } + +# whine mightly about in_atomic + if ($line =~ /\bin_atomic\s*\(/) { + if ($realfile =~ m@^drivers/@) { + ERROR("IN_ATOMIC", + "do not use in_atomic in drivers\n" . $herecurr); + } elsif ($realfile !~ m@^kernel/@) { + WARN("IN_ATOMIC", + "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); + } + } + +# whine about ACCESS_ONCE + if ($^V && $^V ge 5.10.0 && + $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) { + my $par = $1; + my $eq = $2; + my $fun = $3; + $par =~ s/^\(\s*(.*)\s*\)$/$1/; + if (defined($eq)) { + if (WARN("PREFER_WRITE_ONCE", + "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/; + } + } else { + if (WARN("PREFER_READ_ONCE", + "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/; + } + } + } + +# check for mutex_trylock_recursive usage + if ($line =~ /mutex_trylock_recursive/) { + ERROR("LOCKING", + "recursive locking is bad, do not use this ever.\n" . $herecurr); + } + +# check for lockdep_set_novalidate_class + if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || + $line =~ /__lockdep_no_validate__\s*\)/ ) { + if ($realfile !~ m@^kernel/lockdep@ && + $realfile !~ m@^include/linux/lockdep@ && + $realfile !~ m@^drivers/base/core@) { + ERROR("LOCKDEP", + "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); + } + } + + if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || + $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { + WARN("EXPORTED_WORLD_WRITABLE", + "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); + } + +# Mode permission misuses where it seems decimal should be octal +# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop + if ($^V && $^V ge 5.10.0 && + defined $stat && + $line =~ /$mode_perms_search/) { + foreach my $entry (@mode_permission_funcs) { + my $func = $entry->[0]; + my $arg_pos = $entry->[1]; + + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + + my $skip_args = ""; + if ($arg_pos > 1) { + $arg_pos--; + $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; + } + my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; + if ($stat =~ /$test/) { + my $val = $1; + $val = $6 if ($skip_args ne ""); + if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + ($val =~ /^$Octal$/ && length($val) ne 4)) { + ERROR("NON_OCTAL_PERMISSIONS", + "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); + } + if ($val =~ /^$Octal$/ && (oct($val) & 02)) { + ERROR("EXPORTED_WORLD_WRITABLE", + "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); + } + } + } + } + +# check for uses of S_<PERMS> that could be octal for readability + if ($line =~ /\b$mode_perms_string_search\b/) { + my $val = ""; + my $oval = ""; + my $to = 0; + my $curpos = 0; + my $lastpos = 0; + while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { + $curpos = pos($line); + my $match = $2; + my $omatch = $1; + last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); + $lastpos = $curpos; + $to |= $mode_permission_string_types{$match}; + $val .= '\s*\|\s*' if ($val ne ""); + $val .= $match; + $oval .= $omatch; + } + $oval =~ s/^\s*\|\s*//; + $oval =~ s/\s*\|\s*$//; + my $octal = sprintf("%04o", $to); + if (WARN("SYMBOLIC_PERMS", + "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/$val/$octal/; + } + } + +# validate content of MODULE_LICENSE against list from include/linux/module.h + if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { + my $extracted_string = get_quoted_string($line, $rawline); + my $valid_licenses = qr{ + GPL| + GPL\ v2| + GPL\ and\ additional\ rights| + Dual\ BSD/GPL| + Dual\ MIT/GPL| + Dual\ MPL/GPL| + Proprietary + }x; + if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { + WARN("MODULE_LICENSE", + "unknown module license " . $extracted_string . "\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch && $file !~ /cover-letter\.patch$/) { + ERROR("NOT_UNIFIED_DIFF", + "Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + } + + if ($quiet == 0) { + # If there were any defects found and not already fixing them + if (!$clean and !$fix) { + print << "EOM" + +NOTE: For some of the reported defects, checkpatch may be able to + mechanically convert to the typical style using --fix or --fix-inplace. +EOM + } + # If there were whitespace errors which cleanpatch can fix + # then suggest that. + if ($rpt_cleaners) { + $rpt_cleaners = 0; + print << "EOM" + +NOTE: Whitespace errors detected. + You may wish to use scripts/cleanpatch or scripts/cleanfile +EOM + } + } + + if ($clean == 0 && $fix && + ("@rawlines" ne "@fixed" || + $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { + my $newfile = $filename; + $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); + my $linecount = 0; + my $f; + + @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); + + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line . "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; + +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... +EOM + } + } + + if ($quiet == 0) { + print "\n"; + if ($clean == 1) { + print "$vname has no obvious style problems and is ready for submission.\n"; + } else { + print "$vname has style problems, please review.\n"; + } + } + return $clean; +} diff --git a/scripts/do_checkpatch.sh b/scripts/do_checkpatch.sh new file mode 100755 index 0000000..4bf1381 --- /dev/null +++ b/scripts/do_checkpatch.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# +# Copyright (c) 2019 STMicroelectronics . +# Copyright (c) 2015 Intel Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# + +res=$1 +[ -z "$res" ] && res="checkpatch_log" +[ -f ${res} ] && rm ${res} +[ -f ${res}_error.types ] && rm ${res}_error.types +[ -f ${res}_warning.types ] && rm ${res}_warning.types + +dirs_to_check="lib" +files=$(for d in ${dirs_to_check}; do find $d/ -type f -name '*.[ch]'; done) + +for i in $files; do + ./scripts/checkpatch.pl --no-tree -f --emacs --summary-file --show-types --ignore BRACES,PRINTK_WITHOUT_KERN_LEVEL,SPLIT_STRING,PREFER_KERNEL_TYPES,NEW_TYPEDEFS --max-line-length=90 $i >> ${res} +done +grep ERROR: ${res} |cut -d : -f 3,4 |sort -u > ${res}_error.types +grep WARNING: ${res} |cut -d : -f 3,4 |sort -u > ${res}_warning.types +for i in `cat ${res}_error.types`; do + echo -n $i ' '; grep $i ${res} | wc -l +done +for i in `cat ${res}_warning.types`; do + echo -n $i ' '; grep $i ${res} | wc -l +done diff --git a/scripts/gitlint/commit_rules.py b/scripts/gitlint/commit_rules.py new file mode 100644 index 0000000..915f085 --- /dev/null +++ b/scripts/gitlint/commit_rules.py @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: Apache-2.0 + +""" +The classes below are examples of user-defined CommitRules. Commit rules are gitlint rules that +act on the entire commit at once. Once the rules are discovered, gitlint will automatically take care of applying them +to the entire commit. This happens exactly once per commit. + +A CommitRule contrasts with a LineRule (see examples/my_line_rules.py) in that a commit rule is only applied once on +an entire commit. This allows commit rules to implement more complex checks that span multiple lines and/or checks +that should only be done once per gitlint run. + +While every LineRule can be implemented as a CommitRule, it's usually easier and more concise to go with a LineRule if +that fits your needs. +""" + +from gitlint.rules import CommitRule, RuleViolation, CommitMessageTitle, LineRule, CommitMessageBody +from gitlint.options import IntOption, StrOption +import re + +class BodyMinLineCount(CommitRule): + # A rule MUST have a human friendly name + name = "body-min-line-count" + + # A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule). + id = "UC6" + + # A rule MAY have an option_spec if its behavior should be configurable. + options_spec = [IntOption('min-line-count', 2, "Minimum body line count excluding Signed-off-by")] + + def validate(self, commit): + filtered = [x for x in commit.message.body if not x.lower().startswith("signed-off-by") and x != ''] + line_count = len(filtered) + min_line_count = self.options['min-line-count'].value + if line_count < min_line_count: + message = "Body has no content, should at least have {} line.".format(min_line_count) + return [RuleViolation(self.id, message, line_nr=1)] + +class BodyMaxLineCount(CommitRule): + # A rule MUST have a human friendly name + name = "body-max-line-count" + + # A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule). + id = "UC1" + + # A rule MAY have an option_spec if its behavior should be configurable. + options_spec = [IntOption('max-line-count', 3, "Maximum body line count")] + + def validate(self, commit): + line_count = len(commit.message.body) + max_line_count = self.options['max-line-count'].value + if line_count > max_line_count: + message = "Body contains too many lines ({0} > {1})".format(line_count, max_line_count) + return [RuleViolation(self.id, message, line_nr=1)] + +class SignedOffBy(CommitRule): + """ This rule will enforce that each commit contains a "Signed-off-by" line. + We keep things simple here and just check whether the commit body contains a line that starts with "Signed-off-by". + """ + + # A rule MUST have a human friendly name + name = "body-requires-signed-off-by" + + # A rule MUST have an *unique* id, we recommend starting with UC (for User-defined Commit-rule). + id = "UC2" + + def validate(self, commit): + flags = re.UNICODE + flags |= re.IGNORECASE + for line in commit.message.body: + if line.lower().startswith("signed-off-by"): + if not re.search(r"(^)Signed-off-by: ([-'\w.]+) ([-'\w.]+) (.*)", line, flags=flags): + return [RuleViolation(self.id, "Signed-off-by: must have a full name", line_nr=1)] + else: + return + return [RuleViolation(self.id, "Body does not contain a 'Signed-off-by:' line", line_nr=1)] + +class TitleMaxLengthRevert(LineRule): + name = "title-max-length-no-revert" + id = "UC5" + target = CommitMessageTitle + options_spec = [IntOption('line-length', 72, "Max line length")] + violation_message = "Title exceeds max length ({0}>{1})" + + def validate(self, line, _commit): + max_length = self.options['line-length'].value + if len(line) > max_length and not line.startswith("Revert"): + return [RuleViolation(self.id, self.violation_message.format(len(line), max_length), line)] + +class TitleStartsWithSubsystem(LineRule): + name = "title-starts-with-subsystem" + id = "UC3" + target = CommitMessageTitle + options_spec = [StrOption('regex', ".*", "Regex the title should match")] + + def validate(self, title, _commit): + regex = self.options['regex'].value + pattern = re.compile(regex, re.UNICODE) + violation_message = "Title does not follow [subsystem]: [subject] (and should not start with literal subsys:)" + if not pattern.search(title): + return [RuleViolation(self.id, violation_message, title)] + +class MaxLineLengthExceptions(LineRule): + name = "max-line-length-with-exceptions" + id = "UC4" + target = CommitMessageBody + options_spec = [IntOption('line-length', 80, "Max line length")] + violation_message = "Line exceeds max length ({0}>{1})" + + def validate(self, line, _commit): + max_length = self.options['line-length'].value + urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line) + if line.startswith('Signed-off-by'): + return + + if urls: + return + + if len(line) > max_length: + return [RuleViolation(self.id, self.violation_message.format(len(line), max_length), line)] diff --git a/scripts/spelling.txt b/scripts/spelling.txt new file mode 100644 index 0000000..411bf3c --- /dev/null +++ b/scripts/spelling.txt @@ -0,0 +1,1188 @@ +# Originally from Debian's Lintian tool. Various false positives have been +# removed, and various additions have been made as they've been discovered +# in the kernel source. +# +# License: GPLv2 +# +# The format of each line is: +# mistake||correction +# +abandonning||abandoning +abigious||ambiguous +abitrate||arbitrate +abov||above +abreviated||abbreviated +absense||absence +absolut||absolute +absoulte||absolute +acccess||access +acceess||access +acceleratoin||acceleration +accelleration||acceleration +accesing||accessing +accesnt||accent +accessable||accessible +accesss||access +accidentaly||accidentally +accidentually||accidentally +accoding||according +accomodate||accommodate +accomodates||accommodates +accordign||according +accoring||according +accout||account +accquire||acquire +accquired||acquired +accross||across +acessable||accessible +acess||access +achitecture||architecture +acient||ancient +acitions||actions +acitve||active +acknowldegement||acknowledgment +acknowledgement||acknowledgment +ackowledge||acknowledge +ackowledged||acknowledged +acording||according +activete||activate +actived||activated +actualy||actually +acumulating||accumulating +acumulator||accumulator +adapater||adapter +addional||additional +additionaly||additionally +addres||address +addreses||addresses +addresss||address +aditional||additional +aditionally||additionally +aditionaly||additionally +adminstrative||administrative +adress||address +adresses||addresses +adviced||advised +afecting||affecting +againt||against +agaist||against +albumns||albums +alegorical||allegorical +algined||aligned +algorith||algorithm +algorithmical||algorithmically +algoritm||algorithm +algoritms||algorithms +algorrithm||algorithm +algorritm||algorithm +aligment||alignment +alignement||alignment +allign||align +alligned||aligned +allocatote||allocate +allocatrd||allocated +allocte||allocate +allpication||application +alocate||allocate +alogirhtms||algorithms +alogrithm||algorithm +alot||a lot +alow||allow +alows||allows +altough||although +alue||value +ambigious||ambiguous +amoung||among +amout||amount +an union||a union +an user||a user +an userspace||a userspace +an one||a one +analysator||analyzer +ang||and +anniversery||anniversary +annoucement||announcement +anomolies||anomalies +anomoly||anomaly +anway||anyway +aplication||application +appearence||appearance +applicaion||application +appliction||application +applictions||applications +applys||applies +appplications||applications +appropiate||appropriate +appropriatly||appropriately +approriate||appropriate +approriately||appropriately +apropriate||appropriate +aquainted||acquainted +aquired||acquired +aquisition||acquisition +arbitary||arbitrary +architechture||architecture +arguement||argument +arguements||arguments +aritmetic||arithmetic +arne't||aren't +arraival||arrival +artifical||artificial +artillary||artillery +asign||assign +assertation||assertion +assiged||assigned +assigment||assignment +assigments||assignments +assistent||assistant +assocation||association +associcated||associated +assotiated||associated +assum||assume +assumtpion||assumption +asuming||assuming +asycronous||asynchronous +asynchnous||asynchronous +atomatically||automatically +atomicly||atomically +atempt||attempt +attachement||attachment +attched||attached +attemps||attempts +attruibutes||attributes +authentification||authentication +automaticaly||automatically +automaticly||automatically +automatize||automate +automatized||automated +automatizes||automates +autonymous||autonomous +auxillary||auxiliary +auxilliary||auxiliary +avaiable||available +avaible||available +availabe||available +availabled||available +availablity||availability +availale||available +availavility||availability +availble||available +availiable||available +avalable||available +avaliable||available +aysnc||async +backgroud||background +backword||backward +backwords||backwards +bahavior||behavior +bakup||backup +baloon||balloon +baloons||balloons +bandwith||bandwidth +banlance||balance +batery||battery +beacuse||because +becasue||because +becomming||becoming +becuase||because +beeing||being +befor||before +begining||beginning +beter||better +betweeen||between +bianries||binaries +bitmast||bitmask +boardcast||broadcast +borad||board +boundry||boundary +brievely||briefly +broadcat||broadcast +cacluated||calculated +caculation||calculation +calender||calendar +calle||called +callibration||calibration +calucate||calculate +calulate||calculate +cancelation||cancellation +capabilites||capabilities +capabitilies||capabilities +capatibilities||capabilities +carefuly||carefully +cariage||carriage +catagory||category +cehck||check +challange||challenge +challanges||challenges +chanell||channel +changable||changeable +channle||channel +channnel||channel +charachter||character +charachters||characters +charactor||character +charater||character +charaters||characters +charcter||character +chcek||check +chck||check +checksuming||checksumming +childern||children +childs||children +chiled||child +chked||checked +chnage||change +chnages||changes +chnnel||channel +choosen||chosen +chouse||chose +circumvernt||circumvent +claread||cleared +clared||cleared +closeing||closing +clustred||clustered +collapsable||collapsible +colorfull||colorful +comand||command +comit||commit +commerical||commercial +comming||coming +comminucation||communication +commited||committed +commiting||committing +committ||commit +commoditiy||commodity +comsume||consume +comsumer||consumer +comsuming||consuming +compability||compatibility +compaibility||compatibility +compatability||compatibility +compatable||compatible +compatibiliy||compatibility +compatibilty||compatibility +compatiblity||compatibility +competion||completion +compilant||compliant +compleatly||completely +completition||completion +completly||completely +complient||compliant +componnents||components +compres||compress +compresion||compression +comression||compression +comunication||communication +conbination||combination +conditionaly||conditionally +conected||connected +connecetd||connected +configuartion||configuration +configuratoin||configuration +configuraton||configuration +configuretion||configuration +configutation||configuration +conider||consider +conjuction||conjunction +connectinos||connections +connnection||connection +connnections||connections +consistancy||consistency +consistant||consistent +containes||contains +containts||contains +contaisn||contains +contant||contact +contence||contents +continous||continuous +continously||continuously +continueing||continuing +contraints||constraints +contol||control +contoller||controller +controled||controlled +controler||controller +controll||control +contruction||construction +contry||country +conuntry||country +convertion||conversion +convertor||converter +convienient||convenient +convinient||convenient +corected||corrected +correponding||corresponding +correponds||corresponds +correspoding||corresponding +cotrol||control +couter||counter +coutner||counter +cryptocraphic||cryptographic +cunter||counter +curently||currently +cylic||cyclic +dafault||default +deafult||default +deamon||daemon +decompres||decompress +decription||description +defailt||default +defferred||deferred +definate||definite +definately||definitely +defintion||definition +defintions||definitions +defualt||default +defult||default +deintializing||deinitializing +deintialize||deinitialize +deintialized||deinitialized +deivce||device +delared||declared +delare||declare +delares||declares +delaring||declaring +delemiter||delimiter +dependancies||dependencies +dependancy||dependency +dependant||dependent +depreacted||deprecated +depreacte||deprecate +desactivate||deactivate +desciptor||descriptor +desciptors||descriptors +descripton||description +descrition||description +descritptor||descriptor +desctiptor||descriptor +desriptor||descriptor +desriptors||descriptors +destory||destroy +destoryed||destroyed +destorys||destroys +destroied||destroyed +detabase||database +develope||develop +developement||development +developped||developed +developpement||development +developper||developer +developpment||development +deveolpment||development +devided||divided +deviece||device +diable||disable +dictionnary||dictionary +didnt||didn't +diferent||different +differrence||difference +diffrent||different +diffrentiate||differentiate +difinition||definition +diplay||display +direectly||directly +disassocation||disassociation +disapear||disappear +disapeared||disappeared +disappared||disappeared +disble||disable +disbled||disabled +disconnet||disconnect +discontinous||discontinuous +dispertion||dispersion +dissapears||disappears +distiction||distinction +docuentation||documentation +documantation||documentation +documentaion||documentation +documment||document +doesnt||doesn't +dorp||drop +dosen||doesn +downlad||download +downlads||downloads +druing||during +dynmaic||dynamic +easilly||easily +ecspecially||especially +edditable||editable +editting||editing +efective||effective +efficently||efficiently +ehther||ether +eigth||eight +elementry||elementary +eletronic||electronic +embeded||embedded +enabledi||enabled +enchanced||enhanced +encorporating||incorporating +encrupted||encrypted +encrypiton||encryption +endianess||endianness +enhaced||enhanced +enlightnment||enlightenment +enocded||encoded +enterily||entirely +enviroiment||environment +enviroment||environment +environement||environment +environent||environment +eqivalent||equivalent +equiped||equipped +equivelant||equivalent +equivilant||equivalent +eror||error +estbalishment||establishment +etsablishment||establishment +etsbalishment||establishment +excecutable||executable +exceded||exceeded +excellant||excellent +existance||existence +existant||existent +exixt||exist +exlcude||exclude +exlcusive||exclusive +exmaple||example +expecially||especially +explicite||explicit +explicitely||explicitly +explict||explicit +explictely||explicitly +explictly||explicitly +expresion||expression +exprimental||experimental +extened||extended +extensability||extensibility +extention||extension +extracter||extractor +falied||failed +faild||failed +faill||fail +failied||failed +faillure||failure +failue||failure +failuer||failure +faireness||fairness +falied||failed +faliure||failure +familar||familiar +fatser||faster +feauture||feature +feautures||features +fetaure||feature +fetaures||features +fileystem||filesystem +fimware||firmware +finanize||finalize +findn||find +finilizes||finalizes +finsih||finish +flusing||flushing +folloing||following +followign||following +followings||following +follwing||following +forseeable||foreseeable +forse||force +fortan||fortran +forwardig||forwarding +framming||framing +framwork||framework +frequncy||frequency +frome||from +fucntion||function +fuction||function +fuctions||functions +funcion||function +functionallity||functionality +functionaly||functionally +functionnality||functionality +functonality||functionality +funtion||function +funtions||functions +furthur||further +futhermore||furthermore +futrue||future +gaurenteed||guaranteed +generiously||generously +genereate||generate +genric||generic +globel||global +grabing||grabbing +grahical||graphical +grahpical||graphical +grapic||graphic +guage||gauge +guarenteed||guaranteed +guarentee||guarantee +halfs||halves +hander||handler +handfull||handful +hanled||handled +happend||happened +harware||hardware +heirarchically||hierarchically +helpfull||helpful +hierachy||hierarchy +hierarchie||hierarchy +howver||however +hsould||should +hypervior||hypervisor +hypter||hyper +identidier||identifier +iligal||illegal +illigal||illegal +imblance||imbalance +immeadiately||immediately +immedaite||immediate +immediatelly||immediately +immediatly||immediately +immidiate||immediate +impelentation||implementation +impementated||implemented +implemantation||implementation +implemenation||implementation +implementaiton||implementation +implementated||implemented +implemention||implementation +implemetation||implementation +implemntation||implementation +implentation||implementation +implmentation||implementation +implmenting||implementing +incomming||incoming +incompatabilities||incompatibilities +incompatable||incompatible +inconsistant||inconsistent +increas||increase +incrment||increment +indendation||indentation +indended||intended +independant||independent +independantly||independently +independed||independent +indiate||indicate +inexpect||inexpected +infomation||information +informatiom||information +informations||information +informtion||information +infromation||information +ingore||ignore +inital||initial +initalized||initialized +initalised||initialized +initalise||initialize +initalize||initialize +initation||initiation +initators||initiators +initialiazation||initialization +initializiation||initialization +initialzed||initialized +initilization||initialization +initilize||initialize +inofficial||unofficial +insititute||institute +instal||install +instanciated||instantiated +inteface||interface +integreated||integrated +integrety||integrity +integrey||integrity +intendet||intended +intented||intended +interanl||internal +interchangable||interchangeable +interferring||interfering +interger||integer +intermittant||intermittent +internel||internal +interoprability||interoperability +interrface||interface +interrrupt||interrupt +interrup||interrupt +interrups||interrupts +interruptted||interrupted +interupted||interrupted +interupt||interrupt +intial||initial +intialisation||initialisation +intialised||initialised +intialise||initialise +intialization||initialization +intialized||initialized +intialize||initialize +intregral||integral +intrrupt||interrupt +intterrupt||interrupt +intuative||intuitive +invaid||invalid +invalde||invalid +invalide||invalid +invalud||invalid +invididual||individual +invokation||invocation +invokations||invocations +irrelevent||irrelevant +isnt||isn't +isssue||issue +iternations||iterations +itertation||iteration +itslef||itself +jave||java +jeffies||jiffies +juse||just +jus||just +kown||known +langage||language +langauage||language +langauge||language +langugage||language +lauch||launch +layed||laid +leightweight||lightweight +lengh||length +lenght||length +lenth||length +lesstiff||lesstif +libaries||libraries +libary||library +librairies||libraries +libraris||libraries +licenceing||licencing +loggging||logging +loggin||login +logile||logfile +loosing||losing +losted||lost +machinary||machinery +maintainance||maintenance +maintainence||maintenance +maintan||maintain +makeing||making +malplaced||misplaced +malplace||misplace +managable||manageable +managment||management +mangement||management +manoeuvering||maneuvering +mappping||mapping +mathimatical||mathematical +mathimatic||mathematic +mathimatics||mathematics +maxium||maximum +mechamism||mechanism +meetign||meeting +ment||meant +mergable||mergeable +mesage||message +messags||messages +messgaes||messages +messsage||message +messsages||messages +microprocesspr||microprocessor +milliseonds||milliseconds +minium||minimum +minimam||minimum +minumum||minimum +misalinged||misaligned +miscelleneous||miscellaneous +misformed||malformed +mispelled||misspelled +mispelt||misspelt +mising||missing +miximum||maximum +mmnemonic||mnemonic +mnay||many +modulues||modules +momery||memory +monochorome||monochrome +monochromo||monochrome +monocrome||monochrome +mopdule||module +mroe||more +mulitplied||multiplied +multidimensionnal||multidimensional +multple||multiple +mumber||number +muticast||multicast +mutiple||multiple +mutli||multi +nams||names +navagating||navigating +nead||need +neccecary||necessary +neccesary||necessary +neccessary||necessary +necesary||necessary +neded||needed +negaive||negative +negoitation||negotiation +negotation||negotiation +nerver||never +nescessary||necessary +nessessary||necessary +noticable||noticeable +notications||notifications +notifed||notified +numebr||number +numner||number +obtaion||obtain +occassionally||occasionally +occationally||occasionally +occurance||occurrence +occurances||occurrences +occured||occurred +occurence||occurrence +occure||occurred +occured||occurred +occuring||occurring +offet||offset +omited||omitted +omiting||omitting +omitt||omit +ommiting||omitting +ommitted||omitted +onself||oneself +ony||only +operatione||operation +opertaions||operations +optionnal||optional +optmizations||optimizations +orientatied||orientated +orientied||oriented +orignal||original +otherise||otherwise +ouput||output +oustanding||outstanding +overaall||overall +overhread||overhead +overlaping||overlapping +overide||override +overrided||overridden +overriden||overridden +overun||overrun +overwritting||overwriting +overwriten||overwritten +pacakge||package +pachage||package +packacge||package +packege||package +packge||package +packtes||packets +pakage||package +pallette||palette +paln||plan +paramameters||parameters +paramaters||parameters +paramater||parameter +parametes||parameters +parametised||parametrised +paramter||parameter +paramters||parameters +particuarly||particularly +particularily||particularly +partiton||partition +pased||passed +passin||passing +pathes||paths +pecularities||peculiarities +peformance||performance +peice||piece +pendantic||pedantic +peprocessor||preprocessor +perfoming||performing +permissons||permissions +peroid||period +persistance||persistence +persistant||persistent +platfrom||platform +plattform||platform +pleaes||please +ploting||plotting +plugable||pluggable +poinnter||pointer +pointeur||pointer +poiter||pointer +posible||possible +positon||position +possibilites||possibilities +powerfull||powerful +preceeded||preceded +preceeding||preceding +preceed||precede +precendence||precedence +precission||precision +preemptable||preemptible +prefered||preferred +prefferably||preferably +premption||preemption +prepaired||prepared +pressre||pressure +primative||primitive +princliple||principle +priorty||priority +privilaged||privileged +privilage||privilege +priviledge||privilege +priviledges||privileges +probaly||probably +procceed||proceed +proccesors||processors +procesed||processed +proces||process +procesing||processing +processessing||processing +processess||processes +processpr||processor +processsed||processed +processsing||processing +procteted||protected +prodecure||procedure +progams||programs +progess||progress +programers||programmers +programm||program +programms||programs +progresss||progress +promiscous||promiscuous +promps||prompts +pronnounced||pronounced +prononciation||pronunciation +pronouce||pronounce +pronunce||pronounce +propery||property +propigate||propagate +propigation||propagation +propogate||propagate +prosess||process +protable||portable +protcol||protocol +protecion||protection +protocoll||protocol +promixity||proximity +psudo||pseudo +psuedo||pseudo +psychadelic||psychedelic +pwoer||power +quering||querying +raoming||roaming +reasearcher||researcher +reasearchers||researchers +reasearch||research +recepient||recipient +receving||receiving +recieved||received +recieve||receive +reciever||receiver +recieves||receives +recogniced||recognised +recognizeable||recognizable +recommanded||recommended +recyle||recycle +redircet||redirect +redirectrion||redirection +reename||rename +refcounf||refcount +refence||reference +refered||referred +referenace||reference +refering||referring +refernces||references +refernnce||reference +refrence||reference +registerd||registered +registeresd||registered +registes||registers +registraration||registration +regsiter||register +regster||register +regualar||regular +reguator||regulator +regulamentations||regulations +reigstration||registration +releated||related +relevent||relevant +remoote||remote +remore||remote +removeable||removable +repectively||respectively +replacable||replaceable +replacments||replacements +replys||replies +reponse||response +representaion||representation +reqeust||request +requestied||requested +requiere||require +requirment||requirement +requred||required +requried||required +requst||request +reseting||resetting +resizeable||resizable +resouces||resources +resoures||resources +responce||response +ressizes||resizes +ressource||resource +ressources||resources +retransmited||retransmitted +retreived||retrieved +retreive||retrieve +retrive||retrieve +retuned||returned +reudce||reduce +reuest||request +reuqest||request +reutnred||returned +rmeoved||removed +rmeove||remove +rmeoves||removes +rountine||routine +routins||routines +rpsmg||rpmsg +rpmg||rpmsg +rquest||request +runing||running +runned||ran +runnning||running +runtine||runtime +sacrifying||sacrificing +safly||safely +safty||safety +savable||saveable +scaned||scanned +scaning||scanning +scarch||search +seach||search +searchs||searches +secquence||sequence +secund||second +segement||segment +senarios||scenarios +sentivite||sensitive +separatly||separately +sepcify||specify +sepc||spec +seperated||separated +seperately||separately +seperate||separate +seperatly||separately +seperator||separator +sepperate||separate +sequece||sequence +sequencial||sequential +serveral||several +setts||sets +settting||setting +shotdown||shutdown +shoud||should +shouldnt||shouldn't +shoule||should +shrinked||shrunk +siginificantly||significantly +signabl||signal +similary||similarly +similiar||similar +simlar||similar +simliar||similar +simpified||simplified +singaled||signaled +singal||signal +singed||signed +sleeped||slept +softwares||software +speach||speech +specfic||specific +speciefied||specified +specifc||specific +specifed||specified +specificatin||specification +specificaton||specification +specifing||specifying +specifiying||specifying +speficied||specified +speicify||specify +speling||spelling +spinlcok||spinlock +spinock||spinlock +splitted||split +spreaded||spread +spurrious||spurious +sructure||structure +stablilization||stabilization +staically||statically +staion||station +standardss||standards +standartization||standardization +standart||standard +staticly||statically +stoped||stopped +stoppped||stopped +straming||streaming +struc||struct +structres||structures +stuct||struct +strucuture||structure +stucture||structure +sturcture||structure +subdirectoires||subdirectories +suble||subtle +substract||subtract +succesfully||successfully +succesful||successful +successed||succeeded +successfull||successful +successfuly||successfully +sucessfully||successfully +sucess||success +superflous||superfluous +superseeded||superseded +suplied||supplied +suported||supported +suport||support +supportet||supported +suppored||supported +supportin||supporting +suppoted||supported +suppported||supported +suppport||support +supress||suppress +surpresses||suppresses +susbsystem||subsystem +suspeneded||suspended +suspicously||suspiciously +swaping||swapping +switchs||switches +swith||switch +swithable||switchable +swithc||switch +swithced||switched +swithcing||switching +swithed||switched +swithing||switching +symetric||symmetric +synax||syntax +synchonized||synchronized +syncronize||synchronize +syncronized||synchronized +syncronizing||synchronizing +syncronus||synchronous +syste||system +sytem||system +sythesis||synthesis +taht||that +targetted||targeted +targetting||targeting +teh||the +temorary||temporary +temproarily||temporarily +therfore||therefore +thier||their +threds||threads +threshhold||threshold +throught||through +thses||these +tiggered||triggered +tipically||typically +timout||timeout +tmis||this +torerable||tolerable +tramsmitted||transmitted +tramsmit||transmit +tranfer||transfer +transciever||transceiver +transferd||transferred +transfered||transferred +transfering||transferring +transision||transition +transmittd||transmitted +transormed||transformed +trasmission||transmission +treshold||threshold +trigerring||triggering +trun||turn +ture||true +tyep||type +udpate||update +uesd||used +uncommited||uncommitted +unconditionaly||unconditionally +underun||underrun +unecessary||unnecessary +unexecpted||unexpected +unexepected||unexpected +unexpcted||unexpected +unexpectd||unexpected +unexpeted||unexpected +unexpexted||unexpected +unfortunatelly||unfortunately +unifiy||unify +unintialized||uninitialized +unkmown||unknown +unknonw||unknown +unknow||unknown +unkown||unknown +unneded||unneeded +unneedingly||unnecessarily +unnsupported||unsupported +unmached||unmatched +unregester||unregister +unresgister||unregister +unrgesiter||unregister +unsinged||unsigned +unstabel||unstable +unsolicitied||unsolicited +unsuccessfull||unsuccessful +unsuported||unsupported +untill||until +unuseful||useless +upate||update +usefule||useful +usefull||useful +usege||usage +usera||users +usualy||usually +utilites||utilities +utillities||utilities +utilties||utilities +utiltity||utility +utitity||utility +utitlty||utility +vaid||valid +vaild||valid +valide||valid +variantions||variations +varible||variable +varient||variant +vaule||value +verbse||verbose +verisons||versions +verison||version +verson||version +vicefersa||vice-versa +virtal||virtual +virtaul||virtual +virtiual||virtual +visiters||visitors +vitual||virtual +wating||waiting +wether||whether +whataver||whatever +whcih||which +whenver||whenever +wheter||whether +whe||when +wierd||weird +wiil||will +wirte||write +withing||within +wnat||want +workarould||workaround +writeing||writing +writting||writing +zombe||zombie +zomebie||zombie