Hello,
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Alveo PCIe FPGA based platforms have a static *shell* partition and a partial
re-configurable *user* partition. The shell partition is automatically loaded from
flash when host is booted and PCIe is enumerated by BIOS. Shell cannot be changed
till the next cold reboot. The shell exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo platform,
XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA image using
Vitis https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
tools. The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap the image
running on the user partition in order to switch between different workloads by
loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xmgmt* binds to management physical function of Alveo
platform. The modular driver framework is organized into several platform drivers
which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst. Unlike previous V1 and V2 version
of patch series, V3 version does not require export of libfdt symbols.
TESTING AND VALIDATION
xmgmt driver can be tested with full XRT open source stack which includes user
space libraries, board utilities and (out of tree) first generation user physical
function driver xocl. XRT open source runtime stack is available at
https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on Alveo/XRT
security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.htmlhttps://xilinx.github.io/XRT/master/html/security.htmlhttps://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V1 version of patch series can be found here:
https://lore.kernel.org/lkml/20201217075046.28553-1-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-2-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-3-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-4-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-5-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-6-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-7-sonals@xilinx.com/
Lizhi Hou (18):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib platform driver manager
fpga: xrt: group platform driver
fpga: xrt: platform driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main platform driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: VSEC platform driver
fpga: xrt: UCS platform driver
fpga: xrt: ICAP platform driver
fpga: xrt: devctl platform driver
fpga: xrt: clock platform driver
fpga: xrt: clock frequence counter platform driver
fpga: xrt: DDR calibration platform driver
fpga: xrt: partition isolation platform driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842 ++++++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 4 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 48 ++
drivers/fpga/xrt/include/group.h | 27 +
drivers/fpga/xrt/include/metadata.h | 229 ++++++
drivers/fpga/xrt/include/subdev_id.h | 43 ++
drivers/fpga/xrt/include/xclbin-helper.h | 52 ++
drivers/fpga/xrt/include/xleaf.h | 276 +++++++
drivers/fpga/xrt/include/xleaf/axigate.h | 25 +
drivers/fpga/xrt/include/xleaf/calib.h | 30 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +
drivers/fpga/xrt/include/xleaf/clock.h | 31 +
drivers/fpga/xrt/include/xleaf/devctl.h | 43 ++
drivers/fpga/xrt/include/xleaf/icap.h | 29 +
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +
drivers/fpga/xrt/include/xmgmt-main.h | 37 +
drivers/fpga/xrt/include/xroot.h | 114 +++
drivers/fpga/xrt/lib/Kconfig | 16 +
drivers/fpga/xrt/lib/Makefile | 30 +
drivers/fpga/xrt/lib/cdev.c | 231 ++++++
drivers/fpga/xrt/lib/group.c | 265 +++++++
drivers/fpga/xrt/lib/main.c | 274 +++++++
drivers/fpga/xrt/lib/main.h | 17 +
drivers/fpga/xrt/lib/subdev.c | 871 +++++++++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 ++++++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 ++++++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 ++++++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 +++++++++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 ++++++
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++
drivers/fpga/xrt/lib/xroot.c | 598 ++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 524 ++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/fmgr-drv.c | 187 +++++
drivers/fpga/xrt/mgmt/fmgr.h | 28 +
drivers/fpga/xrt/mgmt/main-impl.h | 37 +
drivers/fpga/xrt/mgmt/main-region.c | 471 ++++++++++++
drivers/fpga/xrt/mgmt/main.c | 693 ++++++++++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++
include/uapi/linux/xrt/xmgmt-ioctl.h | 46 ++
53 files changed, 9957 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/main.c
create mode 100644 drivers/fpga/xrt/lib/main.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
create mode 100644 drivers/fpga/xrt/mgmt/main-impl.h
create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/main.c
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h
--
2.18.4
@@ -0,0 +1,842 @@+.. SPDX-License-Identifier: GPL-2.0++==================================+XRTV2 Linux Kernel Driver Overview+==================================++Authors:++* Sonal Santan <sonal.santan@xilinx.com>+* Max Zhen <max.zhen@xilinx.com>+* Lizhi Hou <lizhi.hou@xilinx.com>++XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_+drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_+PCIe platforms from Xilinx.++XRTV2 drivers support *subsystem* style data driven platforms where driver's+configuration and behavior is determined by meta data provided by the platform+(in *device tree* format). Primary management physical function (MPF) driver+is called **xmgmt**. Primary user physical function (UPF) driver is called+**xuser** and is under development. xrt driver framework and HW subsystem+drivers are packaged into a library module called **xrt-lib**, which is+shared by **xmgmt** and **xuser** (under development). The xrt driver framework+implements a pseudo-bus which is used to discover HW subsystems and facilitate+inter HW subsystem interaction.++Driver Modules+==============++xrt-lib.ko+----------++Repository of all subsystem drivers and pure software modules that can potentially+be shared between xmgmt and xuser. All these drivers are structured as Linux+*platform driver* and are instantiated by xmgmt (or xuser under development) based+on meta data associated with hardware. The metadata is in the form of device tree+as mentioned before. Each platform driver statically defines a subsystem node+array by using node name or a string in its ``compatible`` property. And this+array is eventually translated to IOMEM resources of the platform device.++The xrt-lib core infrastructure provides hooks to platform drivers for device node+management, user file operations and ioctl callbacks. The core also provides pseudo-bus+functionality for platform driver registration, discovery and inter platform driver+ioctl calls.++..note::+ See code in ``include/xleaf.h``+++xmgmt.ko+--------++The xmgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo+PCIE device. It consists of one *root* driver, one or more *group* drivers+and one or more *xleaf* drivers. The root and MPF specific xleaf drivers are+in xmgmt.ko. The group driver and other xleaf drivers are in xrt-lib.ko.++The instantiation of specific group driver or xleaf driver is completely data+driven based on meta data (mostly in device tree format) found through VSEC+capability and inside firmware files, such as platform xsabin or user xclbin file.+The root driver manages life cycle of multiple group drivers, which, in turn,+manages multiple xleaf drivers. This allows a single set of driver code to support+all kinds of subsystems exposed by different shells. The difference among all+these subsystems will be handled in xleaf drivers with root and group drivers+being part of the infrastructure and provide common services for all leaves+found on all platforms.++The driver object model looks like the following::++ +-----------++| xroot |+ +-----+-----++ |+ +-----------+-----------++| |+ v v+ +-----------+ +-----------++| group | ... | group |+ +-----+-----+ +------+----++| |+| |+ +-----+----+ +-----+----++| | | |+ v v v v+ +-------+ +-------+ +-------+ +-------++| xleaf |..| xleaf | | xleaf |..| xleaf |+ +-------+ +-------+ +-------+ +-------+++As an example for Xilinx Alveo U50 before user xclbin download, the tree+looks like the following::++ +-----------++| xmgmt |+ +-----+-----++ |+ +-------------------------+--------------------++| | |+ v v v+ +--------+ +--------+ +--------++| group0 | | group1 | | group2 |+ +----+---+ +----+---+ +---+----++| | |+| | |+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------++| | | | | | | | |+ v v | v v | v v |+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |+| xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |+| +---------+ | +------+ +-----------+ |+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<++ +---------+ | +------+ +-----------++| +-------++ +->| CALIB |+ +-------+++After an xclbin is download, group3 will be added and the tree looks like the+following::++ +-----------++| xmgmt |+ +-----+-----++ |+ +-------------------------+--------------------+-----------------++| | | |+ v v v |+ +--------+ +--------+ +--------+ |+| group0 | | group1 | | group2 | |+ +----+---+ +----+---+ +---+----+ |+| | | |+| | | |+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |+| | | | | | | | | |+ v v | v v | v v | |+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |+| xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | | |+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |+| +---------+ | +------+ +-----------+ | |+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |+ +---------+ | +------+ +-----------+ |+| +-------+ |+ +->| CALIB | |+ +-------+ |+ +---+----+ |+| group3 |<--------------------------------------------++ +--------++ |+ |+ +-------+--------+---+--+--------+------+-------++| | | | | | |+ v | v | v | v+ +--------+ | +--------+ | +--------+ | +-----++| CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |+ +--------+ v +--------+ v +--------+ v +-----++ +-------------+ +-------------+ +-------------++| CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |+ +-------------+ +-------------+ +-------------++++xmgmt-root+^^^^^^^^^^++The xmgmt-root driver is a PCIe device driver attached to MPF. It's part of the+infrastructure of the MPF driver and resides in xmgmt.ko. This driver++* manages one or more group drivers+* provides access to functionalities that requires pci_dev, such as PCIE config+ space access, to other xleaf drivers through root calls+* together with group driver, facilities event callbacks for other xleaf drivers+* together with group driver, facilities inter-leaf driver calls for other xleaf+ drivers++When root driver starts, it will explicitly create an initial group instance,+which contains xleaf drivers that will trigger the creation of other group+instances. The root driver will wait for all group and leaves to be created+before it returns from it's probe routine and claim success of the+initialization of the entire xmgmt driver.++..note::+ See code in ``lib/xroot.c`` and ``mgmt/root.c``+++group+^^^^^++The group driver is a platform device driver whose life cycle is managed by+root and does not have real IO mem or IRQ resources. It's part of the+infrastructure of the MPF driver and resides in xrt-lib.ko. This driver++* manages one or more xleaf drivers so that multiple leaves can be managed as a+ group+* provides access to root from leaves, so that root calls, event notifications+ and inter-leaf calls can happen++In xmgmt, an initial group driver instance will be created by root, which+contains leaves that will trigger group instances to be created to manage+groups of leaves found on different partitions on hardware, such as VSEC, Shell,+and User.++Every *fpga_region* has a group object associated with it. The group is+created when xclbin image is loaded on the fpga_region. The existing group+is destroyed when a new xclbin image is loaded. The fpga_region persists+across xclbin downloads.++..note::+ See code in ``lib/group.c``+++xleaf+^^^^^++The xleaf driver is a platform device driver whose life cycle is managed by+a group driver and may or may not have real IO mem or IRQ resources. They+are the real meat of xmgmt and contains platform specific code to Shell and+User found on a MPF.++A xleaf driver may not have real hardware resources when it merely acts as a+driver that manages certain in-memory states for xmgmt. These in-memory states+could be shared by multiple other leaves.++Leaf drivers assigned to specific hardware resources drive specific subsystem in+the device. To manipulate the subsystem or carry out a task, a xleaf driver may+ask help from root via root calls and/or from other leaves via inter-leaf calls.++A xleaf can also broadcast events through infrastructure code for other leaves+to process. It can also receive event notification from infrastructure about+certain events, such as post-creation or pre-exit of a particular xleaf.++..note::+ See code in ``lib/xleaf/*.c``+++FPGA Manager Interaction+========================++fpga_manager+------------++An instance of fpga_manager is created by xmgmt_main and is used for xclbin+image download. fpga_manager requires the full xclbin image before it can+start programming the FPGA configuration engine via ICAP platform driver.++fpga_region+-----------++For every interface exposed by currently loaded xclbin/xsabin in the *parent*+fpga_region a new instance of fpga_region is created like a *child* region.+The device tree of the *parent* fpga_region defines the+resources for a new instance of fpga_bridge which isolates the parent from+child fpga_region. This new instance of fpga_bridge will be used when a+xclbin image is loaded on the child fpga_region. After the xclbin image is+downloaded to the fpga_region, an instance of group is created for the+fpga_region using the device tree obtained as part of xclbin. If this device+tree defines any child interfaces then it can trigger the creation of+fpga_bridge and fpga_region for the next region in the chain.++fpga_bridge+-----------++Like fpga_region, matching fpga_bridge is also created by walking the device+tree of the parent group.++Driver Interfaces+=================++xmgmt Driver Ioctls+-------------------++Ioctls exposed by xmgmt driver to user space are enumerated in the following+table:++== ===================== ============================ ==========================+# Functionality ioctl request code data format+== ===================== ============================ ==========================+1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf+== ===================== ============================ ==========================++User xclbin can be downloaded by using xbmgmt tool from XRT open source suite. See+example usage below::++ xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force++xmgmt Driver Sysfs+------------------++xmgmt driver exposes a rich set of sysfs interfaces. Subsystem platform+drivers export sysfs node for every platform instance.++Every partition also exports its UUIDs. See below for examples::++ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids+++hwmon+-----++xmgmt driver exposes standard hwmon interface to report voltage, current,+temperature, power, etc. These can easily be viewed using *sensors* command+line utility.++Alveo Platform Overview+=======================++Alveo platforms are architected as two physical FPGA partitions: *Shell* and+*User*. The Shell provides basic infrastructure for the Alveo platform like+PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,+clocking, reset, and security. User partition contains user compiled FPGA+binary which is loaded by a process called DFX also known as partial+reconfiguration.++Physical partitions require strict HW compatibility with each other for DFX to+work properly. Every physical partition has two interface UUIDs: *parent* UUID+and *child* UUID. For simple single stage platforms, Shell → User forms parent+child relationship. For complex two stage platforms, Base → Shell → User forms+the parent child relationship chain.++..note::+ Partition compatibility matching is key design component of Alveo platforms+ and XRT. Partitions have child and parent relationship. A loaded partition+ exposes child partition UUID to advertise its compatibility requirement for+ child partition. When loading a child partition the xmgmt management driver+ matches parent UUID of the child partition against child UUID exported by+ the parent. Parent and child partition UUIDs are stored in the *xclbin*+ (for user) or *xsabin* (for base and shell). Except for root UUID, VSEC,+ hardware itself does not know about UUIDs. UUIDs are stored in xsabin and+ xclbin.+++The physical partitions and their loading is illustrated below::++ SHELL USER+ +-----------+ +-------------------++| | | |+| VSEC UUID | CHILD PARENT | LOGIC UUID |+| o------->|<--------o |+| | UUID UUID | |+ +-----+-----+ +--------+----------++| |+ . .+| |+ +---+---+ +------+--------++| POR | | USER COMPILED |+| FLASH | | XCLBIN |+ +-------+ +---------------++++Loading Sequence+----------------++The Shell partition is loaded from flash at system boot time. It establishes the+PCIe link and exposes two physical functions to the BIOS. After OS boot, xmgmt+driver attaches to PCIe physical function 0 exposed by the Shell and then looks+for VSEC in PCIe extended configuration space. Using VSEC it determines the logic+UUID of Shell and uses the UUID to load matching *xsabin* file from Linux firmware+directory. The xsabin file contains metadata to discover peripherals that are part+of Shell and firmware(s) for any embedded soft processors in Shell.++The Shell exports child interface UUID which is used for compatibility check when+loading user compiled xclbin over the User partition as part of DFX. When a user+requests loading of a specific xclbin the xmgmt management driver reads the parent+interface UUID specified in the xclbin and matches it with child interface UUID+exported by Shell to determine if xclbin is compatible with the Shell. If match+fails loading of xclbin is denied.++xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl command. When loading+xclbin, xmgmt driver performs the following *logical* operations:++1. Copy xclbin from user to kernel memory+2. Sanity check the xclbin contents+3. Isolate the User partition+4. Download the bitstream using the FPGA config engine (ICAP)+5. De-isolate the User partition+6. Program the clocks (ClockWiz) driving the User partition+7. Wait for memory controller (MIG) calibration+8. Return the loading status back to the caller++`Platform Loading Overview <https://xilinx.github.io/XRT/master/html/platforms_partitions.html>`_+provides more detailed information on platform loading.+++xsabin+------++Each Alveo platform comes packaged with its own xsabin. The xsabin is trusted+component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`+below. xsabin contains basic information like UUIDs, platform name and metadata in the+form of device tree. See :ref:`device_tree_usage` below for details and example.++xclbin+------++xclbin is compiled by end user using+`Vitis <https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html>`_+tool set from Xilinx. The xclbin contains sections describing user compiled+acceleration engines/kernels, memory subsystems, clocking information etc. It also+contains bitstream for the user partition, UUIDs, platform name, etc. xclbin uses+the same container format as xsabin which is described below.+++.._xsabin_xclbin_container_format:++xsabin/xclbin Container Format+------------------------------++xclbin/xsabin is ELF-like binary container format. It is structured as series of+sections. There is a file header followed by several section headers which is+followed by sections. A section header points to an actual section. There is an+optional signature at the end. The format is defined by header file ``xclbin.h``.+The following figure illustrates a typical xclbin::+++ +---------------------++| |+| HEADER |+ +---------------------++| SECTION HEADER |+| |+ +---------------------++| ... |+| |+ +---------------------++| SECTION HEADER |+| |+ +---------------------++| SECTION |+| |+ +---------------------++| ... |+| |+ +---------------------++| SECTION |+| |+ +---------------------++| SIGNATURE |+| (OPTIONAL) |+ +---------------------++++xclbin/xsabin files can be packaged, un-packaged and inspected using XRT utility+called **xclbinutil**. xclbinutil is part of XRT open source software stack. The+source code for xclbinutil can be found at+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil++For example to enumerate the contents of a xclbin/xsabin use the *--info* switch+as shown below::+++ xclbinutil --info --input /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin+ xclbinutil --info --input /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsabin+++.._device_tree_usage:++Device Tree Usage+-----------------++As mentioned previously xsabin stores metadata which advertise HW subsystems present+in a partition. The metadata is stored in device tree format with well defined schema.+XRT management driver uses this information to bind *platform drivers* to the subsystem+instantiations. The platform drivers are found in **xrt-lib.ko** kernel module defined+later.++Logic UUID+^^^^^^^^^^+A partition is identified uniquely through ``logic_uuid`` property::++ /dts-v1/;+ / {+ logic_uuid = "0123456789abcdef0123456789abcdef";+ ...+ }++Schema Version+^^^^^^^^^^^^^^+Schema version is defined through ``schema_version`` node. And it contains ``major``+and ``minor`` properties as below::++ /dts-v1/;+ / {+ schema_version {+ major = <0x01>;+ minor = <0x00>;+ };+ ...+ }++Partition UUIDs+^^^^^^^^^^^^^^^+As said earlier, each partition may have parent and child UUIDs. These UUIDs are+defined by ``interfaces`` node and ``interface_uuid`` property::++ /dts-v1/;+ / {+ interfaces {+ @0 {+ interface_uuid = "0123456789abcdef0123456789abcdef";+ };+ @1 {+ interface_uuid = "fedcba9876543210fedcba9876543210";+ };+ ...+ };+ ...+ }+++Subsystem Instantiations+^^^^^^^^^^^^^^^^^^^^^^^^+Subsystem instantiations are captured as children of ``addressable_endpoints``+node::++ /dts-v1/;+ / {+ addressable_endpoints {+ abc {+ ...+ };+ def {+ ...+ };+ ...+ }+ }++Subnode 'abc' and 'def' are the name of subsystem nodes++Subsystem Node+^^^^^^^^^^^^^^+Each subsystem node and its properties define a hardware instance::+++ addressable_endpoints {+ abc {+ reg = <0xa 0xb>+ pcie_physical_function = <0x0>;+ pcie_bar_mapping = <0x2>;+ compatible = "abc def";+ firmware {+ firmware_product_name = "abc"+ firmware_branch_name = "def"+ firmware_version_major = <1>+ firmware_version_minor = <2>+ };+ }+ ...+ }++:reg:+ Property defines address range. '<0xa 0xb>' is BAR offset and length pair, both+ are 64-bit integer.+:pcie_physical_function:+ Property specifies which PCIe physical function the subsystem node resides.+:pcie_bar_mapping:+ Property specifies which PCIe BAR the subsystem node resides. '<0x2>' is BAR+ index and it is 0 if this property is not defined.+:compatible:+ Property is a list of strings. The first string in the list specifies the exact+ subsystem node. The following strings represent other devices that the device+ is compatible with.+:firmware:+ Subnode defines the firmware required by this subsystem node.++Alveo U50 Platform Example+^^^^^^^^^^^^^^^^^^^^^^^^^^+::++ /dts-v1/;++ /{+ logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";++ schema_version {+ major = <0x01>;+ minor = <0x00>;+ };++ interfaces {++ @0 {+ interface_uuid = "862c7020a250293e32036f19956669e5";+ };+ };++ addressable_endpoints {++ ep_blp_rom_00 {+ reg = <0x00 0x1f04000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";+ };++ ep_card_flash_program_00 {+ reg = <0x00 0x1f06000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_quad_spi-1.0\0axi_quad_spi";+ interrupts = <0x03 0x03>;+ };++ ep_cmc_firmware_mem_00 {+ reg = <0x00 0x1e20000 0x00 0x20000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";++ firmware {+ firmware_product_name = "cmc";+ firmware_branch_name = "u50";+ firmware_version_major = <0x01>;+ firmware_version_minor = <0x00>;+ };+ };++ ep_cmc_intc_00 {+ reg = <0x00 0x1e03000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";+ interrupts = <0x04 0x04>;+ };++ ep_cmc_mutex_00 {+ reg = <0x00 0x1e02000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_cmc_regmap_00 {+ reg = <0x00 0x1e08000 0x00 0x2000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";++ firmware {+ firmware_product_name = "sc-fw";+ firmware_branch_name = "u50";+ firmware_version_major = <0x05>;+ };+ };++ ep_cmc_reset_00 {+ reg = <0x00 0x1e01000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_ddr_mem_calib_00 {+ reg = <0x00 0x63000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_debug_bscan_mgmt_00 {+ reg = <0x00 0x1e90000 0x00 0x10000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-debug_bridge-1.0\0debug_bridge";+ };++ ep_ert_base_address_00 {+ reg = <0x00 0x21000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_ert_command_queue_mgmt_00 {+ reg = <0x00 0x40000 0x00 0x10000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";+ };++ ep_ert_command_queue_user_00 {+ reg = <0x00 0x40000 0x00 0x10000>;+ pcie_physical_function = <0x01>;+ compatible = "xilinx.com,reg_abs-ert_command_queue-1.0\0ert_command_queue";+ };++ ep_ert_firmware_mem_00 {+ reg = <0x00 0x30000 0x00 0x8000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";++ firmware {+ firmware_product_name = "ert";+ firmware_branch_name = "v20";+ firmware_version_major = <0x01>;+ };+ };++ ep_ert_intc_00 {+ reg = <0x00 0x23000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_intc-1.0\0axi_intc";+ interrupts = <0x05 0x05>;+ };++ ep_ert_reset_00 {+ reg = <0x00 0x22000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_ert_sched_00 {+ reg = <0x00 0x50000 0x00 0x1000>;+ pcie_physical_function = <0x01>;+ compatible = "xilinx.com,reg_abs-ert_sched-1.0\0ert_sched";+ interrupts = <0x09 0x0c>;+ };++ ep_fpga_configuration_00 {+ reg = <0x00 0x1e88000 0x00 0x8000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_hwicap-1.0\0axi_hwicap";+ interrupts = <0x02 0x02>;+ };++ ep_icap_reset_00 {+ reg = <0x00 0x1f07000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_msix_00 {+ reg = <0x00 0x00 0x00 0x20000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-msix-1.0\0msix";+ pcie_bar_mapping = <0x02>;+ };++ ep_pcie_link_mon_00 {+ reg = <0x00 0x1f05000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_pr_isolate_plp_00 {+ reg = <0x00 0x1f01000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_pr_isolate_ulp_00 {+ reg = <0x00 0x1000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_gpio-1.0\0axi_gpio";+ };++ ep_uuid_rom_00 {+ reg = <0x00 0x64000 0x00 0x1000>;+ pcie_physical_function = <0x00>;+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-1.0\0axi_bram_ctrl";+ };++ ep_xdma_00 {+ reg = <0x00 0x00 0x00 0x10000>;+ pcie_physical_function = <0x01>;+ compatible = "xilinx.com,reg_abs-xdma-1.0\0xdma";+ pcie_bar_mapping = <0x02>;+ };+ };++ }++++Deployment Models+=================++Baremetal+---------++In bare-metal deployments both MPF and UPF are visible and accessible. xmgmt+driver binds to MPF. xmgmt driver operations are privileged and available to+system administrator. The full stack is illustrated below::++ HOST++ [XMGMT] [XUSER]+| |+| |+ +-----+ +-----++| MPF | | UPF |+| | | |+| PF0 | | PF1 |+ +--+--+ +--+--++ ......... ^................. ^..........+| |+| PCIe DEVICE |+| |+ +--+------------------+--++| SHELL |+| |+ +------------------------++| USER |+| |+| |+| |+| |+ +------------------------+++++Virtualized+-----------++In virtualized deployments privileged MPF is assigned to host but unprivileged+UPF is assigned to guest VM via PCIe pass-through. xmgmt driver in host binds+to MPF. xmgmt driver operations are privileged and only accessible by hosting+service provider. The full stack is illustrated below::+++ .............+ HOST . VM .+ . .+ [XMGMT] . [XUSER] .+| . | .+| . | .+ +-----+ . +-----+ .+| MPF | . | UPF | .+| | . | | .+| PF0 | . | PF1 | .+ +--+--+ . +--+--+ .+ ......... ^................. ^..........+| |+| PCIe DEVICE |+| |+ +--+------------------+--++| SHELL |+| |+ +------------------------++| USER |+| |+| |+| |+| |+ +------------------------+++++++Platform Security Considerations+================================++`Security of Alveo Platform <https://xilinx.github.io/XRT/master/html/security.html>`_+discusses the deployment options and security implications in great detail.
@@ -0,0 +1,524 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAMetadataparseAPIs+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/++#include<linux/libfdt_env.h>+#include"libfdt.h"+#include"metadata.h"++#define MAX_BLOB_SIZE (4096 * 25)++staticintxrt_md_setprop(structdevice*dev,char*blob,intoffset,+constchar*prop,constvoid*val,intsize);+staticintxrt_md_overlay(structdevice*dev,char*blob,inttarget,+constchar*overlay_blob,intoverlay_offset);+staticintxrt_md_get_endpoint(structdevice*dev,constchar*blob,+constchar*ep_name,constchar*regmap_name,+int*ep_offset);++unsignedlongxrt_md_size(structdevice*dev,constchar*blob)+{+unsignedlonglen=(long)fdt_totalsize(blob);++len=(len>MAX_BLOB_SIZE)?XRT_MD_INVALID_LENGTH:len;+returnlen;+}+EXPORT_SYMBOL_GPL(xrt_md_size);++intxrt_md_create(structdevice*dev,char**blob)+{+intret=0;++WARN_ON(!blob);++*blob=vmalloc(MAX_BLOB_SIZE);+if(!*blob)+return-ENOMEM;++ret=fdt_create_empty_tree(*blob,MAX_BLOB_SIZE);+if(ret){+dev_err(dev,"format blob failed, ret = %d",ret);+gotofailed;+}++ret=fdt_next_node(*blob,-1,NULL);+if(ret<0){+dev_err(dev,"No Node, ret = %d",ret);+gotofailed;+}++ret=fdt_add_subnode(*blob,ret,XRT_MD_NODE_ENDPOINTS);+if(ret<0){+dev_err(dev,"add node failed, ret = %d",ret);+gotofailed;+}++return0;++failed:+vfree(*blob);+*blob=NULL;++returnret;+}+EXPORT_SYMBOL_GPL(xrt_md_create);++staticintxrt_md_add_node(structdevice*dev,char*blob,intparent_offset,+constchar*ep_name)+{+intret;++ret=fdt_add_subnode(blob,parent_offset,ep_name);+if(ret<0&&ret!=-FDT_ERR_EXISTS)+dev_err(dev,"failed to add node %s. %d",ep_name,ret);++returnret;+}++intxrt_md_del_endpoint(structdevice*dev,char*blob,constchar*ep_name,+char*regmap_name)+{+intret;+intep_offset;++ret=xrt_md_get_endpoint(dev,blob,ep_name,regmap_name,&ep_offset);+if(ret){+dev_err(dev,"can not find ep %s",ep_name);+return-EINVAL;+}++ret=fdt_del_node(blob,ep_offset);+if(ret)+dev_err(dev,"delete node %s failed, ret %d",ep_name,ret);++returnret;+}+EXPORT_SYMBOL_GPL(xrt_md_del_endpoint);++staticint__xrt_md_add_endpoint(structdevice*dev,char*blob,+structxrt_md_endpoint*ep,int*offset,boolroot)+{+intret=0;+intep_offset=0;+u32val,count=0;+u64io_range[2];+charcomp[128];++if(!ep->ep_name){+dev_err(dev,"empty name");+return-EINVAL;+}++if(!root){+ret=xrt_md_get_endpoint(dev,blob,XRT_MD_NODE_ENDPOINTS,NULL,+&ep_offset);+if(ret){+dev_err(dev,"invalid blob, ret = %d",ret);+return-EINVAL;+}+}++ep_offset=xrt_md_add_node(dev,blob,ep_offset,ep->ep_name);+if(ep_offset<0){+dev_err(dev,"add endpoint failed, ret = %d",ret);+return-EINVAL;+}+if(offset)+*offset=ep_offset;++if(ep->size!=0){+val=cpu_to_be32(ep->bar);+ret=xrt_md_setprop(dev,blob,ep_offset,XRT_MD_PROP_BAR_IDX,+&val,sizeof(u32));+if(ret){+dev_err(dev,"set %s failed, ret %d",+XRT_MD_PROP_BAR_IDX,ret);+gotofailed;+}+io_range[0]=cpu_to_be64((u64)ep->bar_off);+io_range[1]=cpu_to_be64((u64)ep->size);+ret=xrt_md_setprop(dev,blob,ep_offset,XRT_MD_PROP_IO_OFFSET,+io_range,sizeof(io_range));+if(ret){+dev_err(dev,"set %s failed, ret %d",+XRT_MD_PROP_IO_OFFSET,ret);+gotofailed;+}+}++if(ep->regmap){+if(ep->regmap_ver){+count=snprintf(comp,sizeof(comp),+"%s-%s",ep->regmap,ep->regmap_ver);+count++;+}++count+=snprintf(comp+count,sizeof(comp)-count,+"%s",ep->regmap);+count++;++ret=xrt_md_setprop(dev,blob,ep_offset,XRT_MD_PROP_COMPATIBLE,+comp,count);+if(ret){+dev_err(dev,"set %s failed, ret %d",+XRT_MD_PROP_COMPATIBLE,ret);+gotofailed;+}+}++failed:+if(ret)+xrt_md_del_endpoint(dev,blob,ep->ep_name,NULL);++returnret;+}++intxrt_md_add_endpoint(structdevice*dev,char*blob,+structxrt_md_endpoint*ep)+{+return__xrt_md_add_endpoint(dev,blob,ep,NULL,false);+}+EXPORT_SYMBOL_GPL(xrt_md_add_endpoint);++staticintxrt_md_get_endpoint(structdevice*dev,constchar*blob,+constchar*ep_name,constchar*regmap_name,+int*ep_offset)+{+intoffset;+constchar*name;++for(offset=fdt_next_node(blob,-1,NULL);+offset>=0;+offset=fdt_next_node(blob,offset,NULL)){+name=fdt_get_name(blob,offset,NULL);+if(!name||strncmp(name,ep_name,strlen(ep_name)+1))+continue;+if(!regmap_name||+!fdt_node_check_compatible(blob,offset,regmap_name))+break;+}+if(offset<0)+return-ENODEV;++*ep_offset=offset;++return0;+}++intxrt_md_find_endpoint(structdevice*dev,constchar*blob,+constchar*ep_name,constchar*regmap_name,+constchar**epname)+{+intoffset;+intret;++ret=xrt_md_get_endpoint(dev,blob,ep_name,regmap_name,+&offset);+if(!ret&&epname&&offset>=0)+*epname=fdt_get_name(blob,offset,NULL);++returnret;+}+EXPORT_SYMBOL_GPL(xrt_md_find_endpoint);++intxrt_md_get_prop(structdevice*dev,constchar*blob,constchar*ep_name,+constchar*regmap_name,constchar*prop,+constvoid**val,int*size)+{+intoffset;+intret;++if(val)+*val=NULL;+if(ep_name){+ret=xrt_md_get_endpoint(dev,blob,ep_name,regmap_name,+&offset);+if(ret){+dev_err(dev,"cannot get ep %s, regmap %s, ret = %d",+ep_name,regmap_name,ret);+return-EINVAL;+}+}else{+offset=fdt_next_node(blob,-1,NULL);+if(offset<0){+dev_err(dev,"internal error, ret = %d",offset);+return-EINVAL;+}+}++if(val){+*val=fdt_getprop(blob,offset,prop,size);+if(!*val){+dev_dbg(dev,"get ep %s, prop %s failed",ep_name,prop);+return-EINVAL;+}+}++return0;+}+EXPORT_SYMBOL_GPL(xrt_md_get_prop);++staticintxrt_md_setprop(structdevice*dev,char*blob,intoffset,+constchar*prop,constvoid*val,intsize)+{+intret;++ret=fdt_setprop(blob,offset,prop,val,size);+if(ret)+dev_err(dev,"failed to set prop %d",ret);++returnret;+}++intxrt_md_set_prop(structdevice*dev,char*blob,+constchar*ep_name,constchar*regmap_name,+constchar*prop,constvoid*val,intsize)+{+intoffset;+intret;++if(ep_name){+ret=xrt_md_get_endpoint(dev,blob,ep_name,+regmap_name,&offset);+if(ret){+dev_err(dev,"cannot get node %s, ret = %d",+ep_name,ret);+return-EINVAL;+}+}else{+offset=fdt_next_node(blob,-1,NULL);+if(offset<0){+dev_err(dev,"internal error, ret = %d",offset);+return-EINVAL;+}+}++ret=xrt_md_setprop(dev,blob,offset,prop,val,size);+if(ret)+dev_err(dev,"set prop %s failed, ret = %d",prop,ret);++returnret;+}+EXPORT_SYMBOL_GPL(xrt_md_set_prop);++intxrt_md_copy_endpoint(structdevice*dev,char*blob,constchar*src_blob,+constchar*ep_name,constchar*regmap_name,+constchar*new_ep_name)+{+intoffset,target;+intret;+structxrt_md_endpointep={0};+constchar*newepnm=new_ep_name?new_ep_name:ep_name;++ret=xrt_md_get_endpoint(dev,src_blob,ep_name,regmap_name,+&offset);+if(ret)+return-EINVAL;++ret=xrt_md_get_endpoint(dev,blob,newepnm,regmap_name,&target);+if(ret){+ep.ep_name=newepnm;+ret=__xrt_md_add_endpoint(dev,blob,&ep,&target,+fdt_parent_offset(src_blob,offset)==0);+if(ret)+return-EINVAL;+}++ret=xrt_md_overlay(dev,blob,target,src_blob,offset);+if(ret)+dev_err(dev,"overlay failed, ret = %d",ret);++returnret;+}+EXPORT_SYMBOL_GPL(xrt_md_copy_endpoint);++char*xrt_md_dup(structdevice*dev,constchar*blob)+{+intret;+char*dup_blob;++ret=xrt_md_create(dev,&dup_blob);+if(ret)+returnNULL;+ret=xrt_md_overlay(dev,dup_blob,-1,blob,-1);+if(ret){+vfree(dup_blob);+returnNULL;+}++returndup_blob;+}+EXPORT_SYMBOL_GPL(xrt_md_dup);++staticintxrt_md_overlay(structdevice*dev,char*blob,inttarget,+constchar*overlay_blob,intoverlay_offset)+{+intproperty,subnode;+intret;++WARN_ON(!blob||!overlay_blob);++if(!blob){+dev_err(dev,"blob is NULL");+return-EINVAL;+}++if(target<0){+target=fdt_next_node(blob,-1,NULL);+if(target<0){+dev_err(dev,"invalid target");+return-EINVAL;+}+}+if(overlay_offset<0){+overlay_offset=fdt_next_node(overlay_blob,-1,NULL);+if(overlay_offset<0){+dev_err(dev,"invalid overlay");+return-EINVAL;+}+}++fdt_for_each_property_offset(property,overlay_blob,overlay_offset){+constchar*name;+constvoid*prop;+intprop_len;++prop=fdt_getprop_by_offset(overlay_blob,property,&name,+&prop_len);+if(!prop||prop_len>=MAX_BLOB_SIZE){+dev_err(dev,"internal error");+return-EINVAL;+}++ret=xrt_md_setprop(dev,blob,target,name,prop,+prop_len);+if(ret){+dev_err(dev,"setprop failed, ret = %d",ret);+returnret;+}+}++fdt_for_each_subnode(subnode,overlay_blob,overlay_offset){+constchar*name=fdt_get_name(overlay_blob,subnode,NULL);+intnnode;++nnode=xrt_md_add_node(dev,blob,target,name);+if(nnode==-FDT_ERR_EXISTS)+nnode=fdt_subnode_offset(blob,target,name);+if(nnode<0){+dev_err(dev,"add node failed, ret = %d",nnode);+returnnnode;+}++ret=xrt_md_overlay(dev,blob,nnode,overlay_blob,subnode);+if(ret)+returnret;+}++return0;+}++intxrt_md_get_next_endpoint(structdevice*dev,constchar*blob,+constchar*ep_name,constchar*regmap_name,+char**next_ep,char**next_regmap)+{+intoffset,ret;++if(!ep_name){+ret=xrt_md_get_endpoint(dev,blob,XRT_MD_NODE_ENDPOINTS,NULL,+&offset);+}else{+ret=xrt_md_get_endpoint(dev,blob,ep_name,regmap_name,+&offset);+}++if(ret){+*next_ep=NULL;+*next_regmap=NULL;+return-EINVAL;+}++offset=ep_name?fdt_next_subnode(blob,offset):+fdt_first_subnode(blob,offset);+if(offset<0){+*next_ep=NULL;+*next_regmap=NULL;+return-EINVAL;+}++*next_ep=(char*)fdt_get_name(blob,offset,NULL);+*next_regmap=(char*)fdt_stringlist_get(blob,offset,XRT_MD_PROP_COMPATIBLE,+0,NULL);++return0;+}+EXPORT_SYMBOL_GPL(xrt_md_get_next_endpoint);++intxrt_md_get_compatible_endpoint(structdevice*dev,constchar*blob,+constchar*regmap_name,constchar**ep_name)+{+intep_offset;++ep_offset=fdt_node_offset_by_compatible(blob,-1,regmap_name);+if(ep_offset<0){+*ep_name=NULL;+return-ENOENT;+}++*ep_name=(char*)fdt_get_name(blob,ep_offset,NULL);++return0;+}+EXPORT_SYMBOL_GPL(xrt_md_get_compatible_endpoint);++voidxrt_md_pack(structdevice*dev,char*blob)+{+intret;++ret=fdt_pack(blob);+if(ret)+dev_err(dev,"pack failed %d",ret);+}+EXPORT_SYMBOL_GPL(xrt_md_pack);++intxrt_md_get_intf_uuids(structdevice*dev,constchar*blob,+u32*num_uuids,uuid_t*intf_uuids)+{+intoffset,count=0;+intret;+constchar*uuid_str;++ret=xrt_md_get_endpoint(dev,blob,XRT_MD_NODE_INTERFACES,NULL,&offset);+if(ret)+return-ENOENT;++for(offset=fdt_first_subnode(blob,offset);+offset>=0;+offset=fdt_next_subnode(blob,offset)){+uuid_str=fdt_getprop(blob,offset,XRT_MD_PROP_INTERFACE_UUID,+NULL);+if(!uuid_str){+dev_err(dev,"empty intf uuid node");+return-EINVAL;+}++if(intf_uuids&&count<*num_uuids){+ret=xrt_md_trans_str2uuid(dev,uuid_str,+&intf_uuids[count]);+if(ret)+return-EINVAL;+}+count++;+}++*num_uuids=count;++return0;+}+EXPORT_SYMBOL_GPL(xrt_md_get_intf_uuids);
@@ -0,0 +1,52 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*HeaderfileforXilinxRuntime(XRT)driver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*DavidZhang<davidzha@xilinx.com>+*SonalSantan<sonal.santan@xilinx.com>+*/++#ifndef _XRT_XCLBIN_H+#define _XRT_XCLBIN_H++#include<linux/types.h>+#include<linux/device.h>+#include<linux/xrt/xclbin.h>++#define ICAP_XCLBIN_V2 "xclbin2"+#define DMA_HWICAP_BITFILE_BUFFER_SIZE 1024+#define MAX_XCLBIN_SIZE (1024 * 1024 * 1024) /* Assuming xclbin <= 1G, always */++enumaxlf_section_kind;+structaxlf;++/**+*BitstreamheaderinformationasdefinedbyXilinxtools.+*Pleasenotethatthisstructdefinitionisnotownedbythedriver.+*/+structhw_icap_bit_header{+unsignedintheader_length;/* Length of header in 32 bit words */+unsignedintbitstream_length;/* Length of bitstream to read in bytes*/+unsignedchar*design_name;/* Design name get from bitstream */+unsignedchar*part_name;/* Part name read from bitstream */+unsignedchar*date;/* Date read from bitstream header */+unsignedchar*time;/* Bitstream creation time */+unsignedintmagic_length;/* Length of the magic numbers */+unsignedchar*version;/* Version string */+};++constchar*xrt_xclbin_kind_to_string(enumaxlf_section_kindkind);+intxrt_xclbin_get_section(conststructaxlf*xclbin,+enumaxlf_section_kindkind,void**data,+uint64_t*len);+intxrt_xclbin_get_metadata(structdevice*dev,conststructaxlf*xclbin,char**dtb);+intxrt_xclbin_parse_bitstream_header(constunsignedchar*data,+unsignedintsize,+structhw_icap_bit_header*header);+voidxrt_xclbin_free_header(structhw_icap_bit_header*header);+constchar*xrt_clock_type2epname(enumCLOCK_TYPEtype);++#endif /* _XRT_XCLBIN_H */
@@ -0,0 +1,394 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGADriverXCLBINparser+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:DavidZhang<davidzha@xilinx.com>+*/++#include<asm/errno.h>+#include<linux/vmalloc.h>+#include<linux/device.h>+#include"xclbin-helper.h"+#include"metadata.h"++/* Used for parsing bitstream header */+#define XHI_EVEN_MAGIC_BYTE 0x0f+#define XHI_ODD_MAGIC_BYTE 0xf0++/* Extra mode for IDLE */+#define XHI_OP_IDLE -1+#define XHI_BIT_HEADER_FAILURE -1++/* The imaginary module length register */+#define XHI_MLR 15++staticinlineunsignedcharxhi_data_and_inc(constunsignedchar*d,int*i,intsz)+{+unsignedchardata;++if(*i>=sz)+return-1;++data=d[*i];+(*i)++;++returndata;+}++staticconststructaxlf_section_header*+xrt_xclbin_get_section_hdr(conststructaxlf*xclbin,+enumaxlf_section_kindkind)+{+inti=0;++for(i=0;i<xclbin->m_header.m_numSections;i++){+if(xclbin->m_sections[i].m_sectionKind==kind)+return&xclbin->m_sections[i];+}++returnNULL;+}++staticint+xrt_xclbin_check_section_hdr(conststructaxlf_section_header*header,+u64xclbin_len)+{+intret;++ret=(header->m_sectionOffset+header->m_sectionSize)>xclbin_len?-EINVAL:0;++returnret;+}++staticintxrt_xclbin_section_info(conststructaxlf*xclbin,+enumaxlf_section_kindkind,+u64*offset,u64*size)+{+conststructaxlf_section_header*mem_header=NULL;+u64xclbin_len;+interr=0;++mem_header=xrt_xclbin_get_section_hdr(xclbin,kind);+if(!mem_header)+return-EINVAL;++xclbin_len=xclbin->m_header.m_length;+if(xclbin_len>MAX_XCLBIN_SIZE)+return-EINVAL;++err=xrt_xclbin_check_section_hdr(mem_header,xclbin_len);+if(err)+returnerr;++*offset=mem_header->m_sectionOffset;+*size=mem_header->m_sectionSize;++return0;+}++/* caller should free the allocated memory for **data */+intxrt_xclbin_get_section(conststructaxlf*buf,+enumaxlf_section_kindkind,+void**data,u64*len)+{+conststructaxlf*xclbin=(conststructaxlf*)buf;+void*section=NULL;+interr=0;+u64offset=0;+u64size=0;++err=xrt_xclbin_section_info(xclbin,kind,&offset,&size);+if(err)+returnerr;++section=vmalloc(size);+if(!section)+return-ENOMEM;++memcpy(section,((constchar*)xclbin)+offset,size);++*data=section;+if(len)+*len=size;++return0;+}+EXPORT_SYMBOL_GPL(xrt_xclbin_get_section);++/* parse bitstream header */+intxrt_xclbin_parse_bitstream_header(constunsignedchar*data,+unsignedintsize,+structhw_icap_bit_header*header)+{+unsignedintindex;+unsignedintlen;+unsignedinttmp;+unsignedinti;++memset(header,0,sizeof(*header));+/* Start Index at start of bitstream */+index=0;++/* Initialize HeaderLength. If header returned early inidicates+*failure.+*/+header->header_length=XHI_BIT_HEADER_FAILURE;++/* Get "Magic" length */+header->magic_length=xhi_data_and_inc(data,&index,size);+header->magic_length=(header->magic_length<<8)|xhi_data_and_inc(data,&index,size);++/* Read in "magic" */+for(i=0;i<header->magic_length-1;i++){+tmp=xhi_data_and_inc(data,&index,size);+if(i%2==0&&tmp!=XHI_EVEN_MAGIC_BYTE)+return-1;/* INVALID_FILE_HEADER_ERROR */++if(i%2==1&&tmp!=XHI_ODD_MAGIC_BYTE)+return-1;/* INVALID_FILE_HEADER_ERROR */+}++/* Read null end of magic data. */+tmp=xhi_data_and_inc(data,&index,size);++/* Read 0x01 (short) */+tmp=xhi_data_and_inc(data,&index,size);+tmp=(tmp<<8)|xhi_data_and_inc(data,&index,size);++/* Check the "0x01" half word */+if(tmp!=0x01)+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Read 'a' */+tmp=xhi_data_and_inc(data,&index,size);+if(tmp!='a')+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Get Design Name length */+len=xhi_data_and_inc(data,&index,size);+len=(len<<8)|xhi_data_and_inc(data,&index,size);++/* allocate space for design name and final null character. */+header->design_name=vmalloc(len);+if(!header->design_name)+return-ENOMEM;++/* Read in Design Name */+for(i=0;i<len;i++)+header->design_name[i]=xhi_data_and_inc(data,&index,size);++if(header->design_name[len-1]!='\0')+return-1;++header->version=strstr(header->design_name,"Version=")+strlen("Version=");++/* Read 'b' */+tmp=xhi_data_and_inc(data,&index,size);+if(tmp!='b')+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Get Part Name length */+len=xhi_data_and_inc(data,&index,size);+len=(len<<8)|xhi_data_and_inc(data,&index,size);++/* allocate space for part name and final null character. */+header->part_name=vmalloc(len);+if(!header->part_name)+return-ENOMEM;++/* Read in part name */+for(i=0;i<len;i++)+header->part_name[i]=xhi_data_and_inc(data,&index,size);++if(header->part_name[len-1]!='\0')+return-1;++/* Read 'c' */+tmp=xhi_data_and_inc(data,&index,size);+if(tmp!='c')+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Get date length */+len=xhi_data_and_inc(data,&index,size);+len=(len<<8)|xhi_data_and_inc(data,&index,size);++/* allocate space for date and final null character. */+header->date=vmalloc(len);+if(!header->date)+return-ENOMEM;++/* Read in date name */+for(i=0;i<len;i++)+header->date[i]=xhi_data_and_inc(data,&index,size);++if(header->date[len-1]!='\0')+return-1;++/* Read 'd' */+tmp=xhi_data_and_inc(data,&index,size);+if(tmp!='d')+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Get time length */+len=xhi_data_and_inc(data,&index,size);+len=(len<<8)|xhi_data_and_inc(data,&index,size);++/* allocate space for time and final null character. */+header->time=vmalloc(len);+if(!header->time)+return-ENOMEM;++/* Read in time name */+for(i=0;i<len;i++)+header->time[i]=xhi_data_and_inc(data,&index,size);++if(header->time[len-1]!='\0')+return-1;++/* Read 'e' */+tmp=xhi_data_and_inc(data,&index,size);+if(tmp!='e')+return-1;/* INVALID_FILE_HEADER_ERROR */++/* Get byte length of bitstream */+header->bitstream_length=xhi_data_and_inc(data,&index,size);+header->bitstream_length=(header->bitstream_length<<8)|+xhi_data_and_inc(data,&index,size);+header->bitstream_length=(header->bitstream_length<<8)|+xhi_data_and_inc(data,&index,size);+header->bitstream_length=(header->bitstream_length<<8)|+xhi_data_and_inc(data,&index,size);++header->header_length=index;++return0;+}+EXPORT_SYMBOL_GPL(xrt_xclbin_parse_bitstream_header);++voidxrt_xclbin_free_header(structhw_icap_bit_header*header)+{+vfree(header->design_name);+vfree(header->part_name);+vfree(header->date);+vfree(header->time);+}+EXPORT_SYMBOL_GPL(xrt_xclbin_free_header);++structxrt_clock_desc{+char*clock_ep_name;+u32clock_xclbin_type;+char*clkfreq_ep_name;+}clock_desc[]={+{+.clock_ep_name=XRT_MD_NODE_CLK_KERNEL1,+.clock_xclbin_type=CT_DATA,+.clkfreq_ep_name=XRT_MD_NODE_CLKFREQ_K1,+},+{+.clock_ep_name=XRT_MD_NODE_CLK_KERNEL2,+.clock_xclbin_type=CT_KERNEL,+.clkfreq_ep_name=XRT_MD_NODE_CLKFREQ_K2,+},+{+.clock_ep_name=XRT_MD_NODE_CLK_KERNEL3,+.clock_xclbin_type=CT_SYSTEM,+.clkfreq_ep_name=XRT_MD_NODE_CLKFREQ_HBM,+},+};++constchar*xrt_clock_type2epname(enumCLOCK_TYPEtype)+{+inti;++for(i=0;i<ARRAY_SIZE(clock_desc);i++){+if(clock_desc[i].clock_xclbin_type==type)+returnclock_desc[i].clock_ep_name;+}+returnNULL;+}+EXPORT_SYMBOL_GPL(xrt_clock_type2epname);++staticconstchar*clock_type2clkfreq_name(u32type)+{+inti;++for(i=0;i<ARRAY_SIZE(clock_desc);i++){+if(clock_desc[i].clock_xclbin_type==type)+returnclock_desc[i].clkfreq_ep_name;+}+returnNULL;+}++staticintxrt_xclbin_add_clock_metadata(structdevice*dev,+conststructaxlf*xclbin,+char*dtb)+{+inti;+u16freq;+structclock_freq_topology*clock_topo;+intrc=xrt_xclbin_get_section(xclbin,CLOCK_FREQ_TOPOLOGY,+(void**)&clock_topo,NULL);++if(rc)+return0;++for(i=0;i<clock_topo->m_count;i++){+u8type=clock_topo->m_clock_freq[i].m_type;+constchar*ep_name=xrt_clock_type2epname(type);+constchar*counter_name=clock_type2clkfreq_name(type);++if(!ep_name||!counter_name)+continue;++freq=cpu_to_be16(clock_topo->m_clock_freq[i].m_freq_Mhz);+rc=xrt_md_set_prop(dev,dtb,ep_name,NULL,XRT_MD_PROP_CLK_FREQ,+&freq,sizeof(freq));+if(rc)+break;++rc=xrt_md_set_prop(dev,dtb,ep_name,NULL,XRT_MD_PROP_CLK_CNT,+counter_name,strlen(counter_name)+1);+if(rc)+break;+}++vfree(clock_topo);++returnrc;+}++intxrt_xclbin_get_metadata(structdevice*dev,conststructaxlf*xclbin,char**dtb)+{+char*md=NULL,*newmd=NULL;+u64len;+intrc=xrt_xclbin_get_section(xclbin,PARTITION_METADATA,+(void**)&md,&len);++if(rc)+gotodone;++/* Sanity check the dtb section. */+if(xrt_md_size(dev,md)>len){+rc=-EINVAL;+gotodone;+}++newmd=xrt_md_dup(dev,md);+if(!newmd){+rc=-EFAULT;+gotodone;+}+/* Convert various needed xclbin sections into dtb. */+rc=xrt_xclbin_add_clock_metadata(dev,xclbin,newmd);++done:+if(rc==0)+*dtb=newmd;+else+vfree(newmd);+vfree(md);+returnrc;+}+EXPORT_SYMBOL_GPL(xrt_xclbin_get_metadata);
@@ -0,0 +1,408 @@+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */+/*+*XilinxFPGAcompiledbinarycontainerformat+*+*Copyright(C)2015-2021,XilinxInc+*/++#ifndef _XCLBIN_H_+#define _XCLBIN_H_++#ifdef _WIN32+#include<cstdint>+#include<algorithm>+#include"windows/uuid.h"+#else+#if defined(__KERNEL__)+#include<linux/types.h>+#include<linux/uuid.h>+#include<linux/version.h>+#elif defined(__cplusplus)+#include<cstdlib>+#include<cstdint>+#include<algorithm>+#include<uuid/uuid.h>+#else+#include<stdlib.h>+#include<stdint.h>+#include<uuid/uuid.h>+#endif+#endif++#ifdef __cplusplus+extern"C"{+#endif++/**+*DOC:ContainerformatforXilinxFPGAimages+*Thecontainerstoresbitstreams,metadataandfirmwareimages.+*xclbin/xsabinisELF-likebinarycontainerformat.Itisstructured+*seriesofsections.Thereisafileheaderfollowedbyseveralsection+*headerswhichisfollowedbysections.Asectionheaderpointstoan+*actualsection.Thereisanoptionalsignatureattheend.The+*followingfigureillustratesatypicalxclbin:+*+*+---------------------++*||+*|HEADER|+*+---------------------++*|SECTIONHEADER|+*||+*+---------------------++*|...|+*||+*+---------------------++*|SECTIONHEADER|+*||+*+---------------------++*|SECTION|+*||+*+---------------------++*|...|+*||+*+---------------------++*|SECTION|+*||+*+---------------------++*|SIGNATURE|+*|(OPTIONAL)|+*+---------------------++*/++enumXCLBIN_MODE{+XCLBIN_FLAT,+XCLBIN_PR,+XCLBIN_TANDEM_STAGE2,+XCLBIN_TANDEM_STAGE2_WITH_PR,+XCLBIN_HW_EMU,+XCLBIN_SW_EMU,+XCLBIN_MODE_MAX+};++enumaxlf_section_kind{+BITSTREAM=0,+CLEARING_BITSTREAM,+EMBEDDED_METADATA,+FIRMWARE,+DEBUG_DATA,+SCHED_FIRMWARE,+MEM_TOPOLOGY,+CONNECTIVITY,+IP_LAYOUT,+DEBUG_IP_LAYOUT,+DESIGN_CHECK_POINT,+CLOCK_FREQ_TOPOLOGY,+MCS,+BMC,+BUILD_METADATA,+KEYVALUE_METADATA,+USER_METADATA,+DNA_CERTIFICATE,+PDI,+BITSTREAM_PARTIAL_PDI,+PARTITION_METADATA,+EMULATION_DATA,+SYSTEM_METADATA,+SOFT_KERNEL,+ASK_FLASH,+AIE_METADATA,+ASK_GROUP_TOPOLOGY,+ASK_GROUP_CONNECTIVITY+};++enumMEM_TYPE{+MEM_DDR3,+MEM_DDR4,+MEM_DRAM,+MEM_STREAMING,+MEM_PREALLOCATED_GLOB,+MEM_ARE,+MEM_HBM,+MEM_BRAM,+MEM_URAM,+MEM_STREAMING_CONNECTION+};++enumIP_TYPE{+IP_MB=0,+IP_KERNEL,+IP_DNASC,+IP_DDR4_CONTROLLER,+IP_MEM_DDR4,+IP_MEM_HBM+};++structaxlf_section_header{+uint32_tm_sectionKind;/* Section type */+charm_sectionName[16];/* Examples: "stage2", "clear1", */+/* "clear2", "ocl1", "ocl2, */+/* "ublaze", "sched" */+uint64_tm_sectionOffset;/* File offset of section data */+uint64_tm_sectionSize;/* Size of section data */+};++structaxlf_header{+uint64_tm_length;/* Total size of the xclbin file */+uint64_tm_timeStamp;/* Number of seconds since epoch */+/* when xclbin was created */+uint64_tm_featureRomTimeStamp;/* TimeSinceEpoch of the featureRom */+uint16_tm_versionPatch;/* Patch Version */+uint8_tm_versionMajor;/* Major Version - Version: 2.1.0*/+uint8_tm_versionMinor;/* Minor Version */+uint32_tm_mode;/* XCLBIN_MODE */+union{+struct{+uint64_tm_platformId;/* 64 bit platform ID: */+/* vendor-device-subvendor-subdev */+uint64_tm_featureId;/* 64 bit feature id */+}rom;+unsignedcharrom_uuid[16];/* feature ROM UUID for which */+/* this xclbin was generated */+};+unsignedcharm_platformVBNV[64];/* e.g. */+/* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */+union{+charm_next_axlf[16];/* Name of next xclbin file */+/* in the daisy chain */+uuid_tuuid;/* uuid of this xclbin*/+};+charm_debug_bin[16];/* Name of binary with debug */+/* information */+uint32_tm_numSections;/* Number of section headers */+};++structaxlf{+charm_magic[8];/* Should be "xclbin2\0" */+int32_tm_signature_length;/* Length of the signature. */+/* -1 indicates no signature */+unsignedcharreserved[28];/* Note: Initialized to 0xFFs */++unsignedcharm_keyBlock[256];/* Signature for validation */+/* of binary */+uint64_tm_uniqueId;/* axlf's uniqueId, use it to */+/* skip redownload etc */+structaxlf_headerm_header;/* Inline header */+structaxlf_section_headerm_sections[1];/* One or more section */+/* headers follow */+};++/* bitstream information */+structxlnx_bitstream{+uint8_tm_freq[8];+charbits[1];+};++/**** MEMORY TOPOLOGY SECTION ****/+structmem_data{+uint8_tm_type;/* enum corresponding to mem_type. */+uint8_tm_used;/* if 0 this bank is not present */+union{+uint64_tm_size;/* if mem_type DDR, then size in KB; */+uint64_troute_id;/* if streaming then "route_id" */+};+union{+uint64_tm_base_address;/* if DDR then the base address; */+uint64_tflow_id;/* if streaming then "flow id" */+};+unsignedcharm_tag[16];/* DDR: BANK0,1,2,3, has to be null */+/* terminated; if streaming then stream0, 1 etc */+};++structmem_topology{+int32_tm_count;/* Number of mem_data */+structmem_datam_mem_data[1];/* Should be sorted on mem_type */+};++/**** CONNECTIVITY SECTION ****/+/* Connectivity of each argument of Kernel. It will be in terms of argument+*indexassociated.Forassociatingkernelinstanceswithargumentsand+*banks,startattheconnectivitysection.Usingthem_ip_layout_index+*accesstheip_data.m_name.Nowwecanassociatethiskernelinstance+*withitsoriginalkernelnameandgettheconnectivityaswell.This+*enablesustoformrelatedgroupsofkernelinstances.+*/++structconnection{+int32_targ_index;/* From 0 to n, may not be contiguous as scalars */+/* skipped */+int32_tm_ip_layout_index;/* index into the ip_layout section. */+/* ip_layout.m_ip_data[index].m_type == IP_KERNEL */+int32_tmem_data_index;/* index of the m_mem_data . Flag error is */+/* m_used false. */+};++structconnectivity{+int32_tm_count;+structconnectionm_connection[1];+};++/**** IP_LAYOUT SECTION ****/++/* IP Kernel */+#define IP_INT_ENABLE_MASK 0x0001+#define IP_INTERRUPT_ID_MASK 0x00FE+#define IP_INTERRUPT_ID_SHIFT 0x1++enumIP_CONTROL{+AP_CTRL_HS=0,+AP_CTRL_CHAIN=1,+AP_CTRL_NONE=2,+AP_CTRL_ME=3,+ACCEL_ADAPTER=4+};++#define IP_CONTROL_MASK 0xFF00+#define IP_CONTROL_SHIFT 0x8++/* IPs on AXI lite - their types, names, and base addresses.*/+structip_data{+uint32_tm_type;/* map to IP_TYPE enum */+union{+uint32_tproperties;/* Default: 32-bits to indicate ip */+/* specific property. */+/* m_type: IP_KERNEL+*m_int_enable:Bit-0x0000_0001;+*m_interrupt_id:Bits-0x0000_00FE;+*m_ip_control:Bits=0x0000_FF00;+*/+struct{/* m_type: IP_MEM_* */+uint16_tm_index;+uint8_tm_pc_index;+uint8_tunused;+}indices;+};+uint64_tm_base_address;+uint8_tm_name[64];/* eg Kernel name corresponding to KERNEL */+/* instance, can embed CU name in future. */+};++structip_layout{+int32_tm_count;+structip_datam_ip_data[1];/* All the ip_data needs to be sorted */+/* by m_base_address. */+};++/*** Debug IP section layout ****/+enumDEBUG_IP_TYPE{+UNDEFINED=0,+LAPC,+ILA,+AXI_MM_MONITOR,+AXI_TRACE_FUNNEL,+AXI_MONITOR_FIFO_LITE,+AXI_MONITOR_FIFO_FULL,+ACCEL_MONITOR,+AXI_STREAM_MONITOR,+AXI_STREAM_PROTOCOL_CHECKER,+TRACE_S2MM,+AXI_DMA,+TRACE_S2MM_FULL+};++structdebug_ip_data{+uint8_tm_type;/* type of enum DEBUG_IP_TYPE */+uint8_tm_index_lowbyte;+uint8_tm_properties;+uint8_tm_major;+uint8_tm_minor;+uint8_tm_index_highbyte;+uint8_tm_reserved[2];+uint64_tm_base_address;+charm_name[128];+};++structdebug_ip_layout{+uint16_tm_count;+structdebug_ip_datam_debug_ip_data[1];+};++/* Supported clock frequency types */+enumCLOCK_TYPE{+CT_UNUSED=0,/* Initialized value */+CT_DATA=1,/* Data clock */+CT_KERNEL=2,/* Kernel clock */+CT_SYSTEM=3/* System Clock */+};++/* Clock Frequency Entry */+structclock_freq{+uint16_tm_freq_Mhz;/* Frequency in MHz */+uint8_tm_type;/* Clock type (enum CLOCK_TYPE) */+uint8_tm_unused[5];/* Not used - padding */+charm_name[128];/* Clock Name */+};++/* Clock frequency section */+structclock_freq_topology{+int16_tm_count;/* Number of entries */+structclock_freqm_clock_freq[1];/* Clock array */+};++/* Supported MCS file types */+enumMCS_TYPE{+MCS_UNKNOWN=0,/* Initialized value */+MCS_PRIMARY=1,/* The primary mcs file data */+MCS_SECONDARY=2,/* The secondary mcs file data */+};++/* One chunk of MCS data */+structmcs_chunk{+uint8_tm_type;/* MCS data type */+uint8_tm_unused[7];/* padding */+uint64_tm_offset;/* data offset from the start of */+/* the section */+uint64_tm_size;/* data size */+};++/* MCS data section */+structmcs{+int8_tm_count;/* Number of chunks */+int8_tm_unused[7];/* padding */+structmcs_chunkm_chunk[1];/* MCS chunks followed by data */+};++/* bmc data section */+structbmc{+uint64_tm_offset;/* data offset from the start of */+/* the section */+uint64_tm_size;/* data size (bytes) */+charm_image_name[64];/* Name of the image */+/* (e.g., MSP432P401R) */+charm_device_name[64];/* Device ID (e.g., VCU1525) */+charm_version[64];+charm_md5value[33];/* MD5 Expected Value */+/* (e.g., 56027182079c0bd621761b7dab5a27ca)*/+charm_padding[7];/* Padding */+};++/* soft kernel data section, used by classic driver */+structsoft_kernel{+/** Prefix Syntax:+*mpo-member,pointer,offset+*Thisvariablerepresentsazeroterminatedstring+*thatisoffsetedfromthebeginningofthesection.+*Thepointertoaccessthestringisinitializedasfollows:+*char*pCharString=(address_of_section)+(mpovalue)+*/+uint32_tmpo_name;/* Name of the soft kernel */+uint32_tm_image_offset;/* Image offset */+uint32_tm_image_size;/* Image size */+uint32_tmpo_version;/* Version */+uint32_tmpo_md5_value;/* MD5 checksum */+uint32_tmpo_symbol_name;/* Symbol name */+uint32_tm_num_instances;/* Number of instances */+uint8_tpadding[36];/* Reserved for future use */+uint8_treservedExt[16];/* Reserved for future extended data */+};++enumCHECKSUM_TYPE{+CST_UNKNOWN=0,+CST_SDBM=1,+CST_LAST+};++#ifdef __cplusplus+}+#endif++#endif
@@ -0,0 +1,274 @@+// SPDX-License-Identifier: GPL-2.0+/*+*DriverforXilinxAlveoFPGASupport+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#include<linux/module.h>+#include"xleaf.h"+#include"xroot.h"+#include"main.h"++#define XRT_IPLIB_MODULE_NAME "xrt-lib"+#define XRT_IPLIB_MODULE_VERSION "4.0.0"+#define XRT_MAX_DEVICE_NODES 128+#define XRT_DRVNAME(drv) ((drv)->driver.name)++/*+*SubdevdriverisknownbyIDtoothers.WemaptheIDtoit's+*structplatform_driver,whichcontainsit'sbindingnameanddriver/fileops.+*WealsomapittotheendpointnameinDTBaswell,ifit'sdifferent+*thanthedriver'sbindingname.+*/+structxrt_drv_map{+structlist_headlist;+enumxrt_subdev_idid;+structplatform_driver*drv;+structxrt_subdev_endpoints*eps;+structidaida;/* manage driver instance and char dev minor */+};++staticDEFINE_MUTEX(xrt_lib_lock);/* global lock protecting xrt_drv_maps list */+staticLIST_HEAD(xrt_drv_maps);+structclass*xrt_class;++staticinlinestructxrt_subdev_drvdata*+xrt_drv_map2drvdata(structxrt_drv_map*map)+{+return(structxrt_subdev_drvdata*)map->drv->id_table[0].driver_data;+}++staticstructxrt_drv_map*+xrt_drv_find_map_by_id_nolock(enumxrt_subdev_idid)+{+conststructlist_head*ptr;++list_for_each(ptr,&xrt_drv_maps){+structxrt_drv_map*tmap=list_entry(ptr,structxrt_drv_map,list);++if(tmap->id==id)+returntmap;+}+returnNULL;+}++staticstructxrt_drv_map*+xrt_drv_find_map_by_id(enumxrt_subdev_idid)+{+structxrt_drv_map*map;++mutex_lock(&xrt_lib_lock);+map=xrt_drv_find_map_by_id_nolock(id);+mutex_unlock(&xrt_lib_lock);+/*+*mapshouldremainvalidevenafterlockisdroppedsincearegistered+*drivershouldonlybeunregisteredwhendrivermoduleisbeingunloaded,+*whichmeansthatthedrivershouldnotbeusedbythen.+*/+returnmap;+}++staticintxrt_drv_register_driver(structxrt_drv_map*map)+{+structxrt_subdev_drvdata*drvdata;+intrc=0;+constchar*drvname=XRT_DRVNAME(map->drv);++rc=platform_driver_register(map->drv);+if(rc){+pr_err("register %s platform driver failed\n",drvname);+returnrc;+}++drvdata=xrt_drv_map2drvdata(map);+if(drvdata){+/* Initialize dev_t for char dev node. */+if(xleaf_devnode_enabled(drvdata)){+rc=alloc_chrdev_region(&drvdata->xsd_file_ops.xsf_dev_t,0,+XRT_MAX_DEVICE_NODES,drvname);+if(rc){+platform_driver_unregister(map->drv);+pr_err("failed to alloc dev minor for %s: %d\n",drvname,rc);+returnrc;+}+}else{+drvdata->xsd_file_ops.xsf_dev_t=(dev_t)-1;+}+}++ida_init(&map->ida);++pr_info("%s registered successfully\n",drvname);++return0;+}++staticvoidxrt_drv_unregister_driver(structxrt_drv_map*map)+{+constchar*drvname=XRT_DRVNAME(map->drv);+structxrt_subdev_drvdata*drvdata;++ida_destroy(&map->ida);++drvdata=xrt_drv_map2drvdata(map);+if(drvdata&&drvdata->xsd_file_ops.xsf_dev_t!=(dev_t)-1){+unregister_chrdev_region(drvdata->xsd_file_ops.xsf_dev_t,+XRT_MAX_DEVICE_NODES);+}++platform_driver_unregister(map->drv);++pr_info("%s unregistered successfully\n",drvname);+}++intxleaf_register_driver(enumxrt_subdev_idid,+structplatform_driver*drv,+structxrt_subdev_endpoints*eps)+{+structxrt_drv_map*map;++mutex_lock(&xrt_lib_lock);++map=xrt_drv_find_map_by_id_nolock(id);+if(map){+mutex_unlock(&xrt_lib_lock);+pr_err("Id %d already has a registered driver, 0x%p\n",+id,map->drv);+return-EEXIST;+}++map=vzalloc(sizeof(*map));+if(!map){+mutex_unlock(&xrt_lib_lock);+return-ENOMEM;+}+map->id=id;+map->drv=drv;+map->eps=eps;++xrt_drv_register_driver(map);++list_add(&map->list,&xrt_drv_maps);++mutex_unlock(&xrt_lib_lock);++return0;+}+EXPORT_SYMBOL_GPL(xleaf_register_driver);++voidxleaf_unregister_driver(enumxrt_subdev_idid)+{+structxrt_drv_map*map;++mutex_lock(&xrt_lib_lock);++map=xrt_drv_find_map_by_id_nolock(id);+if(!map){+mutex_unlock(&xrt_lib_lock);+pr_err("Id %d has no registered driver\n",id);+return;+}++list_del(&map->list);++mutex_unlock(&xrt_lib_lock);++xrt_drv_unregister_driver(map);+vfree(map);+}+EXPORT_SYMBOL_GPL(xleaf_unregister_driver);++constchar*xrt_drv_name(enumxrt_subdev_idid)+{+structxrt_drv_map*map=xrt_drv_find_map_by_id(id);++if(map)+returnXRT_DRVNAME(map->drv);+returnNULL;+}++intxrt_drv_get_instance(enumxrt_subdev_idid)+{+structxrt_drv_map*map=xrt_drv_find_map_by_id(id);++returnida_alloc_range(&map->ida,0,XRT_MAX_DEVICE_NODES,GFP_KERNEL);+}++voidxrt_drv_put_instance(enumxrt_subdev_idid,intinstance)+{+structxrt_drv_map*map=xrt_drv_find_map_by_id(id);++ida_free(&map->ida,instance);+}++structxrt_subdev_endpoints*xrt_drv_get_endpoints(enumxrt_subdev_idid)+{+structxrt_drv_map*map=xrt_drv_find_map_by_id(id);+structxrt_subdev_endpoints*eps;++eps=map?map->eps:NULL;+returneps;+}++/* Leaf driver's module init/fini callbacks. */+staticvoid(*leaf_init_fini_cbs[])(bool)={+group_leaf_init_fini,+vsec_leaf_init_fini,+devctl_leaf_init_fini,+axigate_leaf_init_fini,+icap_leaf_init_fini,+calib_leaf_init_fini,+clkfreq_leaf_init_fini,+clock_leaf_init_fini,+ucs_leaf_init_fini,+};++static__initintxrt_lib_init(void)+{+inti;++xrt_class=class_create(THIS_MODULE,XRT_IPLIB_MODULE_NAME);+if(IS_ERR(xrt_class))+returnPTR_ERR(xrt_class);++for(i=0;i<ARRAY_SIZE(leaf_init_fini_cbs);i++)+leaf_init_fini_cbs[i](true);+return0;+}++static__exitvoidxrt_lib_fini(void)+{+structxrt_drv_map*map;+inti;++for(i=0;i<ARRAY_SIZE(leaf_init_fini_cbs);i++)+leaf_init_fini_cbs[i](false);++mutex_lock(&xrt_lib_lock);++while(!list_empty(&xrt_drv_maps)){+map=list_first_entry_or_null(&xrt_drv_maps,structxrt_drv_map,list);+pr_err("Unloading module with %s still registered\n",XRT_DRVNAME(map->drv));+list_del(&map->list);+mutex_unlock(&xrt_lib_lock);+xrt_drv_unregister_driver(map);+vfree(map);+mutex_lock(&xrt_lib_lock);+}++mutex_unlock(&xrt_lib_lock);++class_destroy(xrt_class);+}++module_init(xrt_lib_init);+module_exit(xrt_lib_fini);++MODULE_VERSION(XRT_IPLIB_MODULE_VERSION);+MODULE_AUTHOR("XRT Team <runtime@xilinx.com>");+MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");+MODULE_LICENSE("GPL v2");
@@ -0,0 +1,265 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAGroupDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include"xleaf.h"+#include"subdev_pool.h"+#include"group.h"+#include"metadata.h"+#include"main.h"++#define XRT_GRP "xrt_group"++structxrt_group{+structplatform_device*pdev;+structxrt_subdev_poolleaves;+boolleaves_created;+structmutexlock;/* lock for group */+};++staticintxrt_grp_root_cb(structdevice*dev,void*parg,+u32cmd,void*arg)+{+intrc;+structplatform_device*pdev=+container_of(dev,structplatform_device,dev);+structxrt_group*xg=(structxrt_group*)parg;++switch(cmd){+caseXRT_ROOT_GET_LEAF_HOLDERS:{+structxrt_root_ioctl_get_holders*holders=+(structxrt_root_ioctl_get_holders*)arg;+rc=xrt_subdev_pool_get_holders(&xg->leaves,+holders->xpigh_pdev,+holders->xpigh_holder_buf,+holders->xpigh_holder_buf_len);+break;+}+default:+/* Forward parent call to root. */+rc=xrt_subdev_root_request(pdev,cmd,arg);+break;+}++returnrc;+}++staticintxrt_grp_create_leaves(structxrt_group*xg)+{+structxrt_subdev_platdata*pdata=DEV_PDATA(xg->pdev);+enumxrt_subdev_iddid;+structxrt_subdev_endpoints*eps=NULL;+intep_count=0,i,ret=0,failed=0;+unsignedlongmlen;+char*dtb,*grp_dtb=NULL;+constchar*ep_name;++mutex_lock(&xg->lock);++if(xg->leaves_created){+mutex_unlock(&xg->lock);+return-EEXIST;+}++xrt_info(xg->pdev,"bringing up leaves...");++/* Create all leaves based on dtb. */+if(!pdata)+gotobail;++mlen=xrt_md_size(DEV(xg->pdev),pdata->xsp_dtb);+if(mlen==XRT_MD_INVALID_LENGTH){+xrt_err(xg->pdev,"invalid dtb, len %ld",mlen);+gotobail;+}++grp_dtb=vmalloc(mlen);+if(!grp_dtb)+gotobail;++memcpy(grp_dtb,pdata->xsp_dtb,mlen);+for(did=0;did<XRT_SUBDEV_NUM;){+eps=eps?eps+1:xrt_drv_get_endpoints(did);+if(!eps||!eps->xse_names){+did++;+eps=NULL;+continue;+}+ret=xrt_md_create(DEV(xg->pdev),&dtb);+if(ret){+xrt_err(xg->pdev,"create md failed, drv %s",+xrt_drv_name(did));+failed++;+continue;+}+for(i=0;eps->xse_names[i].ep_name||+eps->xse_names[i].regmap_name;i++){+ep_name=(char*)eps->xse_names[i].ep_name;+if(!ep_name){+xrt_md_get_compatible_endpoint(DEV(xg->pdev),+grp_dtb,+eps->xse_names[i].regmap_name,+&ep_name);+}+if(!ep_name)+continue;++ret=xrt_md_copy_endpoint(DEV(xg->pdev),+dtb,grp_dtb,ep_name,+(char*)eps->xse_names[i].regmap_name,+NULL);+if(ret)+continue;+xrt_md_del_endpoint(DEV(xg->pdev),grp_dtb,ep_name,+(char*)eps->xse_names[i].regmap_name);+ep_count++;+}+if(ep_count>=eps->xse_min_ep){+ret=xrt_subdev_pool_add(&xg->leaves,did,+xrt_grp_root_cb,xg,dtb);+eps=NULL;+if(ret<0){+failed++;+xrt_err(xg->pdev,"failed to create %s: %d",+xrt_drv_name(did),ret);+}+}elseif(ep_count>0){+xrt_md_copy_all_endpoints(DEV(xg->pdev),grp_dtb,dtb);+}+vfree(dtb);+ep_count=0;+}++xg->leaves_created=true;++bail:+vfree(grp_dtb);+mutex_unlock(&xg->lock);++returnfailed==0?0:-ECHILD;+}++staticvoidxrt_grp_remove_leaves(structxrt_group*xg)+{+mutex_lock(&xg->lock);++if(!xg->leaves_created){+mutex_unlock(&xg->lock);+return;+}++xrt_info(xg->pdev,"tearing down leaves...");+xrt_subdev_pool_fini(&xg->leaves);+xg->leaves_created=false;++mutex_unlock(&xg->lock);+}++staticintxrt_grp_probe(structplatform_device*pdev)+{+structxrt_group*xg;++xrt_info(pdev,"probing...");++xg=devm_kzalloc(&pdev->dev,sizeof(*xg),GFP_KERNEL);+if(!xg)+return-ENOMEM;++xg->pdev=pdev;+mutex_init(&xg->lock);+xrt_subdev_pool_init(DEV(pdev),&xg->leaves);+platform_set_drvdata(pdev,xg);++return0;+}++staticintxrt_grp_remove(structplatform_device*pdev)+{+structxrt_group*xg=platform_get_drvdata(pdev);++xrt_info(pdev,"leaving...");+xrt_grp_remove_leaves(xg);+return0;+}++staticintxrt_grp_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+intrc=0;+structxrt_group*xg=platform_get_drvdata(pdev);++switch(cmd){+caseXRT_XLEAF_EVENT:+/* Simply forward to every child. */+xrt_subdev_pool_handle_event(&xg->leaves,+(structxrt_event*)arg);+break;+caseXRT_GROUP_GET_LEAF:{+structxrt_root_ioctl_get_leaf*get_leaf=+(structxrt_root_ioctl_get_leaf*)arg;++rc=xrt_subdev_pool_get(&xg->leaves,get_leaf->xpigl_match_cb,+get_leaf->xpigl_match_arg,+DEV(get_leaf->xpigl_pdev),+&get_leaf->xpigl_leaf);+break;+}+caseXRT_GROUP_PUT_LEAF:{+structxrt_root_ioctl_put_leaf*put_leaf=+(structxrt_root_ioctl_put_leaf*)arg;++rc=xrt_subdev_pool_put(&xg->leaves,put_leaf->xpipl_leaf,+DEV(put_leaf->xpipl_pdev));+break;+}+caseXRT_GROUP_INIT_CHILDREN:+rc=xrt_grp_create_leaves(xg);+break;+caseXRT_GROUP_FINI_CHILDREN:+xrt_grp_remove_leaves(xg);+break;+caseXRT_GROUP_TRIGGER_EVENT:+xrt_subdev_pool_trigger_event(&xg->leaves,(enumxrt_events)(uintptr_t)arg);+break;+default:+xrt_err(pdev,"unknown IOCTL cmd %d",cmd);+rc=-EINVAL;+break;+}+returnrc;+}++staticstructxrt_subdev_drvdataxrt_grp_data={+.xsd_dev_ops={+.xsd_ioctl=xrt_grp_ioctl,+},+};++staticconststructplatform_device_idxrt_grp_id_table[]={+{XRT_GRP,(kernel_ulong_t)&xrt_grp_data},+{},+};++staticstructplatform_driverxrt_group_driver={+.driver={+.name=XRT_GRP,+},+.probe=xrt_grp_probe,+.remove=xrt_grp_remove,+.id_table=xrt_grp_id_table,+};++voidgroup_leaf_init_fini(boolinit)+{+if(init)+xleaf_register_driver(XRT_SUBDEV_GRP,&xrt_group_driver,NULL);+else+xleaf_unregister_driver(XRT_SUBDEV_GRP);+}
@@ -0,0 +1,37 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*HeaderfileforXilinxRuntime(XRT)driver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#ifndef _XMGMT_MAIN_H_+#define _XMGMT_MAIN_H_++#include<linux/xrt/xclbin.h>+#include"xleaf.h"++enumxrt_mgmt_main_ioctl_cmd{+/* section needs to be vfree'd by caller */+XRT_MGMT_MAIN_GET_AXLF_SECTION=XRT_XLEAF_CUSTOM_BASE,/* See comments in xleaf.h */+/* vbnv needs to be kfree'd by caller */+XRT_MGMT_MAIN_GET_VBNV,+};++enumprovider_kind{+XMGMT_BLP,+XMGMT_PLP,+XMGMT_ULP,+};++structxrt_mgmt_main_ioctl_get_axlf_section{+enumprovider_kindxmmigas_axlf_kind;+enumaxlf_section_kindxmmigas_section_kind;+void*xmmigas_section;+u64xmmigas_section_size;+};++#endif /* _XMGMT_MAIN_H_ */
@@ -0,0 +1,693 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAMGMTPFentrypointdriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*SonalSantan<sonals@xilinx.com>+*/++#include<linux/firmware.h>+#include<linux/uaccess.h>+#include"xclbin-helper.h"+#include"metadata.h"+#include"xleaf.h"+#include<linux/xrt/xmgmt-ioctl.h>+#include"xleaf/devctl.h"+#include"xmgmt-main.h"+#include"fmgr.h"+#include"xleaf/icap.h"+#include"xleaf/axigate.h"+#include"main-impl.h"++#define XMGMT_MAIN "xmgmt_main"++structxmgmt_main{+structplatform_device*pdev;+structaxlf*firmware_blp;+structaxlf*firmware_plp;+structaxlf*firmware_ulp;+boolflash_ready;+booldevctl_ready;+structfpga_manager*fmgr;+structmutexbusy_mutex;/* busy lock */++uuid_t*blp_intf_uuids;+u32blp_intf_uuid_num;+};++/* Caller should be responsible for freeing the returned string. */+char*xmgmt_get_vbnv(structplatform_device*pdev)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);+constchar*vbnv;+char*ret;+inti;++if(xmm->firmware_plp)+vbnv=xmm->firmware_plp->m_header.m_platformVBNV;+elseif(xmm->firmware_blp)+vbnv=xmm->firmware_blp->m_header.m_platformVBNV;+else+returnNULL;++ret=kstrdup(vbnv,GFP_KERNEL);+if(!ret)+returnNULL;++for(i=0;i<strlen(ret);i++){+if(ret[i]==':'||ret[i]=='.')+ret[i]='_';+}+returnret;+}++staticintget_dev_uuid(structplatform_device*pdev,char*uuidstr,size_tlen)+{+charuuid[16];+structplatform_device*devctl_leaf;+structxrt_devctl_ioctl_rwdevctl_arg={0};+interr,i,count;++devctl_leaf=xleaf_get_leaf_by_epname(pdev,XRT_MD_NODE_BLP_ROM);+if(!devctl_leaf){+xrt_err(pdev,"can not get %s",XRT_MD_NODE_BLP_ROM);+return-EINVAL;+}++devctl_arg.xgir_id=XRT_DEVCTL_ROM_UUID;+devctl_arg.xgir_buf=uuid;+devctl_arg.xgir_len=sizeof(uuid);+devctl_arg.xgir_offset=0;+err=xleaf_ioctl(devctl_leaf,XRT_DEVCTL_READ,&devctl_arg);+xleaf_put_leaf(pdev,devctl_leaf);+if(err){+xrt_err(pdev,"can not get uuid: %d",err);+returnerr;+}++for(count=0,i=sizeof(uuid)-sizeof(u32);+i>=0&&len>count;i-=sizeof(u32)){+count+=snprintf(uuidstr+count,len-count,"%08x",*(u32*)&uuid[i]);+}+return0;+}++intxmgmt_hot_reset(structplatform_device*pdev)+{+intret=xleaf_broadcast_event(pdev,XRT_EVENT_PRE_HOT_RESET,false);++if(ret){+xrt_err(pdev,"offline failed, hot reset is canceled");+returnret;+}++xleaf_hot_reset(pdev);+xleaf_broadcast_event(pdev,XRT_EVENT_POST_HOT_RESET,false);+return0;+}++staticssize_treset_store(structdevice*dev,structdevice_attribute*da,+constchar*buf,size_tcount)+{+structplatform_device*pdev=to_platform_device(dev);++xmgmt_hot_reset(pdev);+returncount;+}+staticDEVICE_ATTR_WO(reset);++staticssize_tVBNV_show(structdevice*dev,structdevice_attribute*da,char*buf)+{+ssize_tret;+char*vbnv;+structplatform_device*pdev=to_platform_device(dev);++vbnv=xmgmt_get_vbnv(pdev);+ret=sprintf(buf,"%s\n",vbnv);+kfree(vbnv);+returnret;+}+staticDEVICE_ATTR_RO(VBNV);++staticssize_tlogic_uuids_show(structdevice*dev,structdevice_attribute*da,char*buf)+{+ssize_tret;+charuuid[80];+structplatform_device*pdev=to_platform_device(dev);++/* Getting UUID pointed to by VSEC, should be the same as logic UUID of BLP. */+ret=get_dev_uuid(pdev,uuid,sizeof(uuid));+if(ret)+returnret;+ret=sprintf(buf,"%s\n",uuid);+returnret;+}+staticDEVICE_ATTR_RO(logic_uuids);++staticssize_tinterface_uuids_show(structdevice*dev,structdevice_attribute*da,char*buf)+{+ssize_tret=0;+structplatform_device*pdev=to_platform_device(dev);+structxmgmt_main*xmm=platform_get_drvdata(pdev);+u32i;++for(i=0;i<xmm->blp_intf_uuid_num;i++){+charuuidstr[80];++xrt_md_trans_uuid2str(&xmm->blp_intf_uuids[i],uuidstr);+ret+=sprintf(buf+ret,"%s\n",uuidstr);+}+returnret;+}+staticDEVICE_ATTR_RO(interface_uuids);++staticstructattribute*xmgmt_main_attrs[]={+&dev_attr_reset.attr,+&dev_attr_VBNV.attr,+&dev_attr_logic_uuids.attr,+&dev_attr_interface_uuids.attr,+NULL,+};++/*+*sysfshooktoloadxclbinprimarilyusedfordriverdebug+*/+staticssize_tulp_image_write(structfile*filp,structkobject*kobj,+structbin_attribute*attr,char*buffer,loff_toff,size_tcount)+{+structxmgmt_main*xmm=dev_get_drvdata(container_of(kobj,structdevice,kobj));+structaxlf*xclbin;+ulonglen;++if(off==0){+if(count<sizeof(*xclbin)){+xrt_err(xmm->pdev,"count is too small %zu",count);+return-EINVAL;+}++if(xmm->firmware_ulp){+vfree(xmm->firmware_ulp);+xmm->firmware_ulp=NULL;+}+xclbin=(structaxlf*)buffer;+xmm->firmware_ulp=vmalloc(xclbin->m_header.m_length);+if(!xmm->firmware_ulp)+return-ENOMEM;+}else{+xclbin=xmm->firmware_ulp;+}++len=xclbin->m_header.m_length;+if(off+count>=len&&off<len){+memcpy(xmm->firmware_ulp+off,buffer,len-off);+xmgmt_process_xclbin(xmm->pdev,xmm->fmgr,xmm->firmware_ulp,XMGMT_ULP);+}elseif(off+count<len){+memcpy(xmm->firmware_ulp+off,buffer,count);+}++returncount;+}++staticstructbin_attributeulp_image_attr={+.attr={+.name="ulp_image",+.mode=0200+},+.write=ulp_image_write,+.size=0+};++staticstructbin_attribute*xmgmt_main_bin_attrs[]={+&ulp_image_attr,+NULL,+};++staticconststructattribute_groupxmgmt_main_attrgroup={+.attrs=xmgmt_main_attrs,+.bin_attrs=xmgmt_main_bin_attrs,+};++staticintload_firmware_from_flash(structplatform_device*pdev,structaxlf**fw_buf,size_t*len)+{+return-EOPNOTSUPP;+}++staticintload_firmware_from_disk(structplatform_device*pdev,structaxlf**fw_buf,size_t*len)+{+charuuid[80];+interr=0;+charfw_name[256];+conststructfirmware*fw;++err=get_dev_uuid(pdev,uuid,sizeof(uuid));+if(err)+returnerr;++(void)snprintf(fw_name,sizeof(fw_name),"xilinx/%s/partition.xsabin",uuid);+xrt_info(pdev,"try loading fw: %s",fw_name);++err=request_firmware(&fw,fw_name,DEV(pdev));+if(err)+returnerr;++*fw_buf=vmalloc(fw->size);+*len=fw->size;+if(*fw_buf)+memcpy(*fw_buf,fw->data,fw->size);+else+err=-ENOMEM;++release_firmware(fw);+return0;+}++staticconststructaxlf*xmgmt_get_axlf_firmware(structxmgmt_main*xmm,enumprovider_kindkind)+{+switch(kind){+caseXMGMT_BLP:+returnxmm->firmware_blp;+caseXMGMT_PLP:+returnxmm->firmware_plp;+caseXMGMT_ULP:+returnxmm->firmware_ulp;+default:+xrt_err(xmm->pdev,"unknown axlf kind: %d",kind);+returnNULL;+}+}++char*xmgmt_get_dtb(structplatform_device*pdev,enumprovider_kindkind)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);+char*dtb=NULL;+conststructaxlf*provider=xmgmt_get_axlf_firmware(xmm,kind);+intrc;++if(!provider)+returndtb;++rc=xrt_xclbin_get_metadata(DEV(pdev),provider,&dtb);+if(rc)+xrt_err(pdev,"failed to find dtb: %d",rc);+returndtb;+}++staticconstchar*get_uuid_from_firmware(structplatform_device*pdev,conststructaxlf*xclbin)+{+constvoid*uuid=NULL;+constvoid*uuiddup=NULL;+void*dtb=NULL;+intrc;++rc=xrt_xclbin_get_section(xclbin,PARTITION_METADATA,&dtb,NULL);+if(rc)+returnNULL;++rc=xrt_md_get_prop(DEV(pdev),dtb,NULL,NULL,XRT_MD_PROP_LOGIC_UUID,&uuid,NULL);+if(!rc)+uuiddup=kstrdup(uuid,GFP_KERNEL);+vfree(dtb);+returnuuiddup;+}++staticboolis_valid_firmware(structplatform_device*pdev,+conststructaxlf*xclbin,size_tfw_len)+{+constchar*fw_buf=(constchar*)xclbin;+size_taxlflen=xclbin->m_header.m_length;+constchar*fw_uuid;+chardev_uuid[80];+interr;++err=get_dev_uuid(pdev,dev_uuid,sizeof(dev_uuid));+if(err)+returnfalse;++if(memcmp(fw_buf,ICAP_XCLBIN_V2,sizeof(ICAP_XCLBIN_V2))!=0){+xrt_err(pdev,"unknown fw format");+returnfalse;+}++if(axlflen>fw_len){+xrt_err(pdev,"truncated fw, length: %zu, expect: %zu",fw_len,axlflen);+returnfalse;+}++fw_uuid=get_uuid_from_firmware(pdev,xclbin);+if(!fw_uuid||strcmp(fw_uuid,dev_uuid)!=0){+xrt_err(pdev,"bad fw UUID: %s, expect: %s",+fw_uuid?fw_uuid:"<none>",dev_uuid);+kfree(fw_uuid);+returnfalse;+}++kfree(fw_uuid);+returntrue;+}++intxmgmt_get_provider_uuid(structplatform_device*pdev,enumprovider_kindkind,uuid_t*uuid)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);+conststructaxlf*fwbuf;+constchar*fw_uuid;+intrc=-ENOENT;++mutex_lock(&xmm->busy_mutex);++fwbuf=xmgmt_get_axlf_firmware(xmm,kind);+if(!fwbuf)+gotodone;++fw_uuid=get_uuid_from_firmware(pdev,fwbuf);+if(!fw_uuid)+gotodone;++rc=xrt_md_trans_str2uuid(DEV(pdev),fw_uuid,uuid);+kfree(fw_uuid);++done:+mutex_unlock(&xmm->busy_mutex);+returnrc;+}++staticintxmgmt_create_blp(structxmgmt_main*xmm)+{+structplatform_device*pdev=xmm->pdev;+intrc=0;+char*dtb=NULL;+conststructaxlf*provider=xmgmt_get_axlf_firmware(xmm,XMGMT_BLP);++dtb=xmgmt_get_dtb(pdev,XMGMT_BLP);+if(dtb){+rc=xmgmt_process_xclbin(xmm->pdev,xmm->fmgr,provider,XMGMT_BLP);+if(rc){+xrt_err(pdev,"failed to process BLP: %d",rc);+gotofailed;+}++rc=xleaf_create_group(pdev,dtb);+if(rc<0)+xrt_err(pdev,"failed to create BLP group: %d",rc);+else+rc=0;++WARN_ON(xmm->blp_intf_uuids);+xrt_md_get_intf_uuids(&pdev->dev,dtb,&xmm->blp_intf_uuid_num,NULL);+if(xmm->blp_intf_uuid_num>0){+xmm->blp_intf_uuids=vzalloc(sizeof(uuid_t)*xmm->blp_intf_uuid_num);+xrt_md_get_intf_uuids(&pdev->dev,dtb,&xmm->blp_intf_uuid_num,+xmm->blp_intf_uuids);+}+}++failed:+vfree(dtb);+returnrc;+}++staticintxmgmt_load_firmware(structxmgmt_main*xmm)+{+structplatform_device*pdev=xmm->pdev;+intrc;+size_tfwlen;++rc=load_firmware_from_disk(pdev,&xmm->firmware_blp,&fwlen);+if(rc!=0)+rc=load_firmware_from_flash(pdev,&xmm->firmware_blp,&fwlen);+if(rc==0&&is_valid_firmware(pdev,xmm->firmware_blp,fwlen))+(void)xmgmt_create_blp(xmm);+else+xrt_err(pdev,"failed to find firmware, giving up: %d",rc);+returnrc;+}++staticvoidxmgmt_main_event_cb(structplatform_device*pdev,void*arg)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);+structxrt_event*evt=(structxrt_event*)arg;+enumxrt_eventse=evt->xe_evt;+enumxrt_subdev_idid=evt->xe_subdev.xevt_subdev_id;+structplatform_device*leaf;++switch(e){+caseXRT_EVENT_POST_CREATION:{+if(id==XRT_SUBDEV_DEVCTL&&!xmm->devctl_ready){+leaf=xleaf_get_leaf_by_epname(pdev,XRT_MD_NODE_BLP_ROM);+if(leaf){+xmm->devctl_ready=true;+xleaf_put_leaf(pdev,leaf);+}+}elseif(id==XRT_SUBDEV_QSPI&&!xmm->flash_ready){+xmm->flash_ready=true;+}else{+break;+}++if(xmm->devctl_ready)+(void)xmgmt_load_firmware(xmm);+break;+}+caseXRT_EVENT_PRE_REMOVAL:+break;+default:+xrt_dbg(pdev,"ignored event %d",e);+break;+}+}++staticintxmgmt_main_probe(structplatform_device*pdev)+{+structxmgmt_main*xmm;++xrt_info(pdev,"probing...");++xmm=devm_kzalloc(DEV(pdev),sizeof(*xmm),GFP_KERNEL);+if(!xmm)+return-ENOMEM;++xmm->pdev=pdev;+xmm->fmgr=xmgmt_fmgr_probe(pdev);+if(IS_ERR(xmm->fmgr))+returnPTR_ERR(xmm->fmgr);++platform_set_drvdata(pdev,xmm);+mutex_init(&xmm->busy_mutex);++/* Ready to handle req thru sysfs nodes. */+if(sysfs_create_group(&DEV(pdev)->kobj,&xmgmt_main_attrgroup))+xrt_err(pdev,"failed to create sysfs group");+return0;+}++staticintxmgmt_main_remove(structplatform_device*pdev)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);++/* By now, group driver should prevent any inter-leaf call. */++xrt_info(pdev,"leaving...");++vfree(xmm->blp_intf_uuids);+vfree(xmm->firmware_blp);+vfree(xmm->firmware_plp);+vfree(xmm->firmware_ulp);+xmgmt_region_cleanup_all(pdev);+(void)xmgmt_fmgr_remove(xmm->fmgr);+(void)sysfs_remove_group(&DEV(pdev)->kobj,&xmgmt_main_attrgroup);+return0;+}++staticint+xmgmt_main_leaf_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+structxmgmt_main*xmm=platform_get_drvdata(pdev);+intret=0;++switch(cmd){+caseXRT_XLEAF_EVENT:+xmgmt_main_event_cb(pdev,arg);+break;+caseXRT_MGMT_MAIN_GET_AXLF_SECTION:{+structxrt_mgmt_main_ioctl_get_axlf_section*get=+(structxrt_mgmt_main_ioctl_get_axlf_section*)arg;+conststructaxlf*firmware=xmgmt_get_axlf_firmware(xmm,get->xmmigas_axlf_kind);++if(!firmware){+ret=-ENOENT;+}else{+ret=xrt_xclbin_get_section(firmware,get->xmmigas_section_kind,+&get->xmmigas_section,+&get->xmmigas_section_size);+}+break;+}+caseXRT_MGMT_MAIN_GET_VBNV:{+char**vbnv_p=(char**)arg;++*vbnv_p=xmgmt_get_vbnv(pdev);+break;+}+default:+xrt_err(pdev,"unknown cmd: %d",cmd);+ret=-EINVAL;+break;+}+returnret;+}++staticintxmgmt_main_open(structinode*inode,structfile*file)+{+structplatform_device*pdev=xleaf_devnode_open(inode);++/* Device may have gone already when we get here. */+if(!pdev)+return-ENODEV;++xrt_info(pdev,"opened");+file->private_data=platform_get_drvdata(pdev);+return0;+}++staticintxmgmt_main_close(structinode*inode,structfile*file)+{+structxmgmt_main*xmm=file->private_data;++xleaf_devnode_close(inode);++xrt_info(xmm->pdev,"closed");+return0;+}++/*+*Calledforxclbindownloadxclbinloadioctl.+*/+staticintxmgmt_bitstream_axlf_fpga_mgr(structxmgmt_main*xmm,void*axlf,size_tsize)+{+intret;++WARN_ON(!mutex_is_locked(&xmm->busy_mutex));++/*+*Shouldanyerrorhappensduringdownload,wecan'ttrust+*thecachedxclbinanymore.+*/+vfree(xmm->firmware_ulp);+xmm->firmware_ulp=NULL;++ret=xmgmt_process_xclbin(xmm->pdev,xmm->fmgr,axlf,XMGMT_ULP);+if(ret==0)+xmm->firmware_ulp=axlf;++returnret;+}++staticintbitstream_axlf_ioctl(structxmgmt_main*xmm,constvoid__user*arg)+{+void*copy_buffer=NULL;+size_tcopy_buffer_size=0;+structxmgmt_ioc_bitstream_axlfioc_obj={0};+structaxlfxclbin_obj={{0}};+intret=0;++if(copy_from_user((void*)&ioc_obj,arg,sizeof(ioc_obj)))+return-EFAULT;+if(copy_from_user((void*)&xclbin_obj,ioc_obj.xclbin,sizeof(xclbin_obj)))+return-EFAULT;+if(memcmp(xclbin_obj.m_magic,ICAP_XCLBIN_V2,sizeof(ICAP_XCLBIN_V2)))+return-EINVAL;++copy_buffer_size=xclbin_obj.m_header.m_length;+if(copy_buffer_size>MAX_XCLBIN_SIZE)+return-EINVAL;+copy_buffer=vmalloc(copy_buffer_size);+if(!copy_buffer)+return-ENOMEM;++if(copy_from_user(copy_buffer,ioc_obj.xclbin,copy_buffer_size)){+vfree(copy_buffer);+return-EFAULT;+}++ret=xmgmt_bitstream_axlf_fpga_mgr(xmm,copy_buffer,copy_buffer_size);+if(ret)+vfree(copy_buffer);++returnret;+}++staticlongxmgmt_main_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg)+{+longresult=0;+structxmgmt_main*xmm=filp->private_data;++if(_IOC_TYPE(cmd)!=XMGMT_IOC_MAGIC)+return-ENOTTY;++mutex_lock(&xmm->busy_mutex);++xrt_info(xmm->pdev,"ioctl cmd %d, arg %ld",cmd,arg);+switch(cmd){+caseXMGMT_IOCICAPDOWNLOAD_AXLF:+result=bitstream_axlf_ioctl(xmm,(constvoid__user*)arg);+break;+default:+result=-ENOTTY;+break;+}++mutex_unlock(&xmm->busy_mutex);+returnresult;+}++staticstructxrt_subdev_endpointsxrt_mgmt_main_endpoints[]={+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name=XRT_MD_NODE_MGMT_MAIN},+{NULL},+},+.xse_min_ep=1,+},+{0},+};++staticstructxrt_subdev_drvdataxmgmt_main_data={+.xsd_dev_ops={+.xsd_ioctl=xmgmt_main_leaf_ioctl,+},+.xsd_file_ops={+.xsf_ops={+.owner=THIS_MODULE,+.open=xmgmt_main_open,+.release=xmgmt_main_close,+.unlocked_ioctl=xmgmt_main_ioctl,+},+.xsf_dev_name="xmgmt",+},+};++staticconststructplatform_device_idxmgmt_main_id_table[]={+{XMGMT_MAIN,(kernel_ulong_t)&xmgmt_main_data},+{},+};++staticstructplatform_driverxmgmt_main_driver={+.driver={+.name=XMGMT_MAIN,+},+.probe=xmgmt_main_probe,+.remove=xmgmt_main_remove,+.id_table=xmgmt_main_id_table,+};++intxmgmt_main_register_leaf(void)+{+returnxleaf_register_driver(XRT_SUBDEV_MGMT_MAIN,+&xmgmt_main_driver,xrt_mgmt_main_endpoints);+}++voidxmgmt_main_unregister_leaf(void)+{+xleaf_unregister_driver(XRT_SUBDEV_MGMT_MAIN);+}
@@ -0,0 +1,43 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*HeaderfileforXilinxRuntime(XRT)driver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#ifndef _XRT_SUBDEV_ID_H_+#define _XRT_SUBDEV_ID_H_++/*+*EverysubdevdrivershouldhaveanIDforotherstorefertoit.+*Therecanbeunlimitednumberofinstancesofasubdevdriver.A+*<subdev_id,subdev_instance>tupleshouldbeauniqueidentificationof+*aspecificinstanceofasubdevdriver.+*NOTE:PLEASEdonotchangetheorderofIDs.Subdevicesinthesame+*groupareinitializedbythisorder.+*/+enumxrt_subdev_id{+XRT_SUBDEV_GRP=0,+XRT_SUBDEV_VSEC=1,+XRT_SUBDEV_VSEC_GOLDEN=2,+XRT_SUBDEV_DEVCTL=3,+XRT_SUBDEV_AXIGATE=4,+XRT_SUBDEV_ICAP=5,+XRT_SUBDEV_TEST=6,+XRT_SUBDEV_MGMT_MAIN=7,+XRT_SUBDEV_QSPI=8,+XRT_SUBDEV_MAILBOX=9,+XRT_SUBDEV_CMC=10,+XRT_SUBDEV_CALIB=11,+XRT_SUBDEV_CLKFREQ=12,+XRT_SUBDEV_CLOCK=13,+XRT_SUBDEV_SRSR=14,+XRT_SUBDEV_UCS=15,+XRT_SUBDEV_NUM=16,/* Total number of subdevs. */+XRT_ROOT=-1,/* Special ID for root driver. */+};++#endif /* _XRT_SUBDEV_ID_H_ */
@@ -0,0 +1,276 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*HeaderfileforXilinxRuntime(XRT)driver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*SonalSantan<sonal.santan@xilinx.com>+*/++#ifndef _XRT_XLEAF_H_+#define _XRT_XLEAF_H_++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/fs.h>+#include<linux/cdev.h>+#include<linux/pci.h>+#include<linux/libfdt_env.h>+#include"libfdt.h"+#include"subdev_id.h"+#include"xroot.h"+#include"events.h"++/* All subdev drivers should use below common routines to print out msg. */+#define DEV(pdev) (&(pdev)->dev)+#define DEV_PDATA(pdev) \+((structxrt_subdev_platdata*)dev_get_platdata(DEV(pdev)))+#define DEV_DRVDATA(pdev) \+((structxrt_subdev_drvdata*)\+platform_get_device_id(pdev)->driver_data)+#define FMT_PRT(prt_fn, pdev, fmt, args...) \+({typeof(pdev)(_pdev)=(pdev);\+prt_fn(DEV(_pdev),"%s %s: "fmt,\+DEV_PDATA(_pdev)->xsp_root_name,__func__,##args);})+#define xrt_err(pdev, fmt, args...) FMT_PRT(dev_err, pdev, fmt, ##args)+#define xrt_warn(pdev, fmt, args...) FMT_PRT(dev_warn, pdev, fmt, ##args)+#define xrt_info(pdev, fmt, args...) FMT_PRT(dev_info, pdev, fmt, ##args)+#define xrt_dbg(pdev, fmt, args...) FMT_PRT(dev_dbg, pdev, fmt, ##args)++/* Starting IOCTL for common IOCTLs implemented by all leaves. */+#define XRT_XLEAF_COMMON_BASE 0+/* Starting IOCTL for leaves' specific IOCTLs. */+#define XRT_XLEAF_CUSTOM_BASE 64+enumxrt_xleaf_common_ioctl_cmd{+XRT_XLEAF_EVENT=XRT_XLEAF_COMMON_BASE,+};++/*+*Ifpopulatedbysubdevdriver,infrawillhandlethemechanicsof+*chardevice(un)registration.+*/+enumxrt_subdev_file_mode{+/* Infra create cdev, default file name */+XRT_SUBDEV_FILE_DEFAULT=0,+/* Infra create cdev, need to encode inst num in file name */+XRT_SUBDEV_FILE_MULTI_INST,+/* No auto creation of cdev by infra, leaf handles it by itself */+XRT_SUBDEV_FILE_NO_AUTO,+};++structxrt_subdev_file_ops{+conststructfile_operationsxsf_ops;+dev_txsf_dev_t;+constchar*xsf_dev_name;+enumxrt_subdev_file_modexsf_mode;+};++/*+*Subdevdrivercallbackspopulatedbysubdevdriver.+*/+structxrt_subdev_drv_ops{+/*+*Perdriverinstancecallback.Thepdevpointstotheinstance.+*Ifdefinedthesearecalledbyotherleafdrivers.+*Notethatrootdrivermaycallintoxsd_ioctlofagroupdriver.+*/+int(*xsd_ioctl)(structplatform_device*pdev,u32cmd,void*arg);+};++/*+*Definedandpopulatedbysubdevdriver,exportedasdriver_datain+*structplatform_device_id.+*/+structxrt_subdev_drvdata{+structxrt_subdev_file_opsxsd_file_ops;+structxrt_subdev_drv_opsxsd_dev_ops;+};++/*+*Partiallyinitializedbytheparentdriver,then,passedinassubdevdriver's+*platformdatawhencreatingsubdevdriverinstancebycallingplatform+*deviceregisterAPI(platform_device_register_data()orthelikes).+*+*OncedeviceregisterAPIreturns,platformdriverframeworkmakesacopyof+*thisbufferandmaintainsitslifecycle.Thecontentofthebufferis+*completelyownedbysubdevdriver.+*+*Thus,parentdrivershouldbeverycarefulwhenittouchesthisbuffer+*againonceit'shandedovertosubdevdriver.Andthedatastructure+*shouldnotcontainpointerspointingtobuffersthatismanagedby+*otherorparentdriverssinceitcouldhavebeenfreedbeforeplatform+*databufferisfreedbyplatformdriverframework.+*/+structxrt_subdev_platdata{+/*+*Perdriverinstancecallback.Thepdevpointstotheinstance.+*Shouldalwaysbedefinedforsubdevdrivertogetservicefromroot.+*/+xrt_subdev_root_cb_txsp_root_cb;+void*xsp_root_cb_arg;++/* Something to associate w/ root for msg printing. */+constchar*xsp_root_name;++/*+*Chardevsupportforthissubdevinstance.+*Initializedbysubdevdriver.+*/+structcdevxsp_cdev;+structdevice*xsp_sysdev;+structmutexxsp_devnode_lock;/* devnode lock */+structcompletionxsp_devnode_comp;+intxsp_devnode_ref;+boolxsp_devnode_online;+boolxsp_devnode_excl;++/*+*Subdevdriverspecificinitdata.Thebuffershouldbeembedded+*inthisdatastructurebufferafterdtb,sothatitcanbefreed+*togetherwithplatformdata.+*/+loff_txsp_priv_off;/* Offset into this platform data buffer. */+size_txsp_priv_len;++/*+*Populatedbyparentdrivertodescribethedevicetreefor+*thesubdevdrivertohandle.Shouldalwaysbelastonesinceit's+*ofvariablelength.+*/+charxsp_dtb[sizeof(structfdt_header)];+};++/*+*thisstructdefinetheendpointsbelongtothesamesubdevice+*/+structxrt_subdev_ep_names{+constchar*ep_name;+constchar*regmap_name;+};++structxrt_subdev_endpoints{+structxrt_subdev_ep_names*xse_names;+/* minimum number of endpoints to support the subdevice */+u32xse_min_ep;+};++structsubdev_match_arg{+enumxrt_subdev_idid;+intinstance;+};++boolxleaf_has_endpoint(structplatform_device*pdev,constchar*endpoint_name);+structplatform_device*xleaf_get_leaf(structplatform_device*pdev,+xrt_subdev_match_tcb,void*arg);++staticinlineboolsubdev_match(enumxrt_subdev_idid,structplatform_device*pdev,void*arg)+{+conststructsubdev_match_arg*a=(structsubdev_match_arg*)arg;+boolret=(id==a->id&&(pdev->id==a->instance||PLATFORM_DEVID_NONE==a->instance));++returnret;+}++staticinlineboolxrt_subdev_match_epname(enumxrt_subdev_idid,+structplatform_device*pdev,void*arg)+{+returnxleaf_has_endpoint(pdev,arg);+}++staticinlinestructplatform_device*+xleaf_get_leaf_by_id(structplatform_device*pdev,+enumxrt_subdev_idid,intinstance)+{+structsubdev_match_argarg={id,instance};++returnxleaf_get_leaf(pdev,subdev_match,&arg);+}++staticinlinestructplatform_device*+xleaf_get_leaf_by_epname(structplatform_device*pdev,constchar*name)+{+returnxleaf_get_leaf(pdev,xrt_subdev_match_epname,(void*)name);+}++staticinlineintxleaf_ioctl(structplatform_device*tgt,u32cmd,void*arg)+{+structxrt_subdev_drvdata*drvdata=DEV_DRVDATA(tgt);++return(*drvdata->xsd_dev_ops.xsd_ioctl)(tgt,cmd,arg);+}++intxleaf_put_leaf(structplatform_device*pdev,+structplatform_device*leaf);+intxleaf_create_group(structplatform_device*pdev,char*dtb);+intxleaf_destroy_group(structplatform_device*pdev,intinstance);+intxleaf_wait_for_group_bringup(structplatform_device*pdev);+voidxleaf_hot_reset(structplatform_device*pdev);+intxleaf_broadcast_event(structplatform_device*pdev,+enumxrt_eventsevt,boolasync);+voidxleaf_get_barres(structplatform_device*pdev,+structresource**res,uintbar_idx);+voidxleaf_get_root_id(structplatform_device*pdev,+unsignedshort*vendor,unsignedshort*device,+unsignedshort*subvendor,unsignedshort*subdevice);+structdevice*xleaf_register_hwmon(structplatform_device*pdev,+constchar*name,void*drvdata,+conststructattribute_group**grps);+voidxleaf_unregister_hwmon(structplatform_device*pdev,structdevice*hwmon);++/*+*CharacterdevicehelperAPIsforusebyleafdrivers+*/+staticinlineboolxleaf_devnode_enabled(structxrt_subdev_drvdata*drvdata)+{+returndrvdata&&drvdata->xsd_file_ops.xsf_ops.open;+}++intxleaf_devnode_create(structplatform_device*pdev,+constchar*file_name,constchar*inst_name);+intxleaf_devnode_destroy(structplatform_device*pdev);++structplatform_device*xleaf_devnode_open_excl(structinode*inode);+structplatform_device*xleaf_devnode_open(structinode*inode);+voidxleaf_devnode_close(structinode*inode);++/* Helpers. */+staticinlinevoidxrt_memcpy_fromio(void*buf,void__iomem*iomem,u32size)+{+inti;++WARN_ON(size&0x3);+for(i=0;i<size/4;i++)+((u32*)buf)[i]=ioread32((char*)(iomem)+sizeof(u32)*i);+}++staticinlinevoidxrt_memcpy_toio(void__iomem*iomem,void*buf,u32size)+{+inti;++WARN_ON(size&0x3);+for(i=0;i<size/4;i++)+iowrite32(((u32*)buf)[i],((char*)(iomem)+sizeof(u32)*i));+}++intxleaf_register_driver(enumxrt_subdev_idid,structplatform_driver*drv,+structxrt_subdev_endpoints*eps);+voidxleaf_unregister_driver(enumxrt_subdev_idid);++/* Module's init/fini routines for leaf driver in xrt-lib module */+voidgroup_leaf_init_fini(boolinit);+voidvsec_leaf_init_fini(boolinit);+voidvsec_golden_leaf_init_fini(boolinit);+voiddevctl_leaf_init_fini(boolinit);+voidaxigate_leaf_init_fini(boolinit);+voidicap_leaf_init_fini(boolinit);+voidcalib_leaf_init_fini(boolinit);+voidqspi_leaf_init_fini(boolinit);+voidmailbox_leaf_init_fini(boolinit);+voidcmc_leaf_init_fini(boolinit);+voidclkfreq_leaf_init_fini(boolinit);+voidclock_leaf_init_fini(boolinit);+voiducs_leaf_init_fini(boolinit);++#endif /* _XRT_LEAF_H_ */
@@ -0,0 +1,231 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAdevicenodehelperfunctions.+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#include"xleaf.h"++externstructclass*xrt_class;++#define XRT_CDEV_DIR "xfpga"+#define INODE2PDATA(inode) \+container_of((inode)->i_cdev,structxrt_subdev_platdata,xsp_cdev)+#define INODE2PDEV(inode) \+to_platform_device(kobj_to_dev((inode)->i_cdev->kobj.parent))+#define CDEV_NAME(sysdev) (strchr((sysdev)->kobj.name, '!') + 1)++/* Allow it to be accessed from cdev. */+staticvoidxleaf_devnode_allowed(structplatform_device*pdev)+{+structxrt_subdev_platdata*pdata=DEV_PDATA(pdev);++/* Allow new opens. */+mutex_lock(&pdata->xsp_devnode_lock);+pdata->xsp_devnode_online=true;+mutex_unlock(&pdata->xsp_devnode_lock);+}++/* Turn off access from cdev and wait for all existing user to go away. */+staticintxleaf_devnode_disallowed(structplatform_device*pdev)+{+intret=0;+structxrt_subdev_platdata*pdata=DEV_PDATA(pdev);++mutex_lock(&pdata->xsp_devnode_lock);++/* Prevent new opens. */+pdata->xsp_devnode_online=false;+/* Wait for existing user to close. */+while(!ret&&pdata->xsp_devnode_ref){+intrc;++mutex_unlock(&pdata->xsp_devnode_lock);+rc=wait_for_completion_killable(&pdata->xsp_devnode_comp);+mutex_lock(&pdata->xsp_devnode_lock);++if(rc==-ERESTARTSYS){+/* Restore online state. */+pdata->xsp_devnode_online=true;+xrt_err(pdev,"%s is in use, ref=%d",+CDEV_NAME(pdata->xsp_sysdev),+pdata->xsp_devnode_ref);+ret=-EBUSY;+}+}++mutex_unlock(&pdata->xsp_devnode_lock);++returnret;+}++staticstructplatform_device*+__xleaf_devnode_open(structinode*inode,boolexcl)+{+structxrt_subdev_platdata*pdata=INODE2PDATA(inode);+structplatform_device*pdev=INODE2PDEV(inode);+boolopened=false;++mutex_lock(&pdata->xsp_devnode_lock);++if(pdata->xsp_devnode_online){+if(excl&&pdata->xsp_devnode_ref){+xrt_err(pdev,"%s has already been opened exclusively",+CDEV_NAME(pdata->xsp_sysdev));+}elseif(!excl&&pdata->xsp_devnode_excl){+xrt_err(pdev,"%s has been opened exclusively",+CDEV_NAME(pdata->xsp_sysdev));+}else{+pdata->xsp_devnode_ref++;+pdata->xsp_devnode_excl=excl;+opened=true;+xrt_info(pdev,"opened %s, ref=%d",+CDEV_NAME(pdata->xsp_sysdev),+pdata->xsp_devnode_ref);+}+}else{+xrt_err(pdev,"%s is offline",CDEV_NAME(pdata->xsp_sysdev));+}++mutex_unlock(&pdata->xsp_devnode_lock);++pdev=opened?pdev:NULL;+returnpdev;+}++structplatform_device*+xleaf_devnode_open_excl(structinode*inode)+{+return__xleaf_devnode_open(inode,true);+}++structplatform_device*+xleaf_devnode_open(structinode*inode)+{+return__xleaf_devnode_open(inode,false);+}+EXPORT_SYMBOL_GPL(xleaf_devnode_open);++voidxleaf_devnode_close(structinode*inode)+{+structxrt_subdev_platdata*pdata=INODE2PDATA(inode);+structplatform_device*pdev=INODE2PDEV(inode);+boolnotify=false;++mutex_lock(&pdata->xsp_devnode_lock);++pdata->xsp_devnode_ref--;+if(pdata->xsp_devnode_ref==0){+pdata->xsp_devnode_excl=false;+notify=true;+}+if(notify){+xrt_info(pdev,"closed %s, ref=%d",+CDEV_NAME(pdata->xsp_sysdev),pdata->xsp_devnode_ref);+}else{+xrt_info(pdev,"closed %s, notifying waiter",+CDEV_NAME(pdata->xsp_sysdev));+}++mutex_unlock(&pdata->xsp_devnode_lock);++if(notify)+complete(&pdata->xsp_devnode_comp);+}+EXPORT_SYMBOL_GPL(xleaf_devnode_close);++staticinlineenumxrt_subdev_file_mode+devnode_mode(structxrt_subdev_drvdata*drvdata)+{+returndrvdata->xsd_file_ops.xsf_mode;+}++intxleaf_devnode_create(structplatform_device*pdev,constchar*file_name,+constchar*inst_name)+{+structxrt_subdev_drvdata*drvdata=DEV_DRVDATA(pdev);+structxrt_subdev_file_ops*fops=&drvdata->xsd_file_ops;+structxrt_subdev_platdata*pdata=DEV_PDATA(pdev);+structcdev*cdevp;+structdevice*sysdev;+intret=0;+charfname[256];++mutex_init(&pdata->xsp_devnode_lock);+init_completion(&pdata->xsp_devnode_comp);++cdevp=&DEV_PDATA(pdev)->xsp_cdev;+cdev_init(cdevp,&fops->xsf_ops);+cdevp->owner=fops->xsf_ops.owner;+cdevp->dev=MKDEV(MAJOR(fops->xsf_dev_t),pdev->id);++/*+*Setpdevasparentofcdevsothatwhenpdev(anditsplatform+*data)willnotbefreedwhencdevisnotfreed.+*/+cdev_set_parent(cdevp,&DEV(pdev)->kobj);++ret=cdev_add(cdevp,cdevp->dev,1);+if(ret){+xrt_err(pdev,"failed to add cdev: %d",ret);+gotofailed;+}+if(!file_name)+file_name=pdev->name;+if(!inst_name){+if(devnode_mode(drvdata)==XRT_SUBDEV_FILE_MULTI_INST){+snprintf(fname,sizeof(fname),"%s/%s/%s.%u",+XRT_CDEV_DIR,DEV_PDATA(pdev)->xsp_root_name,+file_name,pdev->id);+}else{+snprintf(fname,sizeof(fname),"%s/%s/%s",+XRT_CDEV_DIR,DEV_PDATA(pdev)->xsp_root_name,+file_name);+}+}else{+snprintf(fname,sizeof(fname),"%s/%s/%s.%s",XRT_CDEV_DIR,+DEV_PDATA(pdev)->xsp_root_name,file_name,inst_name);+}+sysdev=device_create(xrt_class,NULL,cdevp->dev,NULL,"%s",fname);+if(IS_ERR(sysdev)){+ret=PTR_ERR(sysdev);+xrt_err(pdev,"failed to create device node: %d",ret);+gotofailed;+}+pdata->xsp_sysdev=sysdev;++xleaf_devnode_allowed(pdev);++xrt_info(pdev,"created (%d, %d): /dev/%s",+MAJOR(cdevp->dev),pdev->id,fname);+return0;++failed:+device_destroy(xrt_class,cdevp->dev);+cdev_del(cdevp);+cdevp->owner=NULL;+returnret;+}++intxleaf_devnode_destroy(structplatform_device*pdev)+{+structxrt_subdev_platdata*pdata=DEV_PDATA(pdev);+structcdev*cdevp=&pdata->xsp_cdev;+dev_tdev=cdevp->dev;+intrc;++rc=xleaf_devnode_disallowed(pdev);+if(rc)+returnrc;++xrt_info(pdev,"removed (%d, %d): /dev/%s/%s",MAJOR(dev),MINOR(dev),+XRT_CDEV_DIR,CDEV_NAME(pdata->xsp_sysdev));+device_destroy(xrt_class,cdevp->dev);+pdata->xsp_sysdev=NULL;+cdev_del(cdevp);+return0;+}
@@ -0,0 +1,871 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAdevicehelperfunctions+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#include<linux/platform_device.h>+#include<linux/pci.h>+#include<linux/vmalloc.h>+#include"xleaf.h"+#include"subdev_pool.h"+#include"main.h"+#include"metadata.h"++#define DEV_IS_PCI(dev) ((dev)->bus == &pci_bus_type)+staticinlinestructdevice*find_root(structplatform_device*pdev)+{+structdevice*d=DEV(pdev);++while(!DEV_IS_PCI(d))+d=d->parent;+returnd;+}++/*+*Itrepresentsaholderofasubdev.Oneholdercanrepeatedlyholdasubdev+*aslongasthereisaunholdcorrespondingtoahold.+*/+structxrt_subdev_holder{+structlist_headxsh_holder_list;+structdevice*xsh_holder;+intxsh_count;+structkrefxsh_kref;+};++/*+*Itrepresentsaspecificinstanceofplatformdriverforasubdev,which+*providesservicestoitsclients(anothersubdevdriverorrootdriver).+*/+structxrt_subdev{+structlist_headxs_dev_list;+structlist_headxs_holder_list;+enumxrt_subdev_idxs_id;/* type of subdev */+structplatform_device*xs_pdev;/* a particular subdev inst */+structcompletionxs_holder_comp;+};++staticstructxrt_subdev*xrt_subdev_alloc(void)+{+structxrt_subdev*sdev=vzalloc(sizeof(*sdev));++if(!sdev)+returnNULL;++INIT_LIST_HEAD(&sdev->xs_dev_list);+INIT_LIST_HEAD(&sdev->xs_holder_list);+init_completion(&sdev->xs_holder_comp);+returnsdev;+}++staticvoidxrt_subdev_free(structxrt_subdev*sdev)+{+vfree(sdev);+}++intxrt_subdev_root_request(structplatform_device*self,u32cmd,void*arg)+{+structdevice*dev=DEV(self);+structxrt_subdev_platdata*pdata=DEV_PDATA(self);++return(*pdata->xsp_root_cb)(dev->parent,pdata->xsp_root_cb_arg,cmd,arg);+}++/*+*Subdevcommonsysfsnodes.+*/+staticssize_tholders_show(structdevice*dev,structdevice_attribute*attr,char*buf)+{+ssize_tlen;+structplatform_device*pdev=to_platform_device(dev);+structxrt_root_ioctl_get_holdersholders={pdev,buf,1024};++len=xrt_subdev_root_request(pdev,XRT_ROOT_GET_LEAF_HOLDERS,&holders);+if(len>=holders.xpigh_holder_buf_len)+returnlen;+buf[len]='\n';+returnlen+1;+}+staticDEVICE_ATTR_RO(holders);++staticstructattribute*xrt_subdev_attrs[]={+&dev_attr_holders.attr,+NULL,+};++staticssize_tmetadata_output(structfile*filp,structkobject*kobj,+structbin_attribute*attr,char*buf,loff_toff,size_tcount)+{+structdevice*dev=kobj_to_dev(kobj);+structplatform_device*pdev=to_platform_device(dev);+structxrt_subdev_platdata*pdata=DEV_PDATA(pdev);+unsignedchar*blob;+unsignedlongsize;+ssize_tret=0;++blob=pdata->xsp_dtb;+size=xrt_md_size(dev,blob);+if(size==XRT_MD_INVALID_LENGTH){+ret=-EINVAL;+gotofailed;+}++if(off>=size)+gotofailed;++if(off+count>size)+count=size-off;+memcpy(buf,blob+off,count);++ret=count;+failed:+returnret;+}++staticstructbin_attributemeta_data_attr={+.attr={+.name="metadata",+.mode=0400+},+.read=metadata_output,+.size=0+};++staticstructbin_attribute*xrt_subdev_bin_attrs[]={+&meta_data_attr,+NULL,+};++staticconststructattribute_groupxrt_subdev_attrgroup={+.attrs=xrt_subdev_attrs,+.bin_attrs=xrt_subdev_bin_attrs,+};++/*+*Giventhedevicemetadata,parseittogetIOrangesandconstruct+*resourcearray.+*/+staticint+xrt_subdev_getres(structdevice*parent,enumxrt_subdev_idid,+char*dtb,structresource**res,int*res_num)+{+structxrt_subdev_platdata*pdata;+structresource*pci_res=NULL;+constu64*bar_range;+constu32*bar_idx;+char*ep_name=NULL,*regmap=NULL;+uintbar;+intcount1=0,count2=0,ret;++if(!dtb)+return-EINVAL;++pdata=DEV_PDATA(to_platform_device(parent));++/* go through metadata and count endpoints in it */+for(xrt_md_get_next_endpoint(parent,dtb,NULL,NULL,&ep_name,®map);ep_name;+xrt_md_get_next_endpoint(parent,dtb,ep_name,regmap,&ep_name,®map)){+ret=xrt_md_get_prop(parent,dtb,ep_name,regmap,+XRT_MD_PROP_IO_OFFSET,(constvoid**)&bar_range,NULL);+if(!ret)+count1++;+}+if(!count1)+return0;++/* allocate resource array for all endpoints been found in metadata */+*res=vzalloc(sizeof(**res)*count1);++/* go through all endpoints again and get IO range for each endpoint */+for(xrt_md_get_next_endpoint(parent,dtb,NULL,NULL,&ep_name,®map);ep_name;+xrt_md_get_next_endpoint(parent,dtb,ep_name,regmap,&ep_name,®map)){+ret=xrt_md_get_prop(parent,dtb,ep_name,regmap,+XRT_MD_PROP_IO_OFFSET,(constvoid**)&bar_range,NULL);+if(ret)+continue;+xrt_md_get_prop(parent,dtb,ep_name,regmap,+XRT_MD_PROP_BAR_IDX,(constvoid**)&bar_idx,NULL);+bar=bar_idx?be32_to_cpu(*bar_idx):0;+xleaf_get_barres(to_platform_device(parent),&pci_res,bar);+(*res)[count2].start=pci_res->start++be64_to_cpu(bar_range[0]);+(*res)[count2].end=pci_res->start++be64_to_cpu(bar_range[0])++be64_to_cpu(bar_range[1])-1;+(*res)[count2].flags=IORESOURCE_MEM;+/* check if there is conflicted resource */+ret=request_resource(pci_res,*res+count2);+if(ret){+dev_err(parent,"Conflict resource %pR\n",*res+count2);+vfree(*res);+*res_num=0;+*res=NULL;+returnret;+}+release_resource(*res+count2);++(*res)[count2].parent=pci_res;++xrt_md_find_endpoint(parent,pdata->xsp_dtb,ep_name,+regmap,&(*res)[count2].name);++count2++;+}++WARN_ON(count1!=count2);+*res_num=count2;++return0;+}++staticinlineenumxrt_subdev_file_mode+xleaf_devnode_mode(structxrt_subdev_drvdata*drvdata)+{+returndrvdata->xsd_file_ops.xsf_mode;+}++staticboolxrt_subdev_cdev_auto_creation(structplatform_device*pdev)+{+structxrt_subdev_drvdata*drvdata=DEV_DRVDATA(pdev);++if(!drvdata)+returnfalse;++returnxleaf_devnode_enabled(drvdata)&&+(xleaf_devnode_mode(drvdata)==XRT_SUBDEV_FILE_DEFAULT||+(xleaf_devnode_mode(drvdata)==XRT_SUBDEV_FILE_MULTI_INST));+}++staticstructxrt_subdev*+xrt_subdev_create(structdevice*parent,enumxrt_subdev_idid,+xrt_subdev_root_cb_tpcb,void*pcb_arg,char*dtb)+{+structxrt_subdev*sdev=NULL;+structplatform_device*pdev=NULL;+structxrt_subdev_platdata*pdata=NULL;+unsignedlongdtb_len=0;+size_tpdata_sz;+intinst=PLATFORM_DEVID_NONE;+structresource*res=NULL;+intres_num=0;++sdev=xrt_subdev_alloc();+if(!sdev){+dev_err(parent,"failed to alloc subdev for ID %d",id);+gotofail;+}+sdev->xs_id=id;++if(dtb){+xrt_md_pack(parent,dtb);+dtb_len=xrt_md_size(parent,dtb);+if(dtb_len==XRT_MD_INVALID_LENGTH){+dev_err(parent,"invalid metadata len %ld",dtb_len);+gotofail;+}+}+pdata_sz=sizeof(structxrt_subdev_platdata)+dtb_len-1;++/* Prepare platform data passed to subdev. */+pdata=vzalloc(pdata_sz);+if(!pdata)+gotofail;++pdata->xsp_root_cb=pcb;+pdata->xsp_root_cb_arg=pcb_arg;+memcpy(pdata->xsp_dtb,dtb,dtb_len);+if(id==XRT_SUBDEV_GRP){+/* Group can only be created by root driver. */+pdata->xsp_root_name=dev_name(parent);+}else{+structplatform_device*grp=to_platform_device(parent);+/* Leaf can only be created by group driver. */+WARN_ON(strcmp(xrt_drv_name(XRT_SUBDEV_GRP),platform_get_device_id(grp)->name));+pdata->xsp_root_name=DEV_PDATA(grp)->xsp_root_name;+}++/* Obtain dev instance number. */+inst=xrt_drv_get_instance(id);+if(inst<0){+dev_err(parent,"failed to obtain instance: %d",inst);+gotofail;+}++/* Create subdev. */+if(id==XRT_SUBDEV_GRP){+pdev=platform_device_register_data(parent,xrt_drv_name(XRT_SUBDEV_GRP),+inst,pdata,pdata_sz);+}else{+intrc=xrt_subdev_getres(parent,id,dtb,&res,&res_num);++if(rc){+dev_err(parent,"failed to get resource for %s.%d: %d",+xrt_drv_name(id),inst,rc);+gotofail;+}+pdev=platform_device_register_resndata(parent,xrt_drv_name(id),+inst,res,res_num,pdata,pdata_sz);+vfree(res);+}+if(IS_ERR(pdev)){+dev_err(parent,"failed to create subdev for %s inst %d: %ld",+xrt_drv_name(id),inst,PTR_ERR(pdev));+gotofail;+}+sdev->xs_pdev=pdev;++if(device_attach(DEV(pdev))!=1){+xrt_err(pdev,"failed to attach");+gotofail;+}++if(sysfs_create_group(&DEV(pdev)->kobj,&xrt_subdev_attrgroup))+xrt_err(pdev,"failed to create sysfs group");++/*+*Createsysfssymlinkunderrootforleaves+*underrandomgroupsforeasyaccesstothem.+*/+if(id!=XRT_SUBDEV_GRP){+if(sysfs_create_link(&find_root(pdev)->kobj,+&DEV(pdev)->kobj,dev_name(DEV(pdev)))){+xrt_err(pdev,"failed to create sysfs link");+}+}++/* All done, ready to handle req thru cdev. */+if(xrt_subdev_cdev_auto_creation(pdev))+xleaf_devnode_create(pdev,DEV_DRVDATA(pdev)->xsd_file_ops.xsf_dev_name,NULL);++vfree(pdata);+returnsdev;++fail:+vfree(pdata);+if(sdev&&!IS_ERR_OR_NULL(sdev->xs_pdev))+platform_device_unregister(sdev->xs_pdev);+if(inst>=0)+xrt_drv_put_instance(id,inst);+xrt_subdev_free(sdev);+returnNULL;+}++staticvoidxrt_subdev_destroy(structxrt_subdev*sdev)+{+structplatform_device*pdev=sdev->xs_pdev;+intinst=pdev->id;+structdevice*dev=DEV(pdev);++/* Take down the device node */+if(xrt_subdev_cdev_auto_creation(pdev))+xleaf_devnode_destroy(pdev);+if(sdev->xs_id!=XRT_SUBDEV_GRP)+sysfs_remove_link(&find_root(pdev)->kobj,dev_name(dev));+sysfs_remove_group(&dev->kobj,&xrt_subdev_attrgroup);+platform_device_unregister(pdev);+xrt_drv_put_instance(sdev->xs_id,inst);+xrt_subdev_free(sdev);+}++structplatform_device*+xleaf_get_leaf(structplatform_device*pdev,xrt_subdev_match_tmatch_cb,void*match_arg)+{+intrc;+structxrt_root_ioctl_get_leafget_leaf={+pdev,match_cb,match_arg,};++rc=xrt_subdev_root_request(pdev,XRT_ROOT_GET_LEAF,&get_leaf);+if(rc)+returnNULL;+returnget_leaf.xpigl_leaf;+}+EXPORT_SYMBOL_GPL(xleaf_get_leaf);++boolxleaf_has_endpoint(structplatform_device*pdev,constchar*endpoint_name)+{+structresource*res;+inti;++for(i=0,res=platform_get_resource(pdev,IORESOURCE_MEM,0);+res;+res=platform_get_resource(pdev,IORESOURCE_MEM,++i)){+if(!strncmp(res->name,endpoint_name,strlen(res->name)+1))+returntrue;+}++returnfalse;+}+EXPORT_SYMBOL_GPL(xleaf_has_endpoint);++intxleaf_put_leaf(structplatform_device*pdev,structplatform_device*leaf)+{+structxrt_root_ioctl_put_leafput_leaf={pdev,leaf};++returnxrt_subdev_root_request(pdev,XRT_ROOT_PUT_LEAF,&put_leaf);+}+EXPORT_SYMBOL_GPL(xleaf_put_leaf);++intxleaf_create_group(structplatform_device*pdev,char*dtb)+{+returnxrt_subdev_root_request(pdev,XRT_ROOT_CREATE_GROUP,dtb);+}+EXPORT_SYMBOL_GPL(xleaf_create_group);++intxleaf_destroy_group(structplatform_device*pdev,intinstance)+{+returnxrt_subdev_root_request(pdev,XRT_ROOT_REMOVE_GROUP,(void*)(uintptr_t)instance);+}+EXPORT_SYMBOL_GPL(xleaf_destroy_group);++intxleaf_wait_for_group_bringup(structplatform_device*pdev)+{+returnxrt_subdev_root_request(pdev,XRT_ROOT_WAIT_GROUP_BRINGUP,NULL);+}+EXPORT_SYMBOL_GPL(xleaf_wait_for_group_bringup);++staticssize_t+xrt_subdev_get_holders(structxrt_subdev*sdev,char*buf,size_tlen)+{+conststructlist_head*ptr;+structxrt_subdev_holder*h;+ssize_tn=0;++list_for_each(ptr,&sdev->xs_holder_list){+h=list_entry(ptr,structxrt_subdev_holder,xsh_holder_list);+n+=snprintf(buf+n,len-n,"%s:%d ",+dev_name(h->xsh_holder),kref_read(&h->xsh_kref));+if(n>=(len-1))+break;+}+returnn;+}++voidxrt_subdev_pool_init(structdevice*dev,structxrt_subdev_pool*spool)+{+INIT_LIST_HEAD(&spool->xsp_dev_list);+spool->xsp_owner=dev;+mutex_init(&spool->xsp_lock);+spool->xsp_closing=false;+}++staticvoidxrt_subdev_free_holder(structxrt_subdev_holder*holder)+{+list_del(&holder->xsh_holder_list);+vfree(holder);+}++staticvoidxrt_subdev_pool_wait_for_holders(structxrt_subdev_pool*spool,structxrt_subdev*sdev)+{+conststructlist_head*ptr,*next;+charholders[128];+structxrt_subdev_holder*holder;+structmutex*lk=&spool->xsp_lock;++WARN_ON(!mutex_is_locked(lk));++while(!list_empty(&sdev->xs_holder_list)){+intrc;++/* It's most likely a bug if we ever enters this loop. */+xrt_subdev_get_holders(sdev,holders,sizeof(holders));+xrt_err(sdev->xs_pdev,"awaits holders: %s",holders);+mutex_unlock(lk);+rc=wait_for_completion_killable(&sdev->xs_holder_comp);+mutex_lock(lk);+if(rc==-ERESTARTSYS){+xrt_err(sdev->xs_pdev,"give up on waiting for holders, clean up now");+list_for_each_safe(ptr,next,&sdev->xs_holder_list){+holder=list_entry(ptr,structxrt_subdev_holder,xsh_holder_list);+xrt_subdev_free_holder(holder);+}+}+}+}++voidxrt_subdev_pool_fini(structxrt_subdev_pool*spool)+{+structlist_head*dl=&spool->xsp_dev_list;+structmutex*lk=&spool->xsp_lock;++mutex_lock(lk);++if(spool->xsp_closing){+mutex_unlock(lk);+return;+}++spool->xsp_closing=true;+/* Remove subdev in the reverse order of added. */+while(!list_empty(dl)){+structxrt_subdev*sdev=list_first_entry(dl,structxrt_subdev,xs_dev_list);++xrt_subdev_pool_wait_for_holders(spool,sdev);+list_del(&sdev->xs_dev_list);+mutex_unlock(lk);+xrt_subdev_destroy(sdev);+mutex_lock(lk);+}++mutex_unlock(lk);+}++staticstructxrt_subdev_holder*xrt_subdev_find_holder(structxrt_subdev*sdev,+structdevice*holder_dev)+{+structlist_head*hl=&sdev->xs_holder_list;+structxrt_subdev_holder*holder;+conststructlist_head*ptr;++list_for_each(ptr,hl){+holder=list_entry(ptr,structxrt_subdev_holder,xsh_holder_list);+if(holder->xsh_holder==holder_dev)+returnholder;+}+returnNULL;+}++staticintxrt_subdev_hold(structxrt_subdev*sdev,structdevice*holder_dev)+{+structxrt_subdev_holder*holder=xrt_subdev_find_holder(sdev,holder_dev);+structlist_head*hl=&sdev->xs_holder_list;++if(!holder){+holder=vzalloc(sizeof(*holder));+if(!holder)+return-ENOMEM;+holder->xsh_holder=holder_dev;+kref_init(&holder->xsh_kref);+list_add_tail(&holder->xsh_holder_list,hl);+}else{+kref_get(&holder->xsh_kref);+}++return0;+}++staticvoidxrt_subdev_free_holder_kref(structkref*kref)+{+structxrt_subdev_holder*holder=container_of(kref,structxrt_subdev_holder,xsh_kref);++xrt_subdev_free_holder(holder);+}++staticint+xrt_subdev_release(structxrt_subdev*sdev,structdevice*holder_dev)+{+structxrt_subdev_holder*holder=xrt_subdev_find_holder(sdev,holder_dev);+structlist_head*hl=&sdev->xs_holder_list;++if(!holder){+dev_err(holder_dev,"can't release, %s did not hold %s",+dev_name(holder_dev),dev_name(DEV(sdev->xs_pdev)));+return-EINVAL;+}+kref_put(&holder->xsh_kref,xrt_subdev_free_holder_kref);++/* kref_put above may remove holder from list. */+if(list_empty(hl))+complete(&sdev->xs_holder_comp);+return0;+}++intxrt_subdev_pool_add(structxrt_subdev_pool*spool,enumxrt_subdev_idid,+xrt_subdev_root_cb_tpcb,void*pcb_arg,char*dtb)+{+structmutex*lk=&spool->xsp_lock;+structlist_head*dl=&spool->xsp_dev_list;+structxrt_subdev*sdev;+intret=0;++sdev=xrt_subdev_create(spool->xsp_owner,id,pcb,pcb_arg,dtb);+if(sdev){+mutex_lock(lk);+if(spool->xsp_closing){+/* No new subdev when pool is going away. */+xrt_err(sdev->xs_pdev,"pool is closing");+ret=-ENODEV;+}else{+list_add(&sdev->xs_dev_list,dl);+}+mutex_unlock(lk);+if(ret)+xrt_subdev_destroy(sdev);+}else{+ret=-EINVAL;+}++ret=ret?ret:sdev->xs_pdev->id;+returnret;+}++intxrt_subdev_pool_del(structxrt_subdev_pool*spool,enumxrt_subdev_idid,intinstance)+{+conststructlist_head*ptr;+structmutex*lk=&spool->xsp_lock;+structlist_head*dl=&spool->xsp_dev_list;+structxrt_subdev*sdev;+intret=-ENOENT;++mutex_lock(lk);+list_for_each(ptr,dl){+sdev=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(sdev->xs_id!=id||sdev->xs_pdev->id!=instance)+continue;+xrt_subdev_pool_wait_for_holders(spool,sdev);+list_del(&sdev->xs_dev_list);+ret=0;+break;+}+mutex_unlock(lk);+if(ret)+returnret;++xrt_subdev_destroy(sdev);+return0;+}++staticintxrt_subdev_pool_get_impl(structxrt_subdev_pool*spool,xrt_subdev_match_tmatch,+void*arg,structdevice*holder_dev,structxrt_subdev**sdevp)+{+conststructlist_head*ptr;+structmutex*lk=&spool->xsp_lock;+structlist_head*dl=&spool->xsp_dev_list;+structxrt_subdev*sdev=NULL;+intret=-ENOENT;++mutex_lock(lk);++if(match==XRT_SUBDEV_MATCH_PREV){+structplatform_device*pdev=(structplatform_device*)arg;+structxrt_subdev*d=NULL;++if(!pdev){+sdev=list_empty(dl)?NULL:+list_last_entry(dl,structxrt_subdev,xs_dev_list);+}else{+list_for_each(ptr,dl){+d=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(d->xs_pdev!=pdev)+continue;+if(!list_is_first(ptr,dl))+sdev=list_prev_entry(d,xs_dev_list);+break;+}+}+}elseif(match==XRT_SUBDEV_MATCH_NEXT){+structplatform_device*pdev=(structplatform_device*)arg;+structxrt_subdev*d=NULL;++if(!pdev){+sdev=list_first_entry_or_null(dl,structxrt_subdev,xs_dev_list);+}else{+list_for_each(ptr,dl){+d=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(d->xs_pdev!=pdev)+continue;+if(!list_is_last(ptr,dl))+sdev=list_next_entry(d,xs_dev_list);+break;+}+}+}else{+list_for_each(ptr,dl){+structxrt_subdev*d=NULL;++d=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(d&&!match(d->xs_id,d->xs_pdev,arg))+continue;+sdev=d;+break;+}+}++if(sdev)+ret=xrt_subdev_hold(sdev,holder_dev);++mutex_unlock(lk);++if(!ret)+*sdevp=sdev;+returnret;+}++intxrt_subdev_pool_get(structxrt_subdev_pool*spool,xrt_subdev_match_tmatch,void*arg,+structdevice*holder_dev,structplatform_device**pdevp)+{+intrc;+structxrt_subdev*sdev;++rc=xrt_subdev_pool_get_impl(spool,match,arg,holder_dev,&sdev);+if(rc){+if(rc!=-ENOENT)+dev_err(holder_dev,"failed to hold device: %d",rc);+returnrc;+}++if(!DEV_IS_PCI(holder_dev)){+xrt_dbg(to_platform_device(holder_dev),"%s <<==== %s",+dev_name(holder_dev),dev_name(DEV(sdev->xs_pdev)));+}++*pdevp=sdev->xs_pdev;+return0;+}++staticintxrt_subdev_pool_put_impl(structxrt_subdev_pool*spool,structplatform_device*pdev,+structdevice*holder_dev)+{+conststructlist_head*ptr;+structmutex*lk=&spool->xsp_lock;+structlist_head*dl=&spool->xsp_dev_list;+structxrt_subdev*sdev;+intret=-ENOENT;++mutex_lock(lk);+list_for_each(ptr,dl){+sdev=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(sdev->xs_pdev!=pdev)+continue;+ret=xrt_subdev_release(sdev,holder_dev);+break;+}+mutex_unlock(lk);++returnret;+}++intxrt_subdev_pool_put(structxrt_subdev_pool*spool,structplatform_device*pdev,+structdevice*holder_dev)+{+intret=xrt_subdev_pool_put_impl(spool,pdev,holder_dev);++if(ret)+returnret;++if(!DEV_IS_PCI(holder_dev)){+xrt_dbg(to_platform_device(holder_dev),"%s <<==X== %s",+dev_name(holder_dev),dev_name(DEV(pdev)));+}+return0;+}++voidxrt_subdev_pool_trigger_event(structxrt_subdev_pool*spool,enumxrt_eventse)+{+structplatform_device*tgt=NULL;+structxrt_subdev*sdev=NULL;+structxrt_eventevt;++while(!xrt_subdev_pool_get_impl(spool,XRT_SUBDEV_MATCH_NEXT,+tgt,spool->xsp_owner,&sdev)){+tgt=sdev->xs_pdev;+evt.xe_evt=e;+evt.xe_subdev.xevt_subdev_id=sdev->xs_id;+evt.xe_subdev.xevt_subdev_instance=tgt->id;+xrt_subdev_root_request(tgt,XRT_ROOT_EVENT,&evt);+xrt_subdev_pool_put_impl(spool,tgt,spool->xsp_owner);+}+}++voidxrt_subdev_pool_handle_event(structxrt_subdev_pool*spool,structxrt_event*evt)+{+structplatform_device*tgt=NULL;+structxrt_subdev*sdev=NULL;++while(!xrt_subdev_pool_get_impl(spool,XRT_SUBDEV_MATCH_NEXT,+tgt,spool->xsp_owner,&sdev)){+tgt=sdev->xs_pdev;+xleaf_ioctl(tgt,XRT_XLEAF_EVENT,evt);+xrt_subdev_pool_put_impl(spool,tgt,spool->xsp_owner);+}+}++ssize_txrt_subdev_pool_get_holders(structxrt_subdev_pool*spool,+structplatform_device*pdev,char*buf,size_tlen)+{+conststructlist_head*ptr;+structmutex*lk=&spool->xsp_lock;+structlist_head*dl=&spool->xsp_dev_list;+structxrt_subdev*sdev;+ssize_tret=0;++mutex_lock(lk);+list_for_each(ptr,dl){+sdev=list_entry(ptr,structxrt_subdev,xs_dev_list);+if(sdev->xs_pdev!=pdev)+continue;+ret=xrt_subdev_get_holders(sdev,buf,len);+break;+}+mutex_unlock(lk);++returnret;+}+EXPORT_SYMBOL_GPL(xrt_subdev_pool_get_holders);++intxleaf_broadcast_event(structplatform_device*pdev,enumxrt_eventsevt,boolasync)+{+structxrt_evente={evt,};+u32cmd=async?XRT_ROOT_EVENT_ASYNC:XRT_ROOT_EVENT;++WARN_ON(evt==XRT_EVENT_POST_CREATION||evt==XRT_EVENT_PRE_REMOVAL);+returnxrt_subdev_root_request(pdev,cmd,&e);+}+EXPORT_SYMBOL_GPL(xleaf_broadcast_event);++voidxleaf_hot_reset(structplatform_device*pdev)+{+xrt_subdev_root_request(pdev,XRT_ROOT_HOT_RESET,NULL);+}+EXPORT_SYMBOL_GPL(xleaf_hot_reset);++voidxleaf_get_barres(structplatform_device*pdev,structresource**res,uintbar_idx)+{+structxrt_root_ioctl_get_resarg={0};++if(bar_idx>PCI_STD_RESOURCE_END){+xrt_err(pdev,"Invalid bar idx %d",bar_idx);+*res=NULL;+return;+}++xrt_subdev_root_request(pdev,XRT_ROOT_GET_RESOURCE,&arg);++*res=&arg.xpigr_res[bar_idx];+}++voidxleaf_get_root_id(structplatform_device*pdev,unsignedshort*vendor,unsignedshort*device,+unsignedshort*subvendor,unsignedshort*subdevice)+{+structxrt_root_ioctl_get_idid={0};++xrt_subdev_root_request(pdev,XRT_ROOT_GET_ID,(void*)&id);+if(vendor)+*vendor=id.xpigi_vendor_id;+if(device)+*device=id.xpigi_device_id;+if(subvendor)+*subvendor=id.xpigi_sub_vendor_id;+if(subdevice)+*subdevice=id.xpigi_sub_device_id;+}++structdevice*xleaf_register_hwmon(structplatform_device*pdev,constchar*name,void*drvdata,+conststructattribute_group**grps)+{+structxrt_root_ioctl_hwmonhm={true,name,drvdata,grps,};++xrt_subdev_root_request(pdev,XRT_ROOT_HWMON,(void*)&hm);+returnhm.xpih_hwmon_dev;+}++voidxleaf_unregister_hwmon(structplatform_device*pdev,structdevice*hwmon)+{+structxrt_root_ioctl_hwmonhm={false,};++hm.xpih_hwmon_dev=hwmon;+xrt_subdev_root_request(pdev,XRT_ROOT_HWMON,(void*)&hm);+}
@@ -0,0 +1,598 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGARootFunctions+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#include<linux/module.h>+#include<linux/pci.h>+#include<linux/hwmon.h>+#include"xroot.h"+#include"subdev_pool.h"+#include"group.h"+#include"metadata.h"++#define XROOT_PDEV(xr) ((xr)->pdev)+#define XROOT_DEV(xr) (&(XROOT_PDEV(xr)->dev))+#define xroot_err(xr, fmt, args...) \+dev_err(XROOT_DEV(xr),"%s: "fmt,__func__,##args)+#define xroot_warn(xr, fmt, args...) \+dev_warn(XROOT_DEV(xr),"%s: "fmt,__func__,##args)+#define xroot_info(xr, fmt, args...) \+dev_info(XROOT_DEV(xr),"%s: "fmt,__func__,##args)+#define xroot_dbg(xr, fmt, args...) \+dev_dbg(XROOT_DEV(xr),"%s: "fmt,__func__,##args)++#define XRT_VSEC_ID 0x20++#define XROOT_GRP_FIRST (-1)+#define XROOT_GRP_LAST (-2)++staticintxroot_root_cb(structdevice*,void*,u32,void*);++structxroot_evt{+structlist_headlist;+structxrt_eventevt;+structcompletioncomp;+boolasync;+};++structxroot_events{+structmutexevt_lock;/* event lock */+structlist_headevt_list;+structwork_structevt_work;+};++structxroot_grps{+structxrt_subdev_poolpool;+structwork_structbringup_work;+atomic_tbringup_pending;+atomic_tbringup_failed;+structcompletionbringup_comp;+};++structxroot{+structpci_dev*pdev;+structxroot_eventsevents;+structxroot_grpsgrps;+structxroot_pf_cbpf_cb;+};++structxroot_grp_match_arg{+enumxrt_subdev_idid;+intinstance;+};++staticboolxroot_grp_match(enumxrt_subdev_idid,+structplatform_device*pdev,void*arg)+{+structxroot_grp_match_arg*a=(structxroot_grp_match_arg*)arg;+returnid==a->id&&pdev->id==a->instance;+}++staticintxroot_get_group(structxroot*xr,intinstance,+structplatform_device**grpp)+{+intrc=0;+structxrt_subdev_pool*grps=&xr->grps.pool;+structdevice*dev=DEV(xr->pdev);+structxroot_grp_match_argarg={XRT_SUBDEV_GRP,instance};++if(instance==XROOT_GRP_LAST){+rc=xrt_subdev_pool_get(grps,XRT_SUBDEV_MATCH_NEXT,+*grpp,dev,grpp);+}elseif(instance==XROOT_GRP_FIRST){+rc=xrt_subdev_pool_get(grps,XRT_SUBDEV_MATCH_PREV,+*grpp,dev,grpp);+}else{+rc=xrt_subdev_pool_get(grps,xroot_grp_match,+&arg,dev,grpp);+}++if(rc&&rc!=-ENOENT)+xroot_err(xr,"failed to hold group %d: %d",instance,rc);+returnrc;+}++staticvoidxroot_put_group(structxroot*xr,structplatform_device*grp)+{+intinst=grp->id;+intrc=xrt_subdev_pool_put(&xr->grps.pool,grp,DEV(xr->pdev));++if(rc)+xroot_err(xr,"failed to release group %d: %d",inst,rc);+}++staticintxroot_trigger_event(structxroot*xr,+structxrt_event*e,boolasync)+{+structxroot_evt*enew=vzalloc(sizeof(*enew));++if(!enew)+return-ENOMEM;++enew->evt=*e;+enew->async=async;+init_completion(&enew->comp);++mutex_lock(&xr->events.evt_lock);+list_add(&enew->list,&xr->events.evt_list);+mutex_unlock(&xr->events.evt_lock);++schedule_work(&xr->events.evt_work);++if(async)+return0;++wait_for_completion(&enew->comp);+vfree(enew);+return0;+}++staticvoid+xroot_group_trigger_event(structxroot*xr,intinst,enumxrt_eventse)+{+intret;+structplatform_device*pdev=NULL;+structxrt_eventevt={0};++WARN_ON(inst<0);+/* Only triggers subdev specific events. */+if(e!=XRT_EVENT_POST_CREATION&&e!=XRT_EVENT_PRE_REMOVAL){+xroot_err(xr,"invalid event %d",e);+return;+}++ret=xroot_get_group(xr,inst,&pdev);+if(ret)+return;++/* Triggers event for children, first. */+(void)xleaf_ioctl(pdev,XRT_GROUP_TRIGGER_EVENT,(void*)(uintptr_t)e);++/* Triggers event for itself. */+evt.xe_evt=e;+evt.xe_subdev.xevt_subdev_id=XRT_SUBDEV_GRP;+evt.xe_subdev.xevt_subdev_instance=inst;+(void)xroot_trigger_event(xr,&evt,false);++(void)xroot_put_group(xr,pdev);+}++intxroot_create_group(void*root,char*dtb)+{+structxroot*xr=(structxroot*)root;+intret;++atomic_inc(&xr->grps.bringup_pending);+ret=xrt_subdev_pool_add(&xr->grps.pool,XRT_SUBDEV_GRP,+xroot_root_cb,xr,dtb);+if(ret>=0){+schedule_work(&xr->grps.bringup_work);+}else{+atomic_dec(&xr->grps.bringup_pending);+atomic_inc(&xr->grps.bringup_failed);+xroot_err(xr,"failed to create group: %d",ret);+}+returnret;+}+EXPORT_SYMBOL_GPL(xroot_create_group);++staticintxroot_destroy_single_group(structxroot*xr,intinstance)+{+structplatform_device*pdev=NULL;+intret;++WARN_ON(instance<0);+ret=xroot_get_group(xr,instance,&pdev);+if(ret)+returnret;++xroot_group_trigger_event(xr,instance,XRT_EVENT_PRE_REMOVAL);++/* Now tear down all children in this group. */+ret=xleaf_ioctl(pdev,XRT_GROUP_FINI_CHILDREN,NULL);+(void)xroot_put_group(xr,pdev);+if(!ret){+ret=xrt_subdev_pool_del(&xr->grps.pool,XRT_SUBDEV_GRP,+instance);+}++returnret;+}++staticintxroot_destroy_group(structxroot*xr,intinstance)+{+structplatform_device*target=NULL;+structplatform_device*deps=NULL;+intret;++WARN_ON(instance<0);+/*+*Makesuretargetgroupexistsandcan'tgoawaybefore+*weremoveit'sdependents+*/+ret=xroot_get_group(xr,instance,&target);+if(ret)+returnret;++/*+*Removeallgroupsdependontargetone.+*AssumingsubdevsinhighergroupIDcandependononesin+*lowerIDgroups,weremovetheminthereservseorder.+*/+while(xroot_get_group(xr,XROOT_GRP_LAST,&deps)!=-ENOENT){+intinst=deps->id;++xroot_put_group(xr,deps);+if(instance==inst)+break;+(void)xroot_destroy_single_group(xr,inst);+deps=NULL;+}++/* Now we can remove the target group. */+xroot_put_group(xr,target);+returnxroot_destroy_single_group(xr,instance);+}++staticintxroot_lookup_group(structxroot*xr,+structxrt_root_ioctl_lookup_group*arg)+{+intrc=-ENOENT;+structplatform_device*grp=NULL;++while(rc<0&&xroot_get_group(xr,XROOT_GRP_LAST,&grp)!=-ENOENT){+if(arg->xpilp_match_cb(XRT_SUBDEV_GRP,grp,+arg->xpilp_match_arg)){+rc=grp->id;+}+xroot_put_group(xr,grp);+}+returnrc;+}++staticvoidxroot_event_work(structwork_struct*work)+{+structxroot_evt*tmp;+structxroot*xr=container_of(work,structxroot,events.evt_work);++mutex_lock(&xr->events.evt_lock);+while(!list_empty(&xr->events.evt_list)){+tmp=list_first_entry(&xr->events.evt_list,+structxroot_evt,list);+list_del(&tmp->list);+mutex_unlock(&xr->events.evt_lock);++(void)xrt_subdev_pool_handle_event(&xr->grps.pool,&tmp->evt);++if(tmp->async)+vfree(tmp);+else+complete(&tmp->comp);++mutex_lock(&xr->events.evt_lock);+}+mutex_unlock(&xr->events.evt_lock);+}++staticvoidxroot_event_init(structxroot*xr)+{+INIT_LIST_HEAD(&xr->events.evt_list);+mutex_init(&xr->events.evt_lock);+INIT_WORK(&xr->events.evt_work,xroot_event_work);+}++staticvoidxroot_event_fini(structxroot*xr)+{+flush_scheduled_work();+WARN_ON(!list_empty(&xr->events.evt_list));+}++staticintxroot_get_leaf(structxroot*xr,structxrt_root_ioctl_get_leaf*arg)+{+intrc=-ENOENT;+structplatform_device*grp=NULL;++while(rc&&xroot_get_group(xr,XROOT_GRP_LAST,&grp)!=-ENOENT){+rc=xleaf_ioctl(grp,XRT_GROUP_GET_LEAF,arg);+xroot_put_group(xr,grp);+}+returnrc;+}++staticintxroot_put_leaf(structxroot*xr,structxrt_root_ioctl_put_leaf*arg)+{+intrc=-ENOENT;+structplatform_device*grp=NULL;++while(rc&&xroot_get_group(xr,XROOT_GRP_LAST,&grp)!=-ENOENT){+rc=xleaf_ioctl(grp,XRT_GROUP_PUT_LEAF,arg);+xroot_put_group(xr,grp);+}+returnrc;+}++staticintxroot_root_cb(structdevice*dev,void*parg,u32cmd,void*arg)+{+structxroot*xr=(structxroot*)parg;+intrc=0;++switch(cmd){+/* Leaf actions. */+caseXRT_ROOT_GET_LEAF:{+structxrt_root_ioctl_get_leaf*getleaf=+(structxrt_root_ioctl_get_leaf*)arg;+rc=xroot_get_leaf(xr,getleaf);+break;+}+caseXRT_ROOT_PUT_LEAF:{+structxrt_root_ioctl_put_leaf*putleaf=+(structxrt_root_ioctl_put_leaf*)arg;+rc=xroot_put_leaf(xr,putleaf);+break;+}+caseXRT_ROOT_GET_LEAF_HOLDERS:{+structxrt_root_ioctl_get_holders*holders=+(structxrt_root_ioctl_get_holders*)arg;+rc=xrt_subdev_pool_get_holders(&xr->grps.pool,+holders->xpigh_pdev,+holders->xpigh_holder_buf,+holders->xpigh_holder_buf_len);+break;+}++/* Group actions. */+caseXRT_ROOT_CREATE_GROUP:+rc=xroot_create_group(xr,(char*)arg);+break;+caseXRT_ROOT_REMOVE_GROUP:+rc=xroot_destroy_group(xr,(int)(uintptr_t)arg);+break;+caseXRT_ROOT_LOOKUP_GROUP:{+structxrt_root_ioctl_lookup_group*getgrp=+(structxrt_root_ioctl_lookup_group*)arg;+rc=xroot_lookup_group(xr,getgrp);+break;+}+caseXRT_ROOT_WAIT_GROUP_BRINGUP:+rc=xroot_wait_for_bringup(xr)?0:-EINVAL;+break;++/* Event actions. */+caseXRT_ROOT_EVENT:+caseXRT_ROOT_EVENT_ASYNC:{+boolasync=(cmd==XRT_ROOT_EVENT_ASYNC);+structxrt_event*evt=(structxrt_event*)arg;++rc=xroot_trigger_event(xr,evt,async);+break;+}++/* Device info. */+caseXRT_ROOT_GET_RESOURCE:{+structxrt_root_ioctl_get_res*res=+(structxrt_root_ioctl_get_res*)arg;+res->xpigr_res=xr->pdev->resource;+break;+}+caseXRT_ROOT_GET_ID:{+structxrt_root_ioctl_get_id*id=+(structxrt_root_ioctl_get_id*)arg;++id->xpigi_vendor_id=xr->pdev->vendor;+id->xpigi_device_id=xr->pdev->device;+id->xpigi_sub_vendor_id=xr->pdev->subsystem_vendor;+id->xpigi_sub_device_id=xr->pdev->subsystem_device;+break;+}++/* MISC generic PCIE driver functions. */+caseXRT_ROOT_HOT_RESET:{+xr->pf_cb.xpc_hot_reset(xr->pdev);+break;+}+caseXRT_ROOT_HWMON:{+structxrt_root_ioctl_hwmon*hwmon=+(structxrt_root_ioctl_hwmon*)arg;++if(hwmon->xpih_register){+hwmon->xpih_hwmon_dev=+hwmon_device_register_with_info(DEV(xr->pdev),+hwmon->xpih_name,+hwmon->xpih_drvdata,+NULL,+hwmon->xpih_groups);+}else{+(void)hwmon_device_unregister(hwmon->xpih_hwmon_dev);+}+break;+}++default:+xroot_err(xr,"unknown IOCTL cmd %d",cmd);+rc=-EINVAL;+break;+}++returnrc;+}++staticvoidxroot_bringup_group_work(structwork_struct*work)+{+structplatform_device*pdev=NULL;+structxroot*xr=container_of(work,structxroot,grps.bringup_work);++while(xroot_get_group(xr,XROOT_GRP_FIRST,&pdev)!=-ENOENT){+intr,i;++i=pdev->id;+r=xleaf_ioctl(pdev,XRT_GROUP_INIT_CHILDREN,NULL);+(void)xroot_put_group(xr,pdev);+if(r==-EEXIST)+continue;/* Already brough up, nothing to do. */+if(r)+atomic_inc(&xr->grps.bringup_failed);++xroot_group_trigger_event(xr,i,XRT_EVENT_POST_CREATION);++if(atomic_dec_and_test(&xr->grps.bringup_pending))+complete(&xr->grps.bringup_comp);+}+}++staticvoidxroot_grps_init(structxroot*xr)+{+xrt_subdev_pool_init(DEV(xr->pdev),&xr->grps.pool);+INIT_WORK(&xr->grps.bringup_work,xroot_bringup_group_work);+atomic_set(&xr->grps.bringup_pending,0);+atomic_set(&xr->grps.bringup_failed,0);+init_completion(&xr->grps.bringup_comp);+}++staticvoidxroot_grps_fini(structxroot*xr)+{+flush_scheduled_work();+xrt_subdev_pool_fini(&xr->grps.pool);+}++intxroot_add_vsec_node(void*root,char*dtb)+{+structxroot*xr=(structxroot*)root;+structdevice*dev=DEV(xr->pdev);+structxrt_md_endpointep={0};+intcap=0,ret=0;+u32off_low,off_high,vsec_bar,header;+u64vsec_off;++while((cap=pci_find_next_ext_capability(xr->pdev,cap,+PCI_EXT_CAP_ID_VNDR))){+pci_read_config_dword(xr->pdev,cap+PCI_VNDR_HEADER,&header);+if(PCI_VNDR_HEADER_ID(header)==XRT_VSEC_ID)+break;+}+if(!cap){+xroot_info(xr,"No Vendor Specific Capability.");+return-ENOENT;+}++if(pci_read_config_dword(xr->pdev,cap+8,&off_low)||+pci_read_config_dword(xr->pdev,cap+12,&off_high)){+xroot_err(xr,"pci_read vendor specific failed.");+return-EINVAL;+}++ep.ep_name=XRT_MD_NODE_VSEC;+ret=xrt_md_add_endpoint(dev,dtb,&ep);+if(ret){+xroot_err(xr,"add vsec metadata failed, ret %d",ret);+gotofailed;+}++vsec_bar=cpu_to_be32(off_low&0xf);+ret=xrt_md_set_prop(dev,dtb,XRT_MD_NODE_VSEC,NULL,+XRT_MD_PROP_BAR_IDX,&vsec_bar,sizeof(vsec_bar));+if(ret){+xroot_err(xr,"add vsec bar idx failed, ret %d",ret);+gotofailed;+}++vsec_off=cpu_to_be64(((u64)off_high<<32)|(off_low&~0xfU));+ret=xrt_md_set_prop(dev,dtb,XRT_MD_NODE_VSEC,NULL,+XRT_MD_PROP_OFFSET,&vsec_off,sizeof(vsec_off));+if(ret){+xroot_err(xr,"add vsec offset failed, ret %d",ret);+gotofailed;+}++failed:+returnret;+}+EXPORT_SYMBOL_GPL(xroot_add_vsec_node);++intxroot_add_simple_node(void*root,char*dtb,constchar*endpoint)+{+structxroot*xr=(structxroot*)root;+structdevice*dev=DEV(xr->pdev);+structxrt_md_endpointep={0};+intret=0;++ep.ep_name=endpoint;+ret=xrt_md_add_endpoint(dev,dtb,&ep);+if(ret)+xroot_err(xr,"add %s failed, ret %d",endpoint,ret);++returnret;+}+EXPORT_SYMBOL_GPL(xroot_add_simple_node);++boolxroot_wait_for_bringup(void*root)+{+structxroot*xr=(structxroot*)root;++wait_for_completion(&xr->grps.bringup_comp);+returnatomic_xchg(&xr->grps.bringup_failed,0)==0;+}+EXPORT_SYMBOL_GPL(xroot_wait_for_bringup);++intxroot_probe(structpci_dev*pdev,structxroot_pf_cb*cb,void**root)+{+structdevice*dev=DEV(pdev);+structxroot*xr=NULL;++dev_info(dev,"%s: probing...",__func__);++xr=devm_kzalloc(dev,sizeof(*xr),GFP_KERNEL);+if(!xr)+return-ENOMEM;++xr->pdev=pdev;+xr->pf_cb=*cb;+xroot_grps_init(xr);+xroot_event_init(xr);++*root=xr;+return0;+}+EXPORT_SYMBOL_GPL(xroot_probe);++voidxroot_remove(void*root)+{+structxroot*xr=(structxroot*)root;+structplatform_device*grp=NULL;++xroot_info(xr,"leaving...");++if(xroot_get_group(xr,XROOT_GRP_FIRST,&grp)==0){+intinstance=grp->id;++xroot_put_group(xr,grp);+(void)xroot_destroy_group(xr,instance);+}++xroot_event_fini(xr);+xroot_grps_fini(xr);+}+EXPORT_SYMBOL_GPL(xroot_remove);++voidxroot_broadcast(void*root,enumxrt_eventsevt)+{+structxroot*xr=(structxroot*)root;+structxrt_evente={0};++/* Root pf driver only broadcasts below two events. */+if(evt!=XRT_EVENT_POST_CREATION&&evt!=XRT_EVENT_PRE_REMOVAL){+xroot_info(xr,"invalid event %d",evt);+return;+}++e.xe_evt=evt;+e.xe_subdev.xevt_subdev_id=XRT_ROOT;+e.xe_subdev.xevt_subdev_instance=0;+(void)xroot_trigger_event(xr,&e,false);+}+EXPORT_SYMBOL_GPL(xroot_broadcast);
The PCIE device driver which attaches to management function on Alveo
devices. It instantiates one or more partition drivers which in turn
instantiate platform drivers. The instantiation of partition and platform
drivers is completely data driven.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xroot.h | 114 +++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++++++++++++++++++++++++
2 files changed, 456 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/mgmt/root.c
@@ -0,0 +1,471 @@+// SPDX-License-Identifier: GPL-2.0+/*+*FPGARegionSupportforXilinxAlveoManagementFunctionDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*BulkofthecodeborrowedfromXRTmgmtdriverfile,fmgr.c+*+*Authors:Lizhi.Hou@xilinx.com+*/++#include<linux/uuid.h>+#include<linux/fpga/fpga-bridge.h>+#include<linux/fpga/fpga-region.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/axigate.h"+#include"xclbin-helper.h"+#include"main-impl.h"++structxmgmt_bridge{+structplatform_device*pdev;+constchar*axigate_name;+};++structxmgmt_region{+structplatform_device*pdev;+structfpga_region*fregion;+uuid_tintf_uuid;+structfpga_bridge*fbridge;+intgrp_inst;+uuid_tdep_uuid;+structlist_headlist;+};++structxmgmt_region_match_arg{+structplatform_device*pdev;+uuid_t*uuids;+u32uuid_num;+};++staticintxmgmt_br_enable_set(structfpga_bridge*bridge,boolenable)+{+structxmgmt_bridge*br_data=(structxmgmt_bridge*)bridge->priv;+structplatform_device*axigate_leaf;+intrc;++axigate_leaf=xleaf_get_leaf_by_epname(br_data->pdev,br_data->axigate_name);+if(!axigate_leaf){+xrt_err(br_data->pdev,"failed to get leaf %s",+br_data->axigate_name);+return-ENOENT;+}++if(enable)+rc=xleaf_ioctl(axigate_leaf,XRT_AXIGATE_FREE,NULL);+else+rc=xleaf_ioctl(axigate_leaf,XRT_AXIGATE_FREEZE,NULL);++if(rc){+xrt_err(br_data->pdev,"failed to %s gate %s, rc %d",+(enable?"free":"freeze"),br_data->axigate_name,+rc);+}++xleaf_put_leaf(br_data->pdev,axigate_leaf);++returnrc;+}++conststructfpga_bridge_opsxmgmt_bridge_ops={+.enable_set=xmgmt_br_enable_set+};++staticvoidxmgmt_destroy_bridge(structfpga_bridge*br)+{+structxmgmt_bridge*br_data=br->priv;++if(!br_data)+return;++xrt_info(br_data->pdev,"destroy fpga bridge %s",br_data->axigate_name);+fpga_bridge_unregister(br);++devm_kfree(DEV(br_data->pdev),br_data);++fpga_bridge_free(br);+}++staticstructfpga_bridge*xmgmt_create_bridge(structplatform_device*pdev,+char*dtb)+{+structxmgmt_bridge*br_data;+structfpga_bridge*br=NULL;+constchar*gate;+intrc;++br_data=devm_kzalloc(DEV(pdev),sizeof(*br_data),GFP_KERNEL);+if(!br_data)+returnNULL;+br_data->pdev=pdev;++br_data->axigate_name=XRT_MD_NODE_GATE_ULP;+rc=xrt_md_find_endpoint(&pdev->dev,dtb,XRT_MD_NODE_GATE_ULP,+NULL,&gate);+if(rc){+br_data->axigate_name=XRT_MD_NODE_GATE_PLP;+rc=xrt_md_find_endpoint(&pdev->dev,dtb,XRT_MD_NODE_GATE_PLP,+NULL,&gate);+}+if(rc){+xrt_err(pdev,"failed to get axigate, rc %d",rc);+gotofailed;+}++br=fpga_bridge_create(DEV(pdev),br_data->axigate_name,+&xmgmt_bridge_ops,br_data);+if(!br){+xrt_err(pdev,"failed to create bridge");+gotofailed;+}++rc=fpga_bridge_register(br);+if(rc){+xrt_err(pdev,"failed to register bridge, rc %d",rc);+gotofailed;+}++xrt_info(pdev,"created fpga bridge %s",br_data->axigate_name);++returnbr;++failed:+if(br)+fpga_bridge_free(br);+if(br_data)+devm_kfree(DEV(pdev),br_data);++returnNULL;+}++staticvoidxmgmt_destroy_region(structfpga_region*re)+{+structxmgmt_region*r_data=re->priv;++xrt_info(r_data->pdev,"destroy fpga region %llx%llx",+re->compat_id->id_l,re->compat_id->id_h);++fpga_region_unregister(re);++if(r_data->grp_inst>0)+xleaf_destroy_group(r_data->pdev,r_data->grp_inst);++if(r_data->fbridge)+xmgmt_destroy_bridge(r_data->fbridge);++if(r_data->fregion->info){+fpga_image_info_free(r_data->fregion->info);+r_data->fregion->info=NULL;+}++fpga_region_free(re);++devm_kfree(DEV(r_data->pdev),r_data);+}++staticintxmgmt_region_match(structdevice*dev,constvoid*data)+{+conststructxmgmt_region_match_arg*arg=data;+conststructfpga_region*match_re;+inti;++if(dev->parent!=&arg->pdev->dev)+returnfalse;++match_re=to_fpga_region(dev);+/*+*Thedevicetreeprovidesbothparentandchilduuidsforan+*xclbininonearray.Herewetrybothuuidstoseeifitmatches+*withtargetregion'scompat_id.Strictlyspeakingweshould+*onlymatchxclbin'sparentuuidwithtargetregion'scompat_id+*butgiventheuuidsbydesignareuniquecomparingwithboth+*doesnothurt.+*/+for(i=0;i<arg->uuid_num;i++){+if(!memcmp(match_re->compat_id,&arg->uuids[i],+sizeof(*match_re->compat_id)))+returntrue;+}++returnfalse;+}++staticintxmgmt_region_match_base(structdevice*dev,constvoid*data)+{+conststructxmgmt_region_match_arg*arg=data;+conststructfpga_region*match_re;+conststructxmgmt_region*r_data;++if(dev->parent!=&arg->pdev->dev)+returnfalse;++match_re=to_fpga_region(dev);+r_data=match_re->priv;+if(uuid_is_null(&r_data->dep_uuid))+returntrue;++returnfalse;+}++staticintxmgmt_region_match_by_depuuid(structdevice*dev,constvoid*data)+{+conststructxmgmt_region_match_arg*arg=data;+conststructfpga_region*match_re;+conststructxmgmt_region*r_data;++if(dev->parent!=&arg->pdev->dev)+returnfalse;++match_re=to_fpga_region(dev);+r_data=match_re->priv;+if(!memcmp(&r_data->dep_uuid,arg->uuids,sizeof(uuid_t)))+returntrue;++returnfalse;+}++staticvoidxmgmt_region_cleanup(structfpga_region*re)+{+structxmgmt_region*r_data=re->priv,*temp;+structplatform_device*pdev=r_data->pdev;+structfpga_region*match_re=NULL;+structdevice*start_dev=NULL;+structxmgmt_region_match_argarg;+LIST_HEAD(free_list);++list_add_tail(&r_data->list,&free_list);+arg.pdev=pdev;+arg.uuid_num=1;++while(!r_data){+arg.uuids=(uuid_t*)r_data->fregion->compat_id;+match_re=fpga_region_class_find(start_dev,&arg,+xmgmt_region_match_by_depuuid);+if(match_re){+r_data=match_re->priv;+list_add_tail(&r_data->list,&free_list);+start_dev=&match_re->dev;+put_device(&match_re->dev);+continue;+}++r_data=list_is_last(&r_data->list,&free_list)?NULL:+list_next_entry(r_data,list);+start_dev=NULL;+}++list_for_each_entry_safe_reverse(r_data,temp,&free_list,list){+if(list_is_first(&r_data->list,&free_list)){+if(r_data->grp_inst>0){+xleaf_destroy_group(pdev,r_data->grp_inst);+r_data->grp_inst=-1;+}+if(r_data->fregion->info){+fpga_image_info_free(r_data->fregion->info);+r_data->fregion->info=NULL;+}+continue;+}+xmgmt_destroy_region(r_data->fregion);+}+}++voidxmgmt_region_cleanup_all(structplatform_device*pdev)+{+structfpga_region*base_re;+structxmgmt_region_match_argarg;++arg.pdev=pdev;++for(base_re=fpga_region_class_find(NULL,&arg,xmgmt_region_match_base);+base_re;+base_re=fpga_region_class_find(NULL,&arg,xmgmt_region_match_base)){+put_device(&base_re->dev);++xmgmt_region_cleanup(base_re);+xmgmt_destroy_region(base_re);+}+}++/*+*Programagivenregionwithgivenxclbinimage.Bringupthesubdevsandthe+*groupobjecttocontainthesubdevs.+*/+staticintxmgmt_region_program(structfpga_region*re,constvoid*xclbin,char*dtb)+{+structxmgmt_region*r_data=re->priv;+structplatform_device*pdev=r_data->pdev;+structfpga_image_info*info;+conststructaxlf*xclbin_obj=xclbin;+intrc;++info=fpga_image_info_alloc(&pdev->dev);+if(!info)+return-ENOMEM;++info->buf=xclbin;+info->count=xclbin_obj->m_header.m_length;+info->flags|=FPGA_MGR_PARTIAL_RECONFIG;+re->info=info;+rc=fpga_region_program_fpga(re);+if(rc){+xrt_err(pdev,"programming xclbin failed, rc %d",rc);+returnrc;+}++/* free bridges to allow reprogram */+if(re->get_bridges)+fpga_bridges_put(&re->bridge_list);++/*+*Nextbringupthesubdevsforthisregionwhichwillbemanagedby+*itsowngroupobject.+*/+r_data->grp_inst=xleaf_create_group(pdev,dtb);+if(r_data->grp_inst<0){+xrt_err(pdev,"failed to create group, rc %d",+r_data->grp_inst);+rc=r_data->grp_inst;+returnrc;+}++rc=xleaf_wait_for_group_bringup(pdev);+if(rc)+xrt_err(pdev,"group bringup failed, rc %d",rc);+returnrc;+}++staticintxmgmt_get_bridges(structfpga_region*re)+{+structxmgmt_region*r_data=re->priv;+structdevice*dev=&r_data->pdev->dev;++returnfpga_bridge_get_to_list(dev,re->info,&re->bridge_list);+}++/*+*Program/createFPGAregionsbasedoninputxclbinfile.Thisiskeyfunction+*stitchingtheflowtogether:+*1.Identifyamatchingexistingregionforthisxclbin+*2.Teardownanypreviousobjectsforthefoundregion+*3.Programthisregionwithinputxclbin+*4.Iterateoverthisregion'sinterfaceuuidstodetermineifitdefinesany+*childregion.Createfpga_regionforthechildregion.+*/+intxmgmt_process_xclbin(structplatform_device*pdev,+structfpga_manager*fmgr,+conststructaxlf*xclbin,+enumprovider_kindkind)+{+structfpga_region*re,*compat_re=NULL;+structxmgmt_region_match_argarg;+structxmgmt_region*r_data;+char*dtb=NULL;+intrc,i;++rc=xrt_xclbin_get_metadata(DEV(pdev),xclbin,&dtb);+if(rc){+xrt_err(pdev,"failed to get dtb: %d",rc);+gotofailed;+}++xrt_md_get_intf_uuids(DEV(pdev),dtb,&arg.uuid_num,NULL);+if(arg.uuid_num==0){+xrt_err(pdev,"failed to get intf uuid");+rc=-EINVAL;+gotofailed;+}+arg.uuids=vzalloc(sizeof(uuid_t)*arg.uuid_num);+if(!arg.uuids){+rc=-ENOMEM;+gotofailed;+}+arg.pdev=pdev;++xrt_md_get_intf_uuids(DEV(pdev),dtb,&arg.uuid_num,arg.uuids);++/* if this is not base firmware, search for a compatible region */+if(kind!=XMGMT_BLP){+compat_re=fpga_region_class_find(NULL,&arg,+xmgmt_region_match);+if(!compat_re){+xrt_err(pdev,"failed to get compatible region");+rc=-ENOENT;+gotofailed;+}++xmgmt_region_cleanup(compat_re);++rc=xmgmt_region_program(compat_re,xclbin,dtb);+if(rc){+xrt_err(pdev,"failed to program region");+gotofailed;+}+}++/* create all the new regions contained in this xclbin */+for(i=0;i<arg.uuid_num;i++){+if(compat_re&&!memcmp(compat_re->compat_id,&arg.uuids[i],+sizeof(*compat_re->compat_id)))+/* region for this interface already exists */+continue;+re=fpga_region_create(DEV(pdev),fmgr,xmgmt_get_bridges);+if(!re){+xrt_err(pdev,"failed to create fpga region");+rc=-EFAULT;+gotofailed;+}+r_data=devm_kzalloc(DEV(pdev),sizeof(*r_data),GFP_KERNEL);+if(!r_data){+rc=-ENOMEM;+fpga_region_free(re);+gotofailed;+}+r_data->pdev=pdev;+r_data->fregion=re;+r_data->grp_inst=-1;+memcpy(&r_data->intf_uuid,&arg.uuids[i],+sizeof(r_data->intf_uuid));+if(compat_re){+memcpy(&r_data->dep_uuid,compat_re->compat_id,+sizeof(r_data->intf_uuid));+}+r_data->fbridge=xmgmt_create_bridge(pdev,dtb);+if(!r_data->fbridge){+xrt_err(pdev,"failed to create fpga bridge");+rc=-EFAULT;+devm_kfree(DEV(pdev),r_data);+fpga_region_free(re);+gotofailed;+}++re->compat_id=(structfpga_compat_id*)&r_data->intf_uuid;+re->priv=r_data;++rc=fpga_region_register(re);+if(rc){+xrt_err(pdev,"failed to register fpga region");+xmgmt_destroy_bridge(r_data->fbridge);+fpga_region_free(re);+devm_kfree(DEV(pdev),r_data);+gotofailed;+}++xrt_info(pdev,"created fpga region %llx%llx",+re->compat_id->id_l,re->compat_id->id_h);+}++failed:+if(compat_re)+put_device(&compat_re->dev);++if(rc){+if(compat_re)+xmgmt_region_cleanup(compat_re);+}++if(dtb)+vfree(dtb);++returnrc;+}
Add clock driver. Clock is a hardware function discovered by walking
xclbin metadata. A platform device node will be created for it. Other
part of driver configures clock through clock driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clock.h | 31 ++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++++++++++
2 files changed, 679 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
Add ICAP driver. ICAP is a hardware function discovered by walking
firmware metadata. A platform device node will be created for it.
FPGA bitstream is written to hardware through ICAP.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/icap.h | 29 +++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 ++++++++++++++++++++++++++
2 files changed, 346 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
Add clock frequence counter driver. Clock frequence counter is
a hardware function discovered by walking xclbin metadata. A platform
device node will be created for it. Other part of driver can read the
actual clock frequence through clock frequence counter driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 +++++++++++++++++++++++
2 files changed, 244 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
Add partition isolation platform driver. partition isolation is
a hardware function discovered by walking firmware metadata.
A platform device node will be created for it. Partition isolation
function isolate the different fpga regions
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/axigate.h | 25 ++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 +++++++++++++++++++++++
2 files changed, 323 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
@@ -0,0 +1,298 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAAXIGateDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/delay.h>+#include<linux/device.h>+#include<linux/io.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/axigate.h"++#define XRT_AXIGATE "xrt_axigate"++structaxigate_regs{+u32iag_wr;+u32iag_rvsd;+u32iag_rd;+}__packed;++structxrt_axigate{+structplatform_device*pdev;+void*base;+structmutexgate_lock;/* gate dev lock */++void*evt_hdl;+constchar*ep_name;++boolgate_freezed;+};++/* the ep names are in the order of hardware layers */+staticconstchar*constxrt_axigate_epnames[]={+XRT_MD_NODE_GATE_PLP,+XRT_MD_NODE_GATE_ULP,+NULL+};++#define reg_rd(g, r) \+ioread32((void*)(g)->base+offsetof(structaxigate_regs,r))+#define reg_wr(g, v, r) \+iowrite32(v,(void*)(g)->base+offsetof(structaxigate_regs,r))++staticinlinevoidfreeze_gate(structxrt_axigate*gate)+{+reg_wr(gate,0,iag_wr);+ndelay(500);+reg_rd(gate,iag_rd);+}++staticinlinevoidfree_gate(structxrt_axigate*gate)+{+reg_wr(gate,0x2,iag_wr);+ndelay(500);+(void)reg_rd(gate,iag_rd);+reg_wr(gate,0x3,iag_wr);+ndelay(500);+reg_rd(gate,iag_rd);+}++staticintxrt_axigate_epname_idx(structplatform_device*pdev)+{+inti;+intret;+structresource*res;++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+if(!res){+xrt_err(pdev,"Empty Resource!");+return-EINVAL;+}++for(i=0;xrt_axigate_epnames[i];i++){+ret=strncmp(xrt_axigate_epnames[i],res->name,+strlen(xrt_axigate_epnames[i])+1);+if(!ret)+break;+}++ret=(xrt_axigate_epnames[i])?i:-EINVAL;+returnret;+}++staticvoidxrt_axigate_freeze(structplatform_device*pdev)+{+structxrt_axigate*gate;+u32freeze=0;++gate=platform_get_drvdata(pdev);++mutex_lock(&gate->gate_lock);+freeze=reg_rd(gate,iag_rd);+if(freeze){/* gate is opened */+xleaf_broadcast_event(pdev,XRT_EVENT_PRE_GATE_CLOSE,false);+freeze_gate(gate);+}++gate->gate_freezed=true;+mutex_unlock(&gate->gate_lock);++xrt_info(pdev,"freeze gate %s",gate->ep_name);+}++staticvoidxrt_axigate_free(structplatform_device*pdev)+{+structxrt_axigate*gate;+u32freeze;++gate=platform_get_drvdata(pdev);++mutex_lock(&gate->gate_lock);+freeze=reg_rd(gate,iag_rd);+if(!freeze){/* gate is closed */+free_gate(gate);+xleaf_broadcast_event(pdev,XRT_EVENT_POST_GATE_OPEN,true);+/* xrt_axigate_free() could be called in event cb, thus+*wecannotwaitforthecompletes+*/+}++gate->gate_freezed=false;+mutex_unlock(&gate->gate_lock);++xrt_info(pdev,"free gate %s",gate->ep_name);+}++staticvoidxrt_axigate_event_cb(structplatform_device*pdev,void*arg)+{+structplatform_device*leaf;+structxrt_event*evt=(structxrt_event*)arg;+enumxrt_eventse=evt->xe_evt;+enumxrt_subdev_idid=evt->xe_subdev.xevt_subdev_id;+intinstance=evt->xe_subdev.xevt_subdev_instance;+structxrt_axigate*gate=platform_get_drvdata(pdev);+structresource*res;++switch(e){+caseXRT_EVENT_POST_CREATION:+break;+default:+return;+}++if(id!=XRT_SUBDEV_AXIGATE)+return;++leaf=xleaf_get_leaf_by_id(pdev,id,instance);+if(!leaf)+return;++res=platform_get_resource(leaf,IORESOURCE_MEM,0);+if(!res||!strncmp(res->name,gate->ep_name,strlen(res->name)+1)){+(void)xleaf_put_leaf(pdev,leaf);+return;+}++/*+*higherlevelaxigateinstancecreated,+*makesurethegateisopenned.Thiscovers1RPflowwhich+*hasplpgateaswell.+*/+if(xrt_axigate_epname_idx(leaf)>xrt_axigate_epname_idx(pdev))+xrt_axigate_free(pdev);+else+xleaf_ioctl(leaf,XRT_AXIGATE_FREE,NULL);++(void)xleaf_put_leaf(pdev,leaf);+}++staticint+xrt_axigate_leaf_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+switch(cmd){+caseXRT_XLEAF_EVENT:+xrt_axigate_event_cb(pdev,arg);+break;+caseXRT_AXIGATE_FREEZE:+xrt_axigate_freeze(pdev);+break;+caseXRT_AXIGATE_FREE:+xrt_axigate_free(pdev);+break;+default:+xrt_err(pdev,"unsupported cmd %d",cmd);+return-EINVAL;+}++return0;+}++staticintxrt_axigate_remove(structplatform_device*pdev)+{+structxrt_axigate*gate;++gate=platform_get_drvdata(pdev);++if(gate->base)+iounmap(gate->base);++platform_set_drvdata(pdev,NULL);+devm_kfree(&pdev->dev,gate);++return0;+}++staticintxrt_axigate_probe(structplatform_device*pdev)+{+structxrt_axigate*gate;+structresource*res;+intret;++gate=devm_kzalloc(&pdev->dev,sizeof(*gate),GFP_KERNEL);+if(!gate)+return-ENOMEM;++gate->pdev=pdev;+platform_set_drvdata(pdev,gate);++xrt_info(pdev,"probing...");+res=platform_get_resource(pdev,IORESOURCE_MEM,0);+if(!res){+xrt_err(pdev,"Empty resource 0");+ret=-EINVAL;+gotofailed;+}++gate->base=ioremap(res->start,res->end-res->start+1);+if(!gate->base){+xrt_err(pdev,"map base iomem failed");+ret=-EFAULT;+gotofailed;+}++gate->ep_name=res->name;++mutex_init(&gate->gate_lock);++return0;++failed:+xrt_axigate_remove(pdev);+returnret;+}++staticstructxrt_subdev_endpointsxrt_axigate_endpoints[]={+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name="ep_pr_isolate_ulp_00"},+{NULL},+},+.xse_min_ep=1,+},+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name="ep_pr_isolate_plp_00"},+{NULL},+},+.xse_min_ep=1,+},+{0},+};++staticstructxrt_subdev_drvdataxrt_axigate_data={+.xsd_dev_ops={+.xsd_ioctl=xrt_axigate_leaf_ioctl,+},+};++staticconststructplatform_device_idxrt_axigate_table[]={+{XRT_AXIGATE,(kernel_ulong_t)&xrt_axigate_data},+{},+};++staticstructplatform_driverxrt_axigate_driver={+.driver={+.name=XRT_AXIGATE,+},+.probe=xrt_axigate_probe,+.remove=xrt_axigate_remove,+.id_table=xrt_axigate_table,+};++voidaxigate_leaf_init_fini(boolinit)+{+if(init){+xleaf_register_driver(XRT_SUBDEV_AXIGATE,+&xrt_axigate_driver,xrt_axigate_endpoints);+}else{+xleaf_unregister_driver(XRT_SUBDEV_AXIGATE);+}+}
Add UCS driver. UCS is a hardware function discovered by walking xclbin
metadata. A platform device node will be created for it.
UCS enables/disables the dynamic region clocks.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 +++++++++++++++++++++++++++
2 files changed, 259 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
@@ -0,0 +1,235 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAUCSDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/delay.h>+#include<linux/device.h>+#include<linux/io.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/ucs.h"+#include"xleaf/clock.h"++#define UCS_ERR(ucs, fmt, arg...) \+xrt_err((ucs)->pdev,fmt"\n",##arg)+#define UCS_WARN(ucs, fmt, arg...) \+xrt_warn((ucs)->pdev,fmt"\n",##arg)+#define UCS_INFO(ucs, fmt, arg...) \+xrt_info((ucs)->pdev,fmt"\n",##arg)+#define UCS_DBG(ucs, fmt, arg...) \+xrt_dbg((ucs)->pdev,fmt"\n",##arg)++#define XRT_UCS "xrt_ucs"++#define CHANNEL1_OFFSET 0+#define CHANNEL2_OFFSET 8++#define CLK_MAX_VALUE 6400++structucs_control_status_ch1{+unsignedintshutdown_clocks_latched:1;+unsignedintreserved1:15;+unsignedintclock_throttling_average:14;+unsignedintreserved2:2;+};++structxrt_ucs{+structplatform_device*pdev;+void__iomem*ucs_base;+structmutexucs_lock;/* ucs dev lock */+};++staticinlineu32reg_rd(structxrt_ucs*ucs,u32offset)+{+returnioread32(ucs->ucs_base+offset);+}++staticinlinevoidreg_wr(structxrt_ucs*ucs,u32val,u32offset)+{+iowrite32(val,ucs->ucs_base+offset);+}++staticvoidxrt_ucs_event_cb(structplatform_device*pdev,void*arg)+{+structplatform_device*leaf;+structxrt_event*evt=(structxrt_event*)arg;+enumxrt_eventse=evt->xe_evt;+enumxrt_subdev_idid=evt->xe_subdev.xevt_subdev_id;+intinstance=evt->xe_subdev.xevt_subdev_instance;++switch(e){+caseXRT_EVENT_POST_CREATION:+break;+default:+xrt_dbg(pdev,"ignored event %d",e);+return;+}++if(id!=XRT_SUBDEV_CLOCK)+return;++leaf=xleaf_get_leaf_by_id(pdev,XRT_SUBDEV_CLOCK,instance);+if(!leaf){+xrt_err(pdev,"does not get clock subdev");+return;+}++xleaf_ioctl(leaf,XRT_CLOCK_VERIFY,NULL);+xleaf_put_leaf(pdev,leaf);+}++staticvoiducs_check(structxrt_ucs*ucs,bool*latched)+{+structucs_control_status_ch1*ucs_status_ch1;+u32status;++mutex_lock(&ucs->ucs_lock);+status=reg_rd(ucs,CHANNEL1_OFFSET);+ucs_status_ch1=(structucs_control_status_ch1*)&status;+if(ucs_status_ch1->shutdown_clocks_latched){+UCS_ERR(ucs,+"Critical temperature or power event, kernel clocks have been stopped.");+UCS_ERR(ucs,+"run 'xbutil valiate -q' to continue. See AR 73398 for more details.");+/* explicitly indicate reset should be latched */+*latched=true;+}elseif(ucs_status_ch1->clock_throttling_average>+CLK_MAX_VALUE){+UCS_ERR(ucs,"kernel clocks %d exceeds expected maximum value %d.",+ucs_status_ch1->clock_throttling_average,+CLK_MAX_VALUE);+}elseif(ucs_status_ch1->clock_throttling_average){+UCS_ERR(ucs,"kernel clocks throttled at %d%%.",+(ucs_status_ch1->clock_throttling_average/+(CLK_MAX_VALUE/100)));+}+mutex_unlock(&ucs->ucs_lock);+}++staticvoiducs_enable(structxrt_ucs*ucs)+{+reg_wr(ucs,1,CHANNEL2_OFFSET);+}++staticint+xrt_ucs_leaf_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+structxrt_ucs*ucs;+intret=0;++ucs=platform_get_drvdata(pdev);++switch(cmd){+caseXRT_XLEAF_EVENT:+xrt_ucs_event_cb(pdev,arg);+break;+caseXRT_UCS_CHECK:{+ucs_check(ucs,(bool*)arg);+break;+}+caseXRT_UCS_ENABLE:+ucs_enable(ucs);+break;+default:+xrt_err(pdev,"unsupported cmd %d",cmd);+return-EINVAL;+}++returnret;+}++staticintucs_remove(structplatform_device*pdev)+{+structxrt_ucs*ucs;++ucs=platform_get_drvdata(pdev);+if(!ucs){+xrt_err(pdev,"driver data is NULL");+return-EINVAL;+}++if(ucs->ucs_base)+iounmap(ucs->ucs_base);++platform_set_drvdata(pdev,NULL);+devm_kfree(&pdev->dev,ucs);++return0;+}++staticintucs_probe(structplatform_device*pdev)+{+structxrt_ucs*ucs=NULL;+structresource*res;+intret;++ucs=devm_kzalloc(&pdev->dev,sizeof(*ucs),GFP_KERNEL);+if(!ucs)+return-ENOMEM;++platform_set_drvdata(pdev,ucs);+ucs->pdev=pdev;+mutex_init(&ucs->ucs_lock);++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+ucs->ucs_base=ioremap(res->start,res->end-res->start+1);+if(!ucs->ucs_base){+UCS_ERR(ucs,"map base %pR failed",res);+ret=-EFAULT;+gotofailed;+}+ucs_enable(ucs);++return0;++failed:+ucs_remove(pdev);+returnret;+}++staticstructxrt_subdev_endpointsxrt_ucs_endpoints[]={+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name=XRT_MD_NODE_UCS_CONTROL_STATUS},+{NULL},+},+.xse_min_ep=1,+},+{0},+};++staticstructxrt_subdev_drvdataxrt_ucs_data={+.xsd_dev_ops={+.xsd_ioctl=xrt_ucs_leaf_ioctl,+},+};++staticconststructplatform_device_idxrt_ucs_table[]={+{XRT_UCS,(kernel_ulong_t)&xrt_ucs_data},+{},+};++staticstructplatform_driverxrt_ucs_driver={+.driver={+.name=XRT_UCS,+},+.probe=ucs_probe,+.remove=ucs_remove,+.id_table=xrt_ucs_table,+};++voiducs_leaf_init_fini(boolinit)+{+if(init)+xleaf_register_driver(XRT_SUBDEV_UCS,&xrt_ucs_driver,xrt_ucs_endpoints);+else+xleaf_unregister_driver(XRT_SUBDEV_UCS);+}
Add DDR calibration driver. DDR calibration is a hardware function
discovered by walking firmware metadata. A platform device node will
be created for it. Hardware provides DDR calibration status through
this function.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/calib.h | 30 ++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 +++++++++++++++++++++++++
2 files changed, 256 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
Add devctl driver. devctl is a type of hardware function which only has
few registers to read or write. They are discovered by walking firmware
metadata. A platform device node will be created for them.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/devctl.h | 43 +++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++++++++++++++++++++
2 files changed, 249 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
@@ -0,0 +1,206 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAdevctlDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/delay.h>+#include<linux/device.h>+#include<linux/io.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/devctl.h"++#define XRT_DEVCTL "xrt_devctl"++structxrt_name_id{+char*ep_name;+intid;+};++staticstructxrt_name_idname_id[XRT_DEVCTL_MAX]={+{XRT_MD_NODE_BLP_ROM,XRT_DEVCTL_ROM_UUID},+{XRT_MD_NODE_GOLDEN_VER,XRT_DEVCTL_GOLDEN_VER},+};++structxrt_devctl{+structplatform_device*pdev;+void__iomem*base_addrs[XRT_DEVCTL_MAX];+ulongsizes[XRT_DEVCTL_MAX];+};++staticintxrt_devctl_name2id(structxrt_devctl*devctl,constchar*name)+{+inti;++for(i=0;i<XRT_DEVCTL_MAX&&name_id[i].ep_name;i++){+if(!strncmp(name_id[i].ep_name,name,strlen(name_id[i].ep_name)+1))+returnname_id[i].id;+}++return-EINVAL;+}++staticint+xrt_devctl_leaf_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+structxrt_devctl*devctl;+intret=0;++devctl=platform_get_drvdata(pdev);++switch(cmd){+caseXRT_XLEAF_EVENT:+/* Does not handle any event. */+break;+caseXRT_DEVCTL_READ:{+structxrt_devctl_ioctl_rw*rw_arg=arg;+u32*p_src,*p_dst,i;++if(rw_arg->xgir_len&0x3){+xrt_err(pdev,"invalid len %d",rw_arg->xgir_len);+return-EINVAL;+}++if(rw_arg->xgir_id>=XRT_DEVCTL_MAX){+xrt_err(pdev,"invalid id %d",rw_arg->xgir_id);+return-EINVAL;+}++p_src=devctl->base_addrs[rw_arg->xgir_id];+if(!p_src){+xrt_err(pdev,"io not found, id %d",+rw_arg->xgir_id);+return-EINVAL;+}+if(rw_arg->xgir_offset+rw_arg->xgir_len>+devctl->sizes[rw_arg->xgir_id]){+xrt_err(pdev,"invalid argument, off %d, len %d",+rw_arg->xgir_offset,rw_arg->xgir_len);+return-EINVAL;+}+p_dst=rw_arg->xgir_buf;+for(i=0;i<rw_arg->xgir_len/sizeof(u32);i++){+u32val=ioread32(p_src+rw_arg->xgir_offset+i);++memcpy(p_dst+i,&val,sizeof(u32));+}+break;+}+default:+xrt_err(pdev,"unsupported cmd %d",cmd);+return-EINVAL;+}++returnret;+}++staticintxrt_devctl_remove(structplatform_device*pdev)+{+structxrt_devctl*devctl;+inti;++devctl=platform_get_drvdata(pdev);++for(i=0;i<XRT_DEVCTL_MAX;i++){+if(devctl->base_addrs[i])+iounmap(devctl->base_addrs[i]);+}++platform_set_drvdata(pdev,NULL);+devm_kfree(&pdev->dev,devctl);++return0;+}++staticintxrt_devctl_probe(structplatform_device*pdev)+{+structxrt_devctl*devctl;+inti,id,ret=0;+structresource*res;++devctl=devm_kzalloc(&pdev->dev,sizeof(*devctl),GFP_KERNEL);+if(!devctl)+return-ENOMEM;++devctl->pdev=pdev;+platform_set_drvdata(pdev,devctl);++xrt_info(pdev,"probing...");+for(i=0,res=platform_get_resource(pdev,IORESOURCE_MEM,0);+res;+res=platform_get_resource(pdev,IORESOURCE_MEM,++i)){+id=xrt_devctl_name2id(devctl,res->name);+if(id<0){+xrt_err(pdev,"ep %s not found",res->name);+continue;+}+devctl->base_addrs[id]=ioremap(res->start,res->end-res->start+1);+if(!devctl->base_addrs[id]){+xrt_err(pdev,"map base failed %pR",res);+ret=-EIO;+gotofailed;+}+devctl->sizes[id]=res->end-res->start+1;+}++failed:+if(ret)+xrt_devctl_remove(pdev);++returnret;+}++staticstructxrt_subdev_endpointsxrt_devctl_endpoints[]={+{+.xse_names=(structxrt_subdev_ep_names[]){+/* add name if ep is in same partition */+{.ep_name=XRT_MD_NODE_BLP_ROM},+{NULL},+},+.xse_min_ep=1,+},+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name=XRT_MD_NODE_GOLDEN_VER},+{NULL},+},+.xse_min_ep=1,+},+/* adding ep bundle generates devctl device instance */+{0},+};++staticstructxrt_subdev_drvdataxrt_devctl_data={+.xsd_dev_ops={+.xsd_ioctl=xrt_devctl_leaf_ioctl,+},+};++staticconststructplatform_device_idxrt_devctl_table[]={+{XRT_DEVCTL,(kernel_ulong_t)&xrt_devctl_data},+{},+};++staticstructplatform_driverxrt_devctl_driver={+.driver={+.name=XRT_DEVCTL,+},+.probe=xrt_devctl_probe,+.remove=xrt_devctl_remove,+.id_table=xrt_devctl_table,+};++voiddevctl_leaf_init_fini(boolinit)+{+if(init)+xleaf_register_driver(XRT_SUBDEV_DEVCTL,&xrt_devctl_driver,xrt_devctl_endpoints);+else+xleaf_unregister_driver(XRT_SUBDEV_DEVCTL);+}
Add VSEC driver. VSEC is a hardware function discovered by walking
PCI Express configure space. A platform device node will be created
for it. VSEC provides board logic UUID and few offset of other hardware
functions.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++++++++++++++++++++++
1 file changed, 359 insertions(+)
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
@@ -0,0 +1,359 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAVSECDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/++#include<linux/platform_device.h>+#include"metadata.h"+#include"xleaf.h"++#define XRT_VSEC "xrt_vsec"++#define VSEC_TYPE_UUID 0x50+#define VSEC_TYPE_FLASH 0x51+#define VSEC_TYPE_PLATINFO 0x52+#define VSEC_TYPE_MAILBOX 0x53+#define VSEC_TYPE_END 0xff++#define VSEC_UUID_LEN 16++structxrt_vsec_header{+u32format;+u32length;+u32entry_sz;+u32rsvd;+}__packed;++#define head_rd(g, r) \+ioread32((void*)(g)->base+offsetof(structxrt_vsec_header,r))++#define GET_BAR(entry) (((entry)->bar_rev >> 4) & 0xf)+#define GET_BAR_OFF(_entry) \+({typeof(_entry)entry=(_entry);\+((entry)->off_lo|((u64)(entry)->off_hi<<16));})+#define GET_REV(entry) ((entry)->bar_rev & 0xf)++structxrt_vsec_entry{+u8type;+u8bar_rev;+u16off_lo;+u32off_hi;+u8ver_type;+u8minor;+u8major;+u8rsvd0;+u32rsvd1;+}__packed;++#define read_entry(g, i, e) \+do{\+u32*p=(u32*)((g)->base+\+sizeof(structxrt_vsec_header)+\+(i)*sizeof(structxrt_vsec_entry));\+u32off;\+for(off=0;\+off<sizeof(structxrt_vsec_entry)/4;\+off++)\+*((u32*)(e)+off)=ioread32(p+off);\+}while(0)++structvsec_device{+u8type;+char*ep_name;+ulongsize;+char*regmap;+};++staticstructvsec_devicevsec_devs[]={+{+.type=VSEC_TYPE_UUID,+.ep_name=XRT_MD_NODE_BLP_ROM,+.size=VSEC_UUID_LEN,+.regmap="vsec-uuid",+},+{+.type=VSEC_TYPE_FLASH,+.ep_name=XRT_MD_NODE_FLASH_VSEC,+.size=4096,+.regmap="vsec-flash",+},+{+.type=VSEC_TYPE_PLATINFO,+.ep_name=XRT_MD_NODE_PLAT_INFO,+.size=4,+.regmap="vsec-platinfo",+},+{+.type=VSEC_TYPE_MAILBOX,+.ep_name=XRT_MD_NODE_MAILBOX_VSEC,+.size=48,+.regmap="vsec-mbx",+},+};++structxrt_vsec{+structplatform_device*pdev;+void*base;+ulonglength;++char*metadata;+charuuid[VSEC_UUID_LEN];+};++staticchar*type2epname(u32type)+{+inti;++for(i=0;i<ARRAY_SIZE(vsec_devs);i++){+if(vsec_devs[i].type==type)+return(vsec_devs[i].ep_name);+}++returnNULL;+}++staticulongtype2size(u32type)+{+inti;++for(i=0;i<ARRAY_SIZE(vsec_devs);i++){+if(vsec_devs[i].type==type)+return(vsec_devs[i].size);+}++return0;+}++staticchar*type2regmap(u32type)+{+inti;++for(i=0;i<ARRAY_SIZE(vsec_devs);i++){+if(vsec_devs[i].type==type)+return(vsec_devs[i].regmap);+}++returnNULL;+}++staticintxrt_vsec_add_node(structxrt_vsec*vsec,+void*md_blob,structxrt_vsec_entry*p_entry)+{+structxrt_md_endpointep;+charregmap_ver[64];+intret;++if(!type2epname(p_entry->type))+return-EINVAL;++/*+*VSECmayhavemorethan1mailboxinstanceforthecard+*whichhasmorethan1physicalfunction.+*Thisisnotsupportedfornow.Assumingonlyonemailbox+*/++snprintf(regmap_ver,sizeof(regmap_ver)-1,"%d-%d.%d.%d",+p_entry->ver_type,p_entry->major,p_entry->minor,+GET_REV(p_entry));+ep.ep_name=type2epname(p_entry->type);+ep.bar=GET_BAR(p_entry);+ep.bar_off=GET_BAR_OFF(p_entry);+ep.size=type2size(p_entry->type);+ep.regmap=type2regmap(p_entry->type);+ep.regmap_ver=regmap_ver;+ret=xrt_md_add_endpoint(DEV(vsec->pdev),vsec->metadata,&ep);+if(ret){+xrt_err(vsec->pdev,"add ep failed, ret %d",ret);+gotofailed;+}++failed:+returnret;+}++staticintxrt_vsec_create_metadata(structxrt_vsec*vsec)+{+structxrt_vsec_entryentry;+inti,ret;++ret=xrt_md_create(&vsec->pdev->dev,&vsec->metadata);+if(ret){+xrt_err(vsec->pdev,"create metadata failed");+returnret;+}++for(i=0;i*sizeof(entry)<vsec->length-+sizeof(structxrt_vsec_header);i++){+read_entry(vsec,i,&entry);+xrt_vsec_add_node(vsec,vsec->metadata,&entry);+}++return0;+}++staticintxrt_vsec_ioctl(structplatform_device*pdev,u32cmd,void*arg)+{+intret=0;++switch(cmd){+caseXRT_XLEAF_EVENT:+/* Does not handle any event. */+break;+default:+ret=-EINVAL;+xrt_err(pdev,"should never been called");+break;+}++returnret;+}++staticintxrt_vsec_mapio(structxrt_vsec*vsec)+{+structxrt_subdev_platdata*pdata=DEV_PDATA(vsec->pdev);+constu32*bar;+constu64*bar_off;+structresource*res=NULL;+ulongaddr;+intret;++if(!pdata||xrt_md_size(DEV(vsec->pdev),pdata->xsp_dtb)==XRT_MD_INVALID_LENGTH){+xrt_err(vsec->pdev,"empty metadata");+return-EINVAL;+}++ret=xrt_md_get_prop(DEV(vsec->pdev),pdata->xsp_dtb,XRT_MD_NODE_VSEC,+NULL,XRT_MD_PROP_BAR_IDX,(constvoid**)&bar,NULL);+if(ret){+xrt_err(vsec->pdev,"failed to get bar idx, ret %d",ret);+return-EINVAL;+}++ret=xrt_md_get_prop(DEV(vsec->pdev),pdata->xsp_dtb,XRT_MD_NODE_VSEC,+NULL,XRT_MD_PROP_OFFSET,(constvoid**)&bar_off,NULL);+if(ret){+xrt_err(vsec->pdev,"failed to get bar off, ret %d",ret);+return-EINVAL;+}++xrt_info(vsec->pdev,"Map vsec at bar %d, offset 0x%llx",+be32_to_cpu(*bar),be64_to_cpu(*bar_off));++xleaf_get_barres(vsec->pdev,&res,be32_to_cpu(*bar));+if(!res){+xrt_err(vsec->pdev,"failed to get bar addr");+return-EINVAL;+}++addr=res->start+(ulong)be64_to_cpu(*bar_off);++vsec->base=ioremap(addr,sizeof(structxrt_vsec_header));+if(!vsec->base){+xrt_err(vsec->pdev,"Map header failed");+return-EIO;+}++vsec->length=head_rd(vsec,length);+iounmap(vsec->base);+vsec->base=ioremap(addr,vsec->length);+if(!vsec->base){+xrt_err(vsec->pdev,"map failed");+return-EIO;+}++return0;+}++staticintxrt_vsec_remove(structplatform_device*pdev)+{+structxrt_vsec*vsec;++vsec=platform_get_drvdata(pdev);++if(vsec->base){+iounmap(vsec->base);+vsec->base=NULL;+}++vfree(vsec->metadata);++return0;+}++staticintxrt_vsec_probe(structplatform_device*pdev)+{+structxrt_vsec*vsec;+intret=0;++vsec=devm_kzalloc(&pdev->dev,sizeof(*vsec),GFP_KERNEL);+if(!vsec)+return-ENOMEM;++vsec->pdev=pdev;+platform_set_drvdata(pdev,vsec);++ret=xrt_vsec_mapio(vsec);+if(ret)+gotofailed;++ret=xrt_vsec_create_metadata(vsec);+if(ret){+xrt_err(pdev,"create metadata failed, ret %d",ret);+gotofailed;+}+ret=xleaf_create_group(pdev,vsec->metadata);+if(ret<0)+xrt_err(pdev,"create group failed, ret %d",ret);+else+ret=0;++failed:+if(ret)+xrt_vsec_remove(pdev);++returnret;+}++staticstructxrt_subdev_endpointsxrt_vsec_endpoints[]={+{+.xse_names=(structxrt_subdev_ep_names[]){+{.ep_name=XRT_MD_NODE_VSEC},+{NULL},+},+.xse_min_ep=1,+},+{0},+};++staticstructxrt_subdev_drvdataxrt_vsec_data={+.xsd_dev_ops={+.xsd_ioctl=xrt_vsec_ioctl,+},+};++staticconststructplatform_device_idxrt_vsec_table[]={+{XRT_VSEC,(kernel_ulong_t)&xrt_vsec_data},+{},+};++staticstructplatform_driverxrt_vsec_driver={+.driver={+.name=XRT_VSEC,+},+.probe=xrt_vsec_probe,+.remove=xrt_vsec_remove,+.id_table=xrt_vsec_table,+};++voidvsec_leaf_init_fini(boolinit)+{+if(init)+xleaf_register_driver(XRT_SUBDEV_VSEC,&xrt_vsec_driver,xrt_vsec_endpoints);+else+xleaf_unregister_driver(XRT_SUBDEV_VSEC);+}
drivers/fpga/xrt/lib/main.c:144:6: warning: assignment to 'struct xrt_drv_map *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
144 | map = vzalloc(sizeof(*map));
| ^
drivers/fpga/xrt/lib/main.c: In function 'xleaf_unregister_driver':
quoted
drivers/fpga/xrt/lib/main.c:181:2: error: implicit declaration of function 'vfree'; did you mean 'kvfree'? [-Werror=implicit-function-declaration]
From: Tom Rix <trix@redhat.com> Date: 2021-02-18 16:41:17
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Hello,
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Thanks for refreshing this patchset.
It will take me a while to do the full review, so I thought I would give some early feed back.
It applies to char-misc-next, but will have conflicts with in-flight patches around the MAINTAINERS file. This is not a big deal.
The checkpatch is much better over v2, the complaints are
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#21:
new file mode 100644
WARNING: From:/Signed-off-by: email address mismatch: 'From: Lizhi Hou [off-list ref]' != 'Signed-off-by: Lizhi Hou [off-list ref]'
MAINTAINERS warning i believe you address in the last patch.
In the next revisions, please fix the signoff.
The test robot is complaining about hppa64. While it may be an unlikely config, it would be best to fix it.
Tom
Alveo PCIe FPGA based platforms have a static *shell* partition and a partial
re-configurable *user* partition. The shell partition is automatically loaded from
flash when host is booted and PCIe is enumerated by BIOS. Shell cannot be changed
till the next cold reboot. The shell exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo platform,
XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA image using
Vitis https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
tools. The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap the image
running on the user partition in order to switch between different workloads by
loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xmgmt* binds to management physical function of Alveo
platform. The modular driver framework is organized into several platform drivers
which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst. Unlike previous V1 and V2 version
of patch series, V3 version does not require export of libfdt symbols.
TESTING AND VALIDATION
xmgmt driver can be tested with full XRT open source stack which includes user
space libraries, board utilities and (out of tree) first generation user physical
function driver xocl. XRT open source runtime stack is available at
https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on Alveo/XRT
security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.htmlhttps://xilinx.github.io/XRT/master/html/security.htmlhttps://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V1 version of patch series can be found here:
https://lore.kernel.org/lkml/20201217075046.28553-1-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-2-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-3-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-4-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-5-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-6-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-7-sonals@xilinx.com/
Lizhi Hou (18):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib platform driver manager
fpga: xrt: group platform driver
fpga: xrt: platform driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main platform driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: VSEC platform driver
fpga: xrt: UCS platform driver
fpga: xrt: ICAP platform driver
fpga: xrt: devctl platform driver
fpga: xrt: clock platform driver
fpga: xrt: clock frequence counter platform driver
fpga: xrt: DDR calibration platform driver
fpga: xrt: partition isolation platform driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842 ++++++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 4 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 48 ++
drivers/fpga/xrt/include/group.h | 27 +
drivers/fpga/xrt/include/metadata.h | 229 ++++++
drivers/fpga/xrt/include/subdev_id.h | 43 ++
drivers/fpga/xrt/include/xclbin-helper.h | 52 ++
drivers/fpga/xrt/include/xleaf.h | 276 +++++++
drivers/fpga/xrt/include/xleaf/axigate.h | 25 +
drivers/fpga/xrt/include/xleaf/calib.h | 30 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +
drivers/fpga/xrt/include/xleaf/clock.h | 31 +
drivers/fpga/xrt/include/xleaf/devctl.h | 43 ++
drivers/fpga/xrt/include/xleaf/icap.h | 29 +
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +
drivers/fpga/xrt/include/xmgmt-main.h | 37 +
drivers/fpga/xrt/include/xroot.h | 114 +++
drivers/fpga/xrt/lib/Kconfig | 16 +
drivers/fpga/xrt/lib/Makefile | 30 +
drivers/fpga/xrt/lib/cdev.c | 231 ++++++
drivers/fpga/xrt/lib/group.c | 265 +++++++
drivers/fpga/xrt/lib/main.c | 274 +++++++
drivers/fpga/xrt/lib/main.h | 17 +
drivers/fpga/xrt/lib/subdev.c | 871 +++++++++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 ++++++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 ++++++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 ++++++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 +++++++++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 ++++++
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++
drivers/fpga/xrt/lib/xroot.c | 598 ++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 524 ++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/fmgr-drv.c | 187 +++++
drivers/fpga/xrt/mgmt/fmgr.h | 28 +
drivers/fpga/xrt/mgmt/main-impl.h | 37 +
drivers/fpga/xrt/mgmt/main-region.c | 471 ++++++++++++
drivers/fpga/xrt/mgmt/main.c | 693 ++++++++++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++
include/uapi/linux/xrt/xmgmt-ioctl.h | 46 ++
53 files changed, 9957 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/main.c
create mode 100644 drivers/fpga/xrt/lib/main.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
create mode 100644 drivers/fpga/xrt/mgmt/main-impl.h
create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/main.c
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h
Hello,
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Thanks for refreshing this patchset.
It will take me a while to do the full review, so I thought I would give some early feed back.
It applies to char-misc-next, but will have conflicts with in-flight patches around the MAINTAINERS file. This is not a big deal.
The checkpatch is much better over v2, the complaints are
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#21:
new file mode 100644
WARNING: From:/Signed-off-by: email address mismatch: 'From: Lizhi Hou [off-list ref]' != 'Signed-off-by: Lizhi Hou [off-list ref]'
MAINTAINERS warning i believe you address in the last patch.
In the next revisions, please fix the signoff.
The test robot is complaining about hppa64. While it may be an unlikely config, it would be best to fix it.
Thanks for reviewing. I will fix signoff, hppa64 and arm build issue
reported by robot in next revision.
Tom
quoted
Alveo PCIe FPGA based platforms have a static *shell* partition and a partial
re-configurable *user* partition. The shell partition is automatically loaded from
flash when host is booted and PCIe is enumerated by BIOS. Shell cannot be changed
till the next cold reboot. The shell exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo platform,
XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA image using
Vitis https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
tools. The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap the image
running on the user partition in order to switch between different workloads by
loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xmgmt* binds to management physical function of Alveo
platform. The modular driver framework is organized into several platform drivers
which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst. Unlike previous V1 and V2 version
of patch series, V3 version does not require export of libfdt symbols.
TESTING AND VALIDATION
xmgmt driver can be tested with full XRT open source stack which includes user
space libraries, board utilities and (out of tree) first generation user physical
function driver xocl. XRT open source runtime stack is available at
https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on Alveo/XRT
security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.htmlhttps://xilinx.github.io/XRT/master/html/security.htmlhttps://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V1 version of patch series can be found here:
https://lore.kernel.org/lkml/20201217075046.28553-1-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-2-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-3-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-4-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-5-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-6-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-7-sonals@xilinx.com/
Lizhi Hou (18):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib platform driver manager
fpga: xrt: group platform driver
fpga: xrt: platform driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main platform driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: VSEC platform driver
fpga: xrt: UCS platform driver
fpga: xrt: ICAP platform driver
fpga: xrt: devctl platform driver
fpga: xrt: clock platform driver
fpga: xrt: clock frequence counter platform driver
fpga: xrt: DDR calibration platform driver
fpga: xrt: partition isolation platform driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842 ++++++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 4 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 48 ++
drivers/fpga/xrt/include/group.h | 27 +
drivers/fpga/xrt/include/metadata.h | 229 ++++++
drivers/fpga/xrt/include/subdev_id.h | 43 ++
drivers/fpga/xrt/include/xclbin-helper.h | 52 ++
drivers/fpga/xrt/include/xleaf.h | 276 +++++++
drivers/fpga/xrt/include/xleaf/axigate.h | 25 +
drivers/fpga/xrt/include/xleaf/calib.h | 30 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +
drivers/fpga/xrt/include/xleaf/clock.h | 31 +
drivers/fpga/xrt/include/xleaf/devctl.h | 43 ++
drivers/fpga/xrt/include/xleaf/icap.h | 29 +
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +
drivers/fpga/xrt/include/xmgmt-main.h | 37 +
drivers/fpga/xrt/include/xroot.h | 114 +++
drivers/fpga/xrt/lib/Kconfig | 16 +
drivers/fpga/xrt/lib/Makefile | 30 +
drivers/fpga/xrt/lib/cdev.c | 231 ++++++
drivers/fpga/xrt/lib/group.c | 265 +++++++
drivers/fpga/xrt/lib/main.c | 274 +++++++
drivers/fpga/xrt/lib/main.h | 17 +
drivers/fpga/xrt/lib/subdev.c | 871 +++++++++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 ++++++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 ++++++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 ++++++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 +++++++++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 ++++++
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++
drivers/fpga/xrt/lib/xroot.c | 598 ++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 524 ++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/fmgr-drv.c | 187 +++++
drivers/fpga/xrt/mgmt/fmgr.h | 28 +
drivers/fpga/xrt/mgmt/main-impl.h | 37 +
drivers/fpga/xrt/mgmt/main-region.c | 471 ++++++++++++
drivers/fpga/xrt/mgmt/main.c | 693 ++++++++++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++
include/uapi/linux/xrt/xmgmt-ioctl.h | 46 ++
53 files changed, 9957 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/main.c
create mode 100644 drivers/fpga/xrt/lib/main.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
create mode 100644 drivers/fpga/xrt/mgmt/main-impl.h
create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/main.c
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h
From: Tom Rix <trix@redhat.com> Date: 2021-02-19 22:27:58
From the documentation, there are a couple of big questions and a bunch of word smithing.
pseudo-bus : do we need a bus ?
xrt-lib real platform devices that aren't fpga, do they need to move to another subsystem ?
Overall looks good, love the ascii art!
On 2/17/21 10:40 PM, Lizhi Hou wrote:
@@ -0,0 +1,842 @@+.. SPDX-License-Identifier: GPL-2.0++==================================+XRTV2 Linux Kernel Driver Overview+==================================++Authors:++* Sonal Santan <sonal.santan@xilinx.com>+* Max Zhen <max.zhen@xilinx.com>+* Lizhi Hou <lizhi.hou@xilinx.com>++XRTV2 drivers are second generation `XRT <https://github.com/Xilinx/XRT>`_+drivers which support `Alveo <https://www.xilinx.com/products/boards-and-kits/alveo.html>`_+PCIe platforms from Xilinx.++XRTV2 drivers support *subsystem* style data driven platforms where driver's
where the driver's
+configuration and behavior is determined by meta data provided by the platform
+(in *device tree* format). Primary management physical function (MPF) driver
+is called **xmgmt**. Primary user physical function (UPF) driver is called
+**xuser** and is under development. xrt driver framework and HW subsystem
+drivers are packaged into a library module called **xrt-lib**, which is
+shared by **xmgmt** and **xuser** (under development). The xrt driver framework
xuser still under development ?
+implements a pseudo-bus which is used to discover HW subsystems and facilitate
+inter HW subsystem interaction.
+
+Driver Modules
+==============
+
+xrt-lib.ko
+----------
+
+Repository of all subsystem drivers and pure software modules that can potentially
subsystem drivers
drivers in fpga/ should be for managing just the fpganess of the fpga.
soft devices ex/ a soft tty should go to their respective subsystem location
Are there any in this patchset you think might move ?
Maybe we can defer reviewing those now.
+be shared between xmgmt and xuser. All these drivers are structured as Linux
+*platform driver* and are instantiated by xmgmt (or xuser under development) based
+on meta data associated with hardware. The metadata is in the form of device tree
with the hardware
form of a device tree
+as mentioned before. Each platform driver statically defines a subsystem node
+array by using node name or a string in its ``compatible`` property. And this
+array is eventually translated to IOMEM resources of the platform device.
+
+The xrt-lib core infrastructure provides hooks to platform drivers for device node
+management, user file operations and ioctl callbacks. The core also provides pseudo-bus
+functionality for platform driver registration, discovery and inter platform driver
+ioctl calls.
core infrastructure.
The interfaces to the infrastructure are not in include/linux/fpga/
Maybe this needs to change.
+
+.. note::
+ See code in ``include/xleaf.h``
+
+
+xmgmt.ko
+--------
+
+The xmgmt driver is a PCIe device driver driving MPF found on Xilinx's Alveo
+PCIE device. It consists of one *root* driver, one or more *group* drivers
+and one or more *xleaf* drivers. The root and MPF specific xleaf drivers are
+in xmgmt.ko. The group driver and other xleaf drivers are in xrt-lib.ko.
I am not sure if *.ko is correct, these will also be intree.
+
+The instantiation of specific group driver or xleaf driver is completely data
of a specific
+driven based on meta data (mostly in device tree format) found through VSEC
mostly ? what is the deviation from device tree ?
+capability and inside firmware files, such as platform xsabin or user xclbin file.
+The root driver manages life cycle of multiple group drivers, which, in turn,
the life cycle
+manages multiple xleaf drivers. This allows a single set of driver code to support
set of drivers
drop 'code'
+all kinds of subsystems exposed by different shells. The difference among all
+these subsystems will be handled in xleaf drivers with root and group drivers
+being part of the infrastructure and provide common services for all leaves
+found on all platforms.
+
+The driver object model looks like the following::
+
+ +-----------+
+ | xroot |
+ +-----+-----+
+ |
+ +-----------+-----------+
+ | |
+ v v
+ +-----------+ +-----------+
+ | group | ... | group |
+ +-----+-----+ +------+----+
+ | |
+ | |
+ +-----+----+ +-----+----+
+ | | | |
+ v v v v
+ +-------+ +-------+ +-------+ +-------+
+ | xleaf |..| xleaf | | xleaf |..| xleaf |
+ +-------+ +-------+ +-------+ +-------+
+
+As an example for Xilinx Alveo U50 before user xclbin download, the tree
+looks like the following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+
+ | | |
+ v v v
+ +--------+ +--------+ +--------+
+ | group0 | | group1 | | group2 |
+ +----+---+ +----+---+ +---+----+
+ | | |
+ | | |
+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
+ | | | | | | | | |
+ v v | v v | v v |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ |
+ | +---------+ | +------+ +-----------+ |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
+ +---------+ | +------+ +-----------+
+ | +-------+
+ +->| CALIB |
+ +-------+
+
Nice ascii art!
+After an xclbin is download, group3 will be added and the tree looks like the
+following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+-----------------+
+ | | | |
+ v v v |
+ +--------+ +--------+ +--------+ |
+ | group0 | | group1 | | group2 | |
+ +----+---+ +----+---+ +---+----+ |
+ | | | |
+ | | | |
+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
+ | | | | | | | | | |
+ v v | v v | v v | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | +---------+ | +------+ +-----------+ | |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
+ +---------+ | +------+ +-----------+ |
+ | +-------+ |
+ +->| CALIB | |
+ +-------+ |
+ +---+----+ |
+ | group3 |<--------------------------------------------+
+ +--------+
+ |
+ |
+ +-------+--------+---+--+--------+------+-------+
+ | | | | | | |
+ v | v | v | v
+ +--------+ | +--------+ | +--------+ | +-----+
+ | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
+ +--------+ v +--------+ v +--------+ v +-----+
+ +-------------+ +-------------+ +-------------+
+ | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
+ +-------------+ +-------------+ +-------------+
+
+
+xmgmt-root
+^^^^^^^^^^
+
+The xmgmt-root driver is a PCIe device driver attached to MPF. It's part of the
+infrastructure of the MPF driver and resides in xmgmt.ko. This driver
+
+* manages one or more group drivers
+* provides access to functionalities that requires pci_dev, such as PCIE config
+ space access, to other xleaf drivers through root calls
+* together with group driver, facilities event callbacks for other xleaf drivers
+* together with group driver, facilities inter-leaf driver calls for other xleaf
Maybe drop 'together with group driver'
+ drivers
+
+When root driver starts, it will explicitly create an initial group instance,
+which contains xleaf drivers that will trigger the creation of other group
+instances. The root driver will wait for all group and leaves to be created
+before it returns from it's probe routine and claim success of the
+initialization of the entire xmgmt driver.
What happens if there a failure in one leaf ? Does the whole board go down ?
+
+.. note::
+ See code in ``lib/xroot.c`` and ``mgmt/root.c``
+
+
+group
+^^^^^
+
+The group driver is a platform device driver whose life cycle is managed by
Maybe call this a 'pseudo device'
+root and does not have real IO mem or IRQ resources. It's part of the
+infrastructure of the MPF driver and resides in xrt-lib.ko. This driver
+
+* manages one or more xleaf drivers so that multiple leaves can be managed as a
+ group
can drop 'so that multiple leaves can be managed as a group' to me, this is the same as 'one or more'
+* provides access to root from leaves, so that root calls, event notifications
+ and inter-leaf calls can happen
+
+In xmgmt, an initial group driver instance will be created by root, which
by the root
+contains leaves that will trigger group instances to be created to manage
+groups of leaves found on different partitions on hardware, such as VSEC, Shell,
+and User.
+
+Every *fpga_region* has a group object associated with it. The group is
+created when xclbin image is loaded on the fpga_region. The existing group
+is destroyed when a new xclbin image is loaded. The fpga_region persists
+across xclbin downloads.
The connection of a 'group' node to a fpga region region is fairly important, maybe move this section earlier. 'group' as an fpganess thing would be kept in fpga/ subsystem.
+
+.. note::
+ See code in ``lib/group.c``
+
+
+xleaf
+^^^^^
+
+The xleaf driver is a platform device driver whose life cycle is managed by
+a group driver and may or may not have real IO mem or IRQ resources. They
+are the real meat of xmgmt and contains platform specific code to Shell and
+User found on a MPF.
+
Maybe a split is pseudo device leaves, those without real IO mem, stay in fpga/ others go ?
+A xleaf driver may not have real hardware resources when it merely acts as a
+driver that manages certain in-memory states for xmgmt. These in-memory states
+could be shared by multiple other leaves.
+
This implies locking and some message passing.
+Leaf drivers assigned to specific hardware resources drive specific subsystem in
drive a specific
+the device. To manipulate the subsystem or carry out a task, a xleaf driver may
+ask help from root via root calls and/or from other leaves via inter-leaf calls.
+
+A xleaf can also broadcast events through infrastructure code for other leaves
+to process. It can also receive event notification from infrastructure about
+certain events, such as post-creation or pre-exit of a particular xleaf.
I would like to see some examples of how the inter node communications work.
+
+.. note::
+ See code in ``lib/xleaf/*.c``
+
+
+FPGA Manager Interaction
+========================
+
+fpga_manager
+------------
+
+An instance of fpga_manager is created by xmgmt_main and is used for xclbin
for the xclbin
+image download. fpga_manager requires the full xclbin image before it can
+start programming the FPGA configuration engine via ICAP platform driver.
via the ICAP
what is ICAP ?
+
+fpga_region
+-----------
+
+For every interface exposed by currently loaded xclbin/xsabin in the *parent*
by the currently
+fpga_region a new instance of fpga_region is created like a *child* region.
fpga_region,
+The device tree of the *parent* fpga_region defines the
+resources for a new instance of fpga_bridge which isolates the parent from
and isolates
+child fpga_region. This new instance of fpga_bridge will be used when a
+xclbin image is loaded on the child fpga_region. After the xclbin image is
+downloaded to the fpga_region, an instance of group is created for the
+fpga_region using the device tree obtained as part of xclbin. If this device
of the xclbin
+tree defines any child interfaces then it can trigger the creation of
interfaces, then
+fpga_bridge and fpga_region for the next region in the chain.
a fpga_bridge and a fpga_region
+
+fpga_bridge
+-----------
+
+Like fpga_region, matching fpga_bridge is also created by walking the device
Like the fpga_region, a matchin
+tree of the parent group.
+
+Driver Interfaces
+=================
+
+xmgmt Driver Ioctls
+-------------------
+
+Ioctls exposed by xmgmt driver to user space are enumerated in the following
+table:
+
+== ===================== ============================ ==========================
+# Functionality ioctl request code data format
+== ===================== ============================ ==========================
+1 FPGA image download XMGMT_IOCICAPDOWNLOAD_AXLF xmgmt_ioc_bitstream_axlf
+== ===================== ============================ ==========================
This data format is described below, maybe swap this section with that so
folks will know what xmgmnt_ioc_bitstream_axlf is before this section.
+
+User xclbin can be downloaded by using xbmgmt tool from XRT open source suite. See
A user xclbin
using the xbmgmt
from the XRT
+example usage below::
+
+ xbmgmt partition --program --path /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xclbin --force
+
+xmgmt Driver Sysfs
+------------------
+
+xmgmt driver exposes a rich set of sysfs interfaces. Subsystem platform
+drivers export sysfs node for every platform instance.
+
+Every partition also exports its UUIDs. See below for examples::
+
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
+
+
+hwmon
+-----
+
+xmgmt driver exposes standard hwmon interface to report voltage, current,
+temperature, power, etc. These can easily be viewed using *sensors* command
+line utility.
+
+Alveo Platform Overview
+=======================
+
+Alveo platforms are architected as two physical FPGA partitions: *Shell* and
+*User*. The Shell provides basic infrastructure for the Alveo platform like
+PCIe connectivity, board management, Dynamic Function Exchange (DFX), sensors,
+clocking, reset, and security. User partition contains user compiled FPGA
the user compiled
+binary which is loaded by a process called DFX also known as partial
+reconfiguration.
+
+Physical partitions require strict HW compatibility with each other for DFX to
+work properly.
swap order
For DFX to work properly physical partitions ..
Every physical partition has two interface UUIDs: *parent* UUID
+and *child* UUID. For simple single stage platforms, Shell → User forms parent
+child relationship. For complex two stage platforms, Base → Shell → User forms
+the parent child relationship chain.
this bit is confusing. is this related to uuid?
+
+.. note::
+ Partition compatibility matching is key design component of Alveo platforms
+ and XRT. Partitions have child and parent relationship. A loaded partition
have a child
+ exposes child partition UUID to advertise its compatibility requirement for
the child's
can drop 'for child partition'
+ child partition. When loading a child partition the xmgmt management driver
When loading a child partition,
+ matches parent UUID of the child partition against child UUID exported by
+ the parent. Parent and child partition UUIDs are stored in the *xclbin*
+ (for user) or *xsabin* (for base and shell).
this is confusing, is this part of the file image format ?
Maybe save/move till the image layout.
Except for root UUID, VSEC,
+ hardware itself does not know about UUIDs. UUIDs are stored in xsabin and
+ xclbin.
This is confusing too, not sure how to untangle.
+
+
+The physical partitions and their loading is illustrated below::
+
+ SHELL USER
+ +-----------+ +-------------------+
+ | | | |
+ | VSEC UUID | CHILD PARENT | LOGIC UUID |
+ | o------->|<--------o |
+ | | UUID UUID | |
+ +-----+-----+ +--------+----------+
+ | |
+ . .
+ | |
+ +---+---+ +------+--------+
+ | POR | | USER COMPILED |
+ | FLASH | | XCLBIN |
+ +-------+ +---------------+
+
+
+Loading Sequence
+----------------
+
+The Shell partition is loaded from flash at system boot time. It establishes the
+PCIe link and exposes two physical functions to the BIOS. After OS boot, xmgmt
the OS boots, the xmgmt
+driver attaches to PCIe physical function 0 exposed by the Shell and then looks
+for VSEC in PCIe extended configuration space. Using VSEC it determines the logic
the PCIe
The driver uses VSEC to determine the UUID of Shell. The UUID is also used to load a matching ...
+UUID of Shell and uses the UUID to load matching *xsabin* file from Linux firmware
+directory. The xsabin file contains metadata to discover peripherals that are part
+of Shell and firmware(s) for any embedded soft processors in Shell.
the firmware needed for any ...
+
+The Shell exports child interface UUID which is used for compatibility check when
export a child
for a compatibility check
+loading user compiled xclbin over the User partition as part of DFX. When a user
+requests loading of a specific xclbin the xmgmt management driver reads the parent
xclbin, the
+interface UUID specified in the xclbin and matches it with child interface UUID
+exported by Shell to determine if xclbin is compatible with the Shell. If match
+fails loading of xclbin is denied.
+
+xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl command. When loading
+xclbin, xmgmt driver performs the following *logical* operations:
+
+1. Copy xclbin from user to kernel memory
+2. Sanity check the xclbin contents
+3. Isolate the User partition
+4. Download the bitstream using the FPGA config engine (ICAP)
+5. De-isolate the User partition
+6. Program the clocks (ClockWiz) driving the User partition
+
+xsabin
+------
+
+Each Alveo platform comes packaged with its own xsabin. The xsabin is trusted
is a trusted
+component of the platform. For format details refer to :ref:`xsabin_xclbin_container_format`
+below. xsabin contains basic information like UUIDs, platform name and metadata in the
+form of device tree. See :ref:`device_tree_usage` below for details and example.
+tool set from Xilinx. The xclbin contains sections describing user compiled
+acceleration engines/kernels, memory subsystems, clocking information etc. It also
+contains bitstream for the user partition, UUIDs, platform name, etc. xclbin uses
bitstreams
+the same container format as xsabin which is described below.
+
+
+.. _xsabin_xclbin_container_format:
+
+xsabin/xclbin Container Format
+------------------------------
+
+xclbin/xsabin is ELF-like binary container format. It is structured as series of
+sections. There is a file header followed by several section headers which is
+followed by sections. A section header points to an actual section. There is an
+optional signature at the end. The format is defined by header file ``xclbin.h``.
+The following figure illustrates a typical xclbin::
+
+
+ +---------------------+
+ | |
+ | HEADER |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | SIGNATURE |
+ | (OPTIONAL) |
+ +---------------------+
+
+
+xclbin/xsabin files can be packaged, un-packaged and inspected using XRT utility
+called **xclbinutil**. xclbinutil is part of XRT open source software stack. The
+source code for xclbinutil can be found at
+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbinutil
+
Works, but maybe the location of a manpage or doc would be better.
+
+/* Note: res_id is defined by leaf driver and must start with 0. */
+struct xrt_iores_map {
+ char *res_name;
+ int res_id;
+};
+
+static inline int xrt_md_res_name2id(const struct xrt_iores_map *res_map,
+ int entry_num, const char *res_name)
+{
+ int i;
+
+ for (i = 0; i < entry_num; i++) {
+ if (!strcmp(res_name, res_map->res_name))
Use the 'n' variant strncmp for better safety.
Fix generally.
A wrapping a single call seems like an unnecessary abstraction layer.
can this be reduced/removed ?
+}
+
+/*
+ * Firmware provides 128 bit hash string as unque id of partition/interface.
The firmware provides a 128 bit hash string as a unique id to the partition/interface.
Existing hw does not yet use the cononical form, so it is necessary to use a translation function.
+ * This string will be canonical textual representation in the future.
+ * Before that, introducing these two functions below to translate
+ * hash string to uuid_t for released hardware.
Is there an existing version string the new hw will use to check which way to go ?
+ */
+static inline void xrt_md_trans_uuid2str(const uuid_t *uuid, char *uuidstr)
+{
+ int i, p;
+ u8 *u = (u8 *)uuid;
+
+ for (p = 0, i = sizeof(uuid_t) - 1; i >= 0; p++, i--)
This loop needs to be improved.
Consider if sizeof(uuid_t) changed, accessing u[] would overflow.
From: Tom Rix <trix@redhat.com> Date: 2021-02-21 14:59:22
As I am looking through the files, I have this comment.
fpga/ is currently a single directory, while files could be organized in subdirectories like
dfl/pci.c
instead have the possible subdir name as a prefix to the filename.
dfl-pci.c
For consistency,
xrt/metadata/metadata.c
should be
xrt-metadata.c
Likewise the build infra needs to integrated within the existing files fpga/Kconfig,Makefile
This is a bigish refactor, so let's get a second opinion.
Moritz ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Update fpga Kconfig/Makefile and add Kconfig/Makefile for new drivers.
Expand the comment, there are several new configs that could use an explanation
#defines should have a prefix, maybe XRT_ or XCLBIN_
+
+enum axlf_section_kind;
+struct axlf;
+
+/**
+ * Bitstream header information as defined by Xilinx tools.
+ * Please note that this struct definition is not owned by the driver.
+ */
+struct hw_icap_bit_header {
File headers usually have fixed length fields like uint32_t
Is this a structure the real header is converted into ?
+ unsigned int header_length; /* Length of header in 32 bit words */
+ unsigned int bitstream_length; /* Length of bitstream to read in bytes*/
+ unsigned char *design_name; /* Design name get from bitstream */
+ unsigned char *part_name; /* Part name read from bitstream */
+ unsigned char *date; /* Date read from bitstream header */
+ unsigned char *time; /* Bitstream creation time */
+ unsigned int magic_length; /* Length of the magic numbers */
+ unsigned char *version; /* Version string */
+};
+
+const char *xrt_xclbin_kind_to_string(enum axlf_section_kind kind);
Only add decl's that are using in multiple files.
This is only defined in xclbin.c, why does it need to be in the header ?
This check can be added to the function call..
or the sanity checking added to the earier call to *get_section_hdr
There a number of small functions that can be combined.
+
+ err = xrt_xclbin_check_section_hdr(mem_header, xclbin_len);
+ if (err)
+ return err;
+
+ *offset = mem_header->m_sectionOffset;
+ *size = mem_header->m_sectionSize;
+
+ return 0;
+}
+
+/* caller should free the allocated memory for **data */
must free
This comment also needs to be with the *.h decl
a general comment
for exported function checking the validity of the inputs in more important.
here you assume **data is valid, really you should check.
+ if (len)
+ *len = size;
len setting being optional, needs to be in the *.h comment
Instead of allocating new memory and making copies of bits of *data
why not have the points reference data ?
The size operations look like translating big endian data to little endian.
This will break on a big endian host.
+/* parse bitstream header */
+int xrt_xclbin_parse_bitstream_header(const unsigned char *data,
+ unsigned int size,
+ struct hw_icap_bit_header *header)
+{
+ unsigned int index;
+ unsigned int len;
+ unsigned int tmp;
+ unsigned int i;
+
+ memset(header, 0, sizeof(*header));
+ /* Start Index at start of bitstream */
+ index = 0;
+
+ /* Initialize HeaderLength. If header returned early inidicates
+ * failure.
This side effect should be documented in the *.h comment.
Also the multi line comment is a bit weird, not sure if it is ok
+ */
+ header->header_length = XHI_BIT_HEADER_FAILURE;
+
+ /* Get "Magic" length */
+ header->magic_length = xhi_data_and_inc(data, &index, size);
+ header->magic_length = (header->magic_length << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Read in "magic" */
+ for (i = 0; i < header->magic_length - 1; i++) {
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (i % 2 == 0 && tmp != XHI_EVEN_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ if (i % 2 == 1 && tmp != XHI_ODD_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+ }
+
+ /* Read null end of magic data. */
+ tmp = xhi_data_and_inc(data, &index, size);
+
+ /* Read 0x01 (short) */
+ tmp = xhi_data_and_inc(data, &index, size);
+ tmp = (tmp << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Check the "0x01" half word */
+ if (tmp != 0x01)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Read 'a' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'a')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Design Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for design name and final null character. */
+ header->design_name = vmalloc(len);
+ if (!header->design_name)
+ return -ENOMEM;
+
+ /* Read in Design Name */
+ for (i = 0; i < len; i++)
+ header->design_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->design_name[len - 1] != '\0')
+ return -1;
+
+ header->version = strstr(header->design_name, "Version=") + strlen("Version=");
+
+ /* Read 'b' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'b')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Part Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for part name and final null character. */
+ header->part_name = vmalloc(len);
+ if (!header->part_name)
+ return -ENOMEM;
+
+ /* Read in part name */
+ for (i = 0; i < len; i++)
+ header->part_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->part_name[len - 1] != '\0')
+ return -1;
+
+ /* Read 'c' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'c')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get date length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for date and final null character. */
+ header->date = vmalloc(len);
+ if (!header->date)
+ return -ENOMEM;
+
+ /* Read in date name */
+ for (i = 0; i < len; i++)
+ header->date[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->date[len - 1] != '\0')
+ return -1;
generally -EINVAL is more meaningful than -1
+
+ /* Read 'd' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'd')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get time length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for time and final null character. */
+ header->time = vmalloc(len);
+ if (!header->time)
+ return -ENOMEM;
+
+ /* Read in time name */
+ for (i = 0; i < len; i++)
+ header->time[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->time[len - 1] != '\0')
+ return -1;
+
+ /* Read 'e' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'e')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get byte length of bitstream */
+ header->bitstream_length = xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
generally a problem
This is confusing, collect the bytes in a temp[] and construct the header->bitstream_length in on statement.
This is a case where xhi_data_and_inc return is not checked and if it failed could blow up later.
+
+ header->header_length = index;
index is not a good variable name if it going to be stored as a length.
consider changing it to something like current_length.
Review these includes, some could be convenience includes.
ex/ linux/version.h with no obvious use of version macros.
struct axlf_header {
+ uint64_t m_length; /* Total size of the xclbin file */
.. snip ..
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
As mentioned in an earlier patch, if uuid_t is larger than 16 bytes, axlf_header breaks.
while it is convenient to have this type here, it would be better this access was handled in another way.
Maybe a host specific function.
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * DOC: Container format for Xilinx FPGA images
+ * The container stores bitstreams, metadata and firmware images.
+ * xclbin/xsabin is ELF-like binary container format. It is structured
is an ELF-like file format. It is a structured
+ * series of sections. There is a file header followed by several section
+ * headers which is followed by sections. A section header points to an
+ * actual section. There is an optional signature at the end. The
+ * following figure illustrates a typical xclbin:
+ *
+ * +---------------------+
+ * | |
+ * | HEADER |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | SIGNATURE |
+ * | (OPTIONAL) |
+ * +---------------------+
This ascii art is a mixture of tabs and spaces, for someone with tab = 2 spaces, this will look messed up.
convert the tabs to spaces
+ */
+
+enum XCLBIN_MODE {
+ XCLBIN_FLAT,
generally
all enums used in a file format should be initialized.
This likely should be
XCLBIN_FLAT = 0,
i did not see the version checked earlier, which one is expected ?
+ uint8_t m_versionMinor; /* Minor Version */
+ uint32_t m_mode; /* XCLBIN_MODE */
+ union {
+ struct {
+ uint64_t m_platformId; /* 64 bit platform ID: */
+ /* vendor-device-subvendor-subdev */
+ uint64_t m_featureId; /* 64 bit feature id */
+ } rom;
+ unsigned char rom_uuid[16]; /* feature ROM UUID for which */
+ /* this xclbin was generated */
+ };
+ unsigned char m_platformVBNV[64]; /* e.g. */
what is VBNV?
+ /* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
+ char m_debug_bin[16]; /* Name of binary with debug */
+ /* information */
+ uint32_t m_numSections; /* Number of section headers */
+};
+
+struct axlf {
+ char m_magic[8]; /* Should be "xclbin2\0" */
+ int32_t m_signature_length; /* Length of the signature. */
+ /* -1 indicates no signature */
+ unsigned char reserved[28]; /* Note: Initialized to 0xFFs */
+
+ unsigned char m_keyBlock[256]; /* Signature for validation */
+ /* of binary */
+ uint64_t m_uniqueId; /* axlf's uniqueId, use it to */
+ /* skip redownload etc */
+ struct axlf_header m_header; /* Inline header */
+ struct axlf_section_header m_sections[1]; /* One or more section */
+ /* headers follow */
+};
+
+/* bitstream information */
+struct xlnx_bitstream {
+ uint8_t m_freq[8];
+ char bits[1];
+};
+
+/**** MEMORY TOPOLOGY SECTION ****/
+struct mem_data {
+ uint8_t m_type; /* enum corresponding to mem_type. */
+ uint8_t m_used; /* if 0 this bank is not present */
+ union {
+ uint64_t m_size; /* if mem_type DDR, then size in KB; */
+ uint64_t route_id; /* if streaming then "route_id" */
+ };
+ union {
+ uint64_t m_base_address;/* if DDR then the base address; */
+ uint64_t flow_id; /* if streaming then "flow id" */
+ };
+ unsigned char m_tag[16]; /* DDR: BANK0,1,2,3, has to be null */
+ /* terminated; if streaming then stream0, 1 etc */
+};
+
+struct mem_topology {
+ int32_t m_count; /* Number of mem_data */
+ struct mem_data m_mem_data[1]; /* Should be sorted on mem_type */
+};
+
+/**** CONNECTIVITY SECTION ****/
+/* Connectivity of each argument of Kernel. It will be in terms of argument
This section does not make sense.
Likely you mean some algorithm kernel, rather than the linux kernel.
+ * index associated. For associating kernel instances with arguments and
+ * banks, start at the connectivity section. Using the m_ip_layout_index
+ * access the ip_data.m_name. Now we can associate this kernel instance
+ * with its original kernel name and get the connectivity as well. This
+ * enables us to form related groups of kernel instances.
+ */
+
+struct connection {
+ int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */
+ /* skipped */
+ int32_t m_ip_layout_index; /* index into the ip_layout section. */
+ /* ip_layout.m_ip_data[index].m_type == IP_KERNEL */
+ int32_t mem_data_index; /* index of the m_mem_data . Flag error is */
+ /* m_used false. */
+};
+
+struct connectivity {
+ int32_t m_count;
+ struct connection m_connection[1];
+};
+
+/**** IP_LAYOUT SECTION ****/
+
+/* IP Kernel */
+#define IP_INT_ENABLE_MASK 0x0001
+#define IP_INTERRUPT_ID_MASK 0x00FE
+#define IP_INTERRUPT_ID_SHIFT 0x1
+
+enum IP_CONTROL {
+ AP_CTRL_HS = 0,
+ AP_CTRL_CHAIN = 1,
+ AP_CTRL_NONE = 2,
+ AP_CTRL_ME = 3,
+ ACCEL_ADAPTER = 4
assigning beyond the first is not necessary unless there are dups or gaps
+};
+
+#define IP_CONTROL_MASK 0xFF00
+#define IP_CONTROL_SHIFT 0x8
+
+/* IPs on AXI lite - their types, names, and base addresses.*/
+struct ip_data {
+ uint32_t m_type; /* map to IP_TYPE enum */
+ union {
+ uint32_t properties; /* Default: 32-bits to indicate ip */
+ /* specific property. */
+ /* m_type: IP_KERNEL
+ * m_int_enable : Bit - 0x0000_0001;
+ * m_interrupt_id : Bits - 0x0000_00FE;
+ * m_ip_control : Bits = 0x0000_FF00;
+ */
+ struct { /* m_type: IP_MEM_* */
+ uint16_t m_index;
+ uint8_t m_pc_index;
+ uint8_t unused;
+ } indices;
+ };
+ uint64_t m_base_address;
+ uint8_t m_name[64]; /* eg Kernel name corresponding to KERNEL */
+ /* instance, can embed CU name in future. */
+};
+
+struct ip_layout {
+ int32_t m_count;
+ struct ip_data m_ip_data[1]; /* All the ip_data needs to be sorted */
+ /* by m_base_address. */
general
doing the bla[1] for c++ ?
Tom
+};
+
+/*** Debug IP section layout ****/
+enum DEBUG_IP_TYPE {
+ UNDEFINED = 0,
+ LAPC,
+ ILA,
+ AXI_MM_MONITOR,
+ AXI_TRACE_FUNNEL,
+ AXI_MONITOR_FIFO_LITE,
+ AXI_MONITOR_FIFO_FULL,
+ ACCEL_MONITOR,
+ AXI_STREAM_MONITOR,
+ AXI_STREAM_PROTOCOL_CHECKER,
+ TRACE_S2MM,
+ AXI_DMA,
+ TRACE_S2MM_FULL
+};
+
+struct debug_ip_data {
+ uint8_t m_type; /* type of enum DEBUG_IP_TYPE */
+ uint8_t m_index_lowbyte;
+ uint8_t m_properties;
+ uint8_t m_major;
+ uint8_t m_minor;
+ uint8_t m_index_highbyte;
+ uint8_t m_reserved[2];
+ uint64_t m_base_address;
+ char m_name[128];
+};
+
+struct debug_ip_layout {
+ uint16_t m_count;
+ struct debug_ip_data m_debug_ip_data[1];
+};
+
+/* Supported clock frequency types */
+enum CLOCK_TYPE {
+ CT_UNUSED = 0, /* Initialized value */
+ CT_DATA = 1, /* Data clock */
+ CT_KERNEL = 2, /* Kernel clock */
+ CT_SYSTEM = 3 /* System Clock */
+};
+
+/* Clock Frequency Entry */
+struct clock_freq {
+ uint16_t m_freq_Mhz; /* Frequency in MHz */
+ uint8_t m_type; /* Clock type (enum CLOCK_TYPE) */
+ uint8_t m_unused[5]; /* Not used - padding */
+ char m_name[128]; /* Clock Name */
+};
+
+/* Clock frequency section */
+struct clock_freq_topology {
+ int16_t m_count; /* Number of entries */
+ struct clock_freq m_clock_freq[1]; /* Clock array */
+};
+
+/* Supported MCS file types */
+enum MCS_TYPE {
+ MCS_UNKNOWN = 0, /* Initialized value */
+ MCS_PRIMARY = 1, /* The primary mcs file data */
+ MCS_SECONDARY = 2, /* The secondary mcs file data */
+};
+
+/* One chunk of MCS data */
+struct mcs_chunk {
+ uint8_t m_type; /* MCS data type */
+ uint8_t m_unused[7]; /* padding */
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size */
+};
+
+/* MCS data section */
+struct mcs {
+ int8_t m_count; /* Number of chunks */
+ int8_t m_unused[7]; /* padding */
+ struct mcs_chunk m_chunk[1]; /* MCS chunks followed by data */
+};
+
+/* bmc data section */
+struct bmc {
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size (bytes) */
+ char m_image_name[64]; /* Name of the image */
+ /* (e.g., MSP432P401R) */
+ char m_device_name[64]; /* Device ID (e.g., VCU1525) */
+ char m_version[64];
+ char m_md5value[33]; /* MD5 Expected Value */
+ /* (e.g., 56027182079c0bd621761b7dab5a27ca)*/
+ char m_padding[7]; /* Padding */
+};
+
+/* soft kernel data section, used by classic driver */
+struct soft_kernel {
+ /** Prefix Syntax:
+ * mpo - member, pointer, offset
+ * This variable represents a zero terminated string
+ * that is offseted from the beginning of the section.
+ * The pointer to access the string is initialized as follows:
+ * char * pCharString = (address_of_section) + (mpo value)
+ */
+ uint32_t mpo_name; /* Name of the soft kernel */
+ uint32_t m_image_offset; /* Image offset */
+ uint32_t m_image_size; /* Image size */
+ uint32_t mpo_version; /* Version */
+ uint32_t mpo_md5_value; /* MD5 checksum */
+ uint32_t mpo_symbol_name; /* Symbol name */
+ uint32_t m_num_instances; /* Number of instances */
+ uint8_t padding[36]; /* Reserved for future use */
+ uint8_t reservedExt[16]; /* Reserved for future extended data */
+};
+
+enum CHECKSUM_TYPE {
+ CST_UNKNOWN = 0,
+ CST_SDBM = 1,
+ CST_LAST
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
#defines should have a prefix, maybe XRT_ or XCLBIN_
quoted
+
+enum axlf_section_kind;
+struct axlf;
+
+/**
+ * Bitstream header information as defined by Xilinx tools.
+ * Please note that this struct definition is not owned by the driver.
+ */
+struct hw_icap_bit_header {
File headers usually have fixed length fields like uint32_t
Is this a structure the real header is converted into ?
quoted
+ unsigned int header_length; /* Length of header in 32 bit words */
+ unsigned int bitstream_length; /* Length of bitstream to read in bytes*/
+ unsigned char *design_name; /* Design name get from bitstream */
+ unsigned char *part_name; /* Part name read from bitstream */
+ unsigned char *date; /* Date read from bitstream header */
+ unsigned char *time; /* Bitstream creation time */
+ unsigned int magic_length; /* Length of the magic numbers */
+ unsigned char *version; /* Version string */
+};
+
+const char *xrt_xclbin_kind_to_string(enum axlf_section_kind kind);
Only add decl's that are using in multiple files.
This is only defined in xclbin.c, why does it need to be in the header ?
The return value of this funtion is not always checked, at the least add a dev_err here
quoted
+
+ data = d[*i];
+ (*i)++;
+
+ return data;
+}
+
+static const struct axlf_section_header *
+xrt_xclbin_get_section_hdr(const struct axlf *xclbin,
+ enum axlf_section_kind kind)
+{
+ int i = 0;
+
+ for (i = 0; i < xclbin->m_header.m_numSections; i++) {
+ if (xclbin->m_sections[i].m_sectionKind == kind)
+ return &xclbin->m_sections[i];
+ }
+
+ return NULL;
+}
+
+static int
+xrt_xclbin_check_section_hdr(const struct axlf_section_header *header,
+ u64 xclbin_len)
+{
+ int ret;
+
+ ret = (header->m_sectionOffset + header->m_sectionSize) > xclbin_len ? -EINVAL : 0;
Tristate is harder to read, consider replacing with if()
int ret = 0
if ()
ret =
Why not just:
if (header->m_section_offset + header->m_section_size)
return -EINVAL;
return 0;
Also please fix the camelCase throughout the entire patchset.
This check can be added to the function call..
or the sanity checking added to the earier call to *get_section_hdr
There a number of small functions that can be combined.
quoted
+
+ err = xrt_xclbin_check_section_hdr(mem_header, xclbin_len);
+ if (err)
+ return err;
+
+ *offset = mem_header->m_sectionOffset;
+ *size = mem_header->m_sectionSize;
+
+ return 0;
+}
+
+/* caller should free the allocated memory for **data */
must free
This comment also needs to be with the *.h decl
a general comment
for exported function checking the validity of the inputs in more important.
here you assume **data is valid, really you should check.
quoted
+ if (len)
+ *len = size;
len setting being optional, needs to be in the *.h comment
Instead of allocating new memory and making copies of bits of *data
why not have the points reference data ?
The size operations look like translating big endian data to little endian.
This will break on a big endian host.
quoted
+/* parse bitstream header */
+int xrt_xclbin_parse_bitstream_header(const unsigned char *data,
+ unsigned int size,
+ struct hw_icap_bit_header *header)
+{
+ unsigned int index;
+ unsigned int len;
+ unsigned int tmp;
+ unsigned int i;
+
+ memset(header, 0, sizeof(*header));
+ /* Start Index at start of bitstream */
+ index = 0;
+
+ /* Initialize HeaderLength. If header returned early inidicates
+ * failure.
This side effect should be documented in the *.h comment.
Also the multi line comment is a bit weird, not sure if it is ok
quoted
+ */
+ header->header_length = XHI_BIT_HEADER_FAILURE;
+
+ /* Get "Magic" length */
+ header->magic_length = xhi_data_and_inc(data, &index, size);
+ header->magic_length = (header->magic_length << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Read in "magic" */
+ for (i = 0; i < header->magic_length - 1; i++) {
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (i % 2 == 0 && tmp != XHI_EVEN_MAGIC_BYTE)
if !(i % 2) ...
quoted
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ if (i % 2 == 1 && tmp != XHI_ODD_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+ }
+
+ /* Read null end of magic data. */
+ tmp = xhi_data_and_inc(data, &index, size);
+
+ /* Read 0x01 (short) */
+ tmp = xhi_data_and_inc(data, &index, size);
+ tmp = (tmp << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Check the "0x01" half word */
+ if (tmp != 0x01)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Read 'a' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'a')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Design Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for design name and final null character. */
+ header->design_name = vmalloc(len);
+ if (!header->design_name)
+ return -ENOMEM;
+
+ /* Read in Design Name */
+ for (i = 0; i < len; i++)
+ header->design_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->design_name[len - 1] != '\0')
+ return -1;
+
+ header->version = strstr(header->design_name, "Version=") + strlen("Version=");
+
+ /* Read 'b' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'b')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Part Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for part name and final null character. */
+ header->part_name = vmalloc(len);
+ if (!header->part_name)
+ return -ENOMEM;
+
+ /* Read in part name */
+ for (i = 0; i < len; i++)
+ header->part_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->part_name[len - 1] != '\0')
+ return -1;
+
+ /* Read 'c' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'c')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get date length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for date and final null character. */
+ header->date = vmalloc(len);
+ if (!header->date)
+ return -ENOMEM;
+
+ /* Read in date name */
+ for (i = 0; i < len; i++)
+ header->date[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->date[len - 1] != '\0')
+ return -1;
generally -EINVAL is more meaningful than -1
quoted
+
+ /* Read 'd' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'd')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get time length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for time and final null character. */
+ header->time = vmalloc(len);
+ if (!header->time)
+ return -ENOMEM;
+
+ /* Read in time name */
+ for (i = 0; i < len; i++)
+ header->time[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->time[len - 1] != '\0')
+ return -1;
+
+ /* Read 'e' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'e')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get byte length of bitstream */
+ header->bitstream_length = xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
generally a problem
This is confusing, collect the bytes in a temp[] and construct the header->bitstream_length in on statement.
This is a case where xhi_data_and_inc return is not checked and if it failed could blow up later.
quoted
+
+ header->header_length = index;
index is not a good variable name if it going to be stored as a length.
consider changing it to something like current_length.
Review these includes, some could be convenience includes.
ex/ linux/version.h with no obvious use of version macros.
struct axlf_header {
+ uint64_t m_length; /* Total size of the xclbin file */
.. snip ..
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
As mentioned in an earlier patch, if uuid_t is larger than 16 bytes, axlf_header breaks.
while it is convenient to have this type here, it would be better this access was handled in another way.
Maybe a host specific function.
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
quoted
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * DOC: Container format for Xilinx FPGA images
+ * The container stores bitstreams, metadata and firmware images.
+ * xclbin/xsabin is ELF-like binary container format. It is structured
is an ELF-like file format. It is a structured
quoted
+ * series of sections. There is a file header followed by several section
+ * headers which is followed by sections. A section header points to an
+ * actual section. There is an optional signature at the end. The
+ * following figure illustrates a typical xclbin:
+ *
+ * +---------------------+
+ * | |
+ * | HEADER |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | SIGNATURE |
+ * | (OPTIONAL) |
+ * +---------------------+
This ascii art is a mixture of tabs and spaces, for someone with tab = 2 spaces, this will look messed up.
convert the tabs to spaces
quoted
+ */
+
+enum XCLBIN_MODE {
+ XCLBIN_FLAT,
generally
all enums used in a file format should be initialized.
This likely should be
XCLBIN_FLAT = 0,
i did not see the version checked earlier, which one is expected ?
quoted
+ uint8_t m_versionMinor; /* Minor Version */
+ uint32_t m_mode; /* XCLBIN_MODE */
+ union {
+ struct {
+ uint64_t m_platformId; /* 64 bit platform ID: */
+ /* vendor-device-subvendor-subdev */
+ uint64_t m_featureId; /* 64 bit feature id */
+ } rom;
+ unsigned char rom_uuid[16]; /* feature ROM UUID for which */
+ /* this xclbin was generated */
+ };
+ unsigned char m_platformVBNV[64]; /* e.g. */
what is VBNV?
quoted
+ /* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
+ char m_debug_bin[16]; /* Name of binary with debug */
+ /* information */
+ uint32_t m_numSections; /* Number of section headers */
+};
+
+struct axlf {
+ char m_magic[8]; /* Should be "xclbin2\0" */
+ int32_t m_signature_length; /* Length of the signature. */
+ /* -1 indicates no signature */
+ unsigned char reserved[28]; /* Note: Initialized to 0xFFs */
+
+ unsigned char m_keyBlock[256]; /* Signature for validation */
+ /* of binary */
+ uint64_t m_uniqueId; /* axlf's uniqueId, use it to */
+ /* skip redownload etc */
+ struct axlf_header m_header; /* Inline header */
+ struct axlf_section_header m_sections[1]; /* One or more section */
+ /* headers follow */
+};
+
+/* bitstream information */
+struct xlnx_bitstream {
+ uint8_t m_freq[8];
+ char bits[1];
+};
+
+/**** MEMORY TOPOLOGY SECTION ****/
+struct mem_data {
+ uint8_t m_type; /* enum corresponding to mem_type. */
+ uint8_t m_used; /* if 0 this bank is not present */
+ union {
+ uint64_t m_size; /* if mem_type DDR, then size in KB; */
+ uint64_t route_id; /* if streaming then "route_id" */
+ };
+ union {
+ uint64_t m_base_address;/* if DDR then the base address; */
+ uint64_t flow_id; /* if streaming then "flow id" */
+ };
+ unsigned char m_tag[16]; /* DDR: BANK0,1,2,3, has to be null */
+ /* terminated; if streaming then stream0, 1 etc */
+};
+
+struct mem_topology {
+ int32_t m_count; /* Number of mem_data */
+ struct mem_data m_mem_data[1]; /* Should be sorted on mem_type */
+};
+
+/**** CONNECTIVITY SECTION ****/
+/* Connectivity of each argument of Kernel. It will be in terms of argument
This section does not make sense.
Likely you mean some algorithm kernel, rather than the linux kernel.
quoted
+ * index associated. For associating kernel instances with arguments and
+ * banks, start at the connectivity section. Using the m_ip_layout_index
+ * access the ip_data.m_name. Now we can associate this kernel instance
+ * with its original kernel name and get the connectivity as well. This
+ * enables us to form related groups of kernel instances.
+ */
+
+struct connection {
+ int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */
+ /* skipped */
+ int32_t m_ip_layout_index; /* index into the ip_layout section. */
+ /* ip_layout.m_ip_data[index].m_type == IP_KERNEL */
+ int32_t mem_data_index; /* index of the m_mem_data . Flag error is */
+ /* m_used false. */
+};
+
+struct connectivity {
+ int32_t m_count;
+ struct connection m_connection[1];
+};
+
+/**** IP_LAYOUT SECTION ****/
+
+/* IP Kernel */
+#define IP_INT_ENABLE_MASK 0x0001
+#define IP_INTERRUPT_ID_MASK 0x00FE
+#define IP_INTERRUPT_ID_SHIFT 0x1
+
+enum IP_CONTROL {
+ AP_CTRL_HS = 0,
+ AP_CTRL_CHAIN = 1,
+ AP_CTRL_NONE = 2,
+ AP_CTRL_ME = 3,
+ ACCEL_ADAPTER = 4
assigning beyond the first is not necessary unless there are dups or gaps
quoted
+};
+
+#define IP_CONTROL_MASK 0xFF00
+#define IP_CONTROL_SHIFT 0x8
+
+/* IPs on AXI lite - their types, names, and base addresses.*/
+struct ip_data {
+ uint32_t m_type; /* map to IP_TYPE enum */
+ union {
+ uint32_t properties; /* Default: 32-bits to indicate ip */
+ /* specific property. */
+ /* m_type: IP_KERNEL
+ * m_int_enable : Bit - 0x0000_0001;
+ * m_interrupt_id : Bits - 0x0000_00FE;
+ * m_ip_control : Bits = 0x0000_FF00;
+ */
+ struct { /* m_type: IP_MEM_* */
+ uint16_t m_index;
+ uint8_t m_pc_index;
+ uint8_t unused;
+ } indices;
+ };
+ uint64_t m_base_address;
+ uint8_t m_name[64]; /* eg Kernel name corresponding to KERNEL */
+ /* instance, can embed CU name in future. */
+};
+
+struct ip_layout {
+ int32_t m_count;
+ struct ip_data m_ip_data[1]; /* All the ip_data needs to be sorted */
+ /* by m_base_address. */
general
doing the bla[1] for c++ ?
Tom
quoted
+};
+
+/*** Debug IP section layout ****/
+enum DEBUG_IP_TYPE {
+ UNDEFINED = 0,
+ LAPC,
+ ILA,
+ AXI_MM_MONITOR,
+ AXI_TRACE_FUNNEL,
+ AXI_MONITOR_FIFO_LITE,
+ AXI_MONITOR_FIFO_FULL,
+ ACCEL_MONITOR,
+ AXI_STREAM_MONITOR,
+ AXI_STREAM_PROTOCOL_CHECKER,
+ TRACE_S2MM,
+ AXI_DMA,
+ TRACE_S2MM_FULL
+};
+
+struct debug_ip_data {
+ uint8_t m_type; /* type of enum DEBUG_IP_TYPE */
+ uint8_t m_index_lowbyte;
+ uint8_t m_properties;
+ uint8_t m_major;
+ uint8_t m_minor;
+ uint8_t m_index_highbyte;
+ uint8_t m_reserved[2];
+ uint64_t m_base_address;
+ char m_name[128];
+};
+
+struct debug_ip_layout {
+ uint16_t m_count;
+ struct debug_ip_data m_debug_ip_data[1];
+};
+
+/* Supported clock frequency types */
+enum CLOCK_TYPE {
+ CT_UNUSED = 0, /* Initialized value */
+ CT_DATA = 1, /* Data clock */
+ CT_KERNEL = 2, /* Kernel clock */
+ CT_SYSTEM = 3 /* System Clock */
+};
+
+/* Clock Frequency Entry */
+struct clock_freq {
+ uint16_t m_freq_Mhz; /* Frequency in MHz */
+ uint8_t m_type; /* Clock type (enum CLOCK_TYPE) */
+ uint8_t m_unused[5]; /* Not used - padding */
+ char m_name[128]; /* Clock Name */
+};
+
+/* Clock frequency section */
+struct clock_freq_topology {
+ int16_t m_count; /* Number of entries */
+ struct clock_freq m_clock_freq[1]; /* Clock array */
+};
+
+/* Supported MCS file types */
+enum MCS_TYPE {
+ MCS_UNKNOWN = 0, /* Initialized value */
+ MCS_PRIMARY = 1, /* The primary mcs file data */
+ MCS_SECONDARY = 2, /* The secondary mcs file data */
+};
+
+/* One chunk of MCS data */
+struct mcs_chunk {
+ uint8_t m_type; /* MCS data type */
just call them type, unused, offset. Drop the m_*
quoted
+ uint8_t m_unused[7]; /* padding */
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size */
+};
+
+/* MCS data section */
+struct mcs {
+ int8_t m_count; /* Number of chunks */
+ int8_t m_unused[7]; /* padding */
+ struct mcs_chunk m_chunk[1]; /* MCS chunks followed by data */
+};
+
+/* bmc data section */
+struct bmc {
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size (bytes) */
+ char m_image_name[64]; /* Name of the image */
+ /* (e.g., MSP432P401R) */
+ char m_device_name[64]; /* Device ID (e.g., VCU1525) */
+ char m_version[64];
+ char m_md5value[33]; /* MD5 Expected Value */
+ /* (e.g., 56027182079c0bd621761b7dab5a27ca)*/
+ char m_padding[7]; /* Padding */
+};
+
+/* soft kernel data section, used by classic driver */
+struct soft_kernel {
+ /** Prefix Syntax:
+ * mpo - member, pointer, offset
+ * This variable represents a zero terminated string
+ * that is offseted from the beginning of the section.
+ * The pointer to access the string is initialized as follows:
+ * char * pCharString = (address_of_section) + (mpo value)
+ */
+ uint32_t mpo_name; /* Name of the soft kernel */
+ uint32_t m_image_offset; /* Image offset */
+ uint32_t m_image_size; /* Image size */
+ uint32_t mpo_version; /* Version */
+ uint32_t mpo_md5_value; /* MD5 checksum */
+ uint32_t mpo_symbol_name; /* Symbol name */
+ uint32_t m_num_instances; /* Number of instances */
+ uint8_t padding[36]; /* Reserved for future use */
+ uint8_t reservedExt[16]; /* Reserved for future extended data */
+};
+
+enum CHECKSUM_TYPE {
+ CST_UNKNOWN = 0,
+ CST_SDBM = 1,
+ CST_LAST
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
I'll take a closer look, these were just random things I bumped into.
- Moritz
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-21 18:41:17
On Sun, Feb 21, 2021 at 06:57:31AM -0800, Tom Rix wrote:
As I am looking through the files, I have this comment.
fpga/ is currently a single directory, while files could be organized in subdirectories like
dfl/pci.c
instead have the possible subdir name as a prefix to the filename.
dfl-pci.c
For consistency,
xrt/metadata/metadata.c
should be
xrt-metadata.c
Agreed. Keep the prefix.
Likewise the build infra needs to integrated within the existing files fpga/Kconfig,Makefile
This is a bigish refactor, so let's get a second opinion.
In what sense? You mean adding a subdirectory? Maybe something like this
drivers/fpga
- dfl/
- xilinx/
- intel/
- lattice/
- xrt/
...
would generally make sense.
We didn't have enough drivers to prioritize that yet, but we can look
into it.
Moritz ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Update fpga Kconfig/Makefile and add Kconfig/Makefile for new drivers.
Expand the comment, there are several new configs that could use an explanation
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-21 20:22:03
Lizhi,
On Wed, Feb 17, 2021 at 10:40:17PM -0800, Lizhi Hou wrote:
quoted hunk
Add DDR calibration driver. DDR calibration is a hardware function
discovered by walking firmware metadata. A platform device node will
be created for it. Hardware provides DDR calibration status through
this function.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/calib.h | 30 ++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 +++++++++++++++++++++++++
2 files changed, 256 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
@@ -0,0 +1,226 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAmemorycalibrationdriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*memorycalibration+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*/+#include<linux/delay.h>+#include"xclbin-helper.h"+#include"metadata.h"+#include"xleaf/calib.h"++#define XRT_CALIB "xrt_calib"++structcalib_cache{+structlist_headlink;+constchar*ep_name;+char*data;+u32data_size;+};++structcalib{+structplatform_device*pdev;+void*calib_base;+structmutexlock;/* calibration dev lock */+structlist_headcache_list;+u32cache_num;+enumxrt_calib_resultsresult;+};++#define CALIB_DONE(calib) \+(ioread32((calib)->calib_base)&BIT(0))++staticvoidcalib_cache_clean_nolock(structcalib*calib)+{+structcalib_cache*cache,*temp;++list_for_each_entry_safe(cache,temp,&calib->cache_list,link){+vfree(cache->data);+list_del(&cache->link);+vfree(cache);+}+calib->cache_num=0;+}++staticvoidcalib_cache_clean(structcalib*calib)+{+mutex_lock(&calib->lock);+calib_cache_clean_nolock(calib);+mutex_unlock(&calib->lock);+}++staticintcalib_srsr(structcalib*calib,structplatform_device*srsr_leaf)+{+return-EOPNOTSUPP;+}++staticintcalib_calibration(structcalib*calib)+{+inti;++for(i=0;i<20;i++){+if(CALIB_DONE(calib))+break;+msleep(500);+}++if(i==20){+xrt_err(calib->pdev,+"MIG calibration timeout after bitstream download");+return-ETIMEDOUT;+}++xrt_info(calib->pdev,"took %dms",i*500);+return0;+}++staticvoidxrt_calib_event_cb(structplatform_device*pdev,void*arg)+{+structcalib*calib=platform_get_drvdata(pdev);+structxrt_event*evt=(structxrt_event*)arg;+enumxrt_eventse=evt->xe_evt;+enumxrt_subdev_idid=evt->xe_subdev.xevt_subdev_id;+intinstance=evt->xe_subdev.xevt_subdev_instance;+structplatform_device*leaf;+intret;++switch(e){+caseXRT_EVENT_POST_CREATION:{+if(id==XRT_SUBDEV_SRSR){+leaf=xleaf_get_leaf_by_id(pdev,+XRT_SUBDEV_SRSR,+instance);+if(!leaf){+xrt_err(pdev,"does not get SRSR subdev");+return;+}+ret=calib_srsr(calib,leaf);+xleaf_put_leaf(pdev,leaf);+calib->result=+ret?XRT_CALIB_FAILED:XRT_CALIB_SUCCEEDED;+}elseif(id==XRT_SUBDEV_UCS){+ret=calib_calibration(calib);+calib->result=+ret?XRT_CALIB_FAILED:XRT_CALIB_SUCCEEDED;+}+break;+}+default:+break;+}+}++staticintxrt_calib_remove(structplatform_device*pdev)+{+structcalib*calib=platform_get_drvdata(pdev);++calib_cache_clean(calib);++if(calib->calib_base)+iounmap(calib->calib_base);++platform_set_drvdata(pdev,NULL);+devm_kfree(&pdev->dev,calib);++return0;+}++staticintxrt_calib_probe(structplatform_device*pdev)+{+structcalib*calib;+structresource*res;+interr=0;++calib=devm_kzalloc(&pdev->dev,sizeof(*calib),GFP_KERNEL);+if(!calib)+return-ENOMEM;++calib->pdev=pdev;+platform_set_drvdata(pdev,calib);++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+if(!res)+gotofailed;++calib->calib_base=ioremap(res->start,res->end-res->start+1);+if(!calib->calib_base){+err=-EIO;+xrt_err(pdev,"Map iomem failed");+gotofailed;+}++mutex_init(&calib->lock);+INIT_LIST_HEAD(&calib->cache_list);++return0;++failed:+xrt_calib_remove(pdev);+returnerr;+}
This is extremly weird imho, you have a platform driver that essentially
does not register with a subsystem or anything else.
Do you intend to use this from the outside through
platform_get_drvdata(), or how does this tie together with the rest?
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-21 20:25:38
On Wed, Feb 17, 2021 at 10:40:13PM -0800, Lizhi Hou wrote:
quoted hunk
Add ICAP driver. ICAP is a hardware function discovered by walking
firmware metadata. A platform device node will be created for it.
FPGA bitstream is written to hardware through ICAP.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/icap.h | 29 +++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 ++++++++++++++++++++++++++
2 files changed, 346 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-21 20:37:02
On Wed, Feb 17, 2021 at 10:40:18PM -0800, Lizhi Hou wrote:
quoted hunk
Add partition isolation platform driver. partition isolation is
a hardware function discovered by walking firmware metadata.
A platform device node will be created for it. Partition isolation
function isolate the different fpga regions
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/axigate.h | 25 ++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 +++++++++++++++++++++++
2 files changed, 323 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
+
+ switch (e) {
+ case XRT_EVENT_POST_CREATION:
+ break;
+ default:
+ return;
+ }
+
+ if (id != XRT_SUBDEV_AXIGATE)
+ return;
+
+ leaf = xleaf_get_leaf_by_id(pdev, id, instance);
+ if (!leaf)
+ return;
+
+ res = platform_get_resource(leaf, IORESOURCE_MEM, 0);
+ if (!res || !strncmp(res->name, gate->ep_name, strlen(res->name) + 1)) {
+ (void)xleaf_put_leaf(pdev, leaf);
+ return;
+ }
+
+ /*
+ * higher level axigate instance created,
+ * make sure the gate is openned. This covers 1RP flow which
+ * has plp gate as well.
+ */
+ if (xrt_axigate_epname_idx(leaf) > xrt_axigate_epname_idx(pdev))
+ xrt_axigate_free(pdev);
+ else
+ xleaf_ioctl(leaf, XRT_AXIGATE_FREE, NULL);
+
+ (void)xleaf_put_leaf(pdev, leaf);
+}
+
+static int
+xrt_axigate_leaf_ioctl(struct platform_device *pdev, u32 cmd, void *arg)
+{
+ switch (cmd) {
+ case XRT_XLEAF_EVENT:
+ xrt_axigate_event_cb(pdev, arg);
+ break;
+ case XRT_AXIGATE_FREEZE:
+ xrt_axigate_freeze(pdev);
+ break;
+ case XRT_AXIGATE_FREE:
+ xrt_axigate_free(pdev);
+ break;
+ default:
+ xrt_err(pdev, "unsupported cmd %d", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int xrt_axigate_remove(struct platform_device *pdev)
+{
+ struct xrt_axigate *gate;
+
+ gate = platform_get_drvdata(pdev);
+
+ if (gate->base)
+ iounmap(gate->base);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, gate);
No! The point of using devres is so cleanup happens on removal.
While you're at it, if you move the ioremap to a devres version, this
function can basically go away entirely.
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-21 20:44:12
Lizhi,
On Wed, Feb 17, 2021 at 10:40:01PM -0800, Lizhi Hou wrote:
Hello,
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Alveo PCIe FPGA based platforms have a static *shell* partition and a partial
re-configurable *user* partition. The shell partition is automatically loaded from
flash when host is booted and PCIe is enumerated by BIOS. Shell cannot be changed
till the next cold reboot. The shell exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo platform,
XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA image using
Vitis https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
tools. The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap the image
running on the user partition in order to switch between different workloads by
loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xmgmt* binds to management physical function of Alveo
platform. The modular driver framework is organized into several platform drivers
which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst. Unlike previous V1 and V2 version
of patch series, V3 version does not require export of libfdt symbols.
TESTING AND VALIDATION
xmgmt driver can be tested with full XRT open source stack which includes user
space libraries, board utilities and (out of tree) first generation user physical
function driver xocl. XRT open source runtime stack is available at
https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on Alveo/XRT
security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.htmlhttps://xilinx.github.io/XRT/master/html/security.htmlhttps://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V1 version of patch series can be found here:
https://lore.kernel.org/lkml/20201217075046.28553-1-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-2-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-3-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-4-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-5-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-6-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-7-sonals@xilinx.com/
Lizhi Hou (18):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib platform driver manager
fpga: xrt: group platform driver
fpga: xrt: platform driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main platform driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: VSEC platform driver
fpga: xrt: UCS platform driver
fpga: xrt: ICAP platform driver
fpga: xrt: devctl platform driver
fpga: xrt: clock platform driver
fpga: xrt: clock frequence counter platform driver
fpga: xrt: DDR calibration platform driver
fpga: xrt: partition isolation platform driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842 ++++++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 4 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 48 ++
drivers/fpga/xrt/include/group.h | 27 +
drivers/fpga/xrt/include/metadata.h | 229 ++++++
drivers/fpga/xrt/include/subdev_id.h | 43 ++
drivers/fpga/xrt/include/xclbin-helper.h | 52 ++
drivers/fpga/xrt/include/xleaf.h | 276 +++++++
drivers/fpga/xrt/include/xleaf/axigate.h | 25 +
drivers/fpga/xrt/include/xleaf/calib.h | 30 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +
drivers/fpga/xrt/include/xleaf/clock.h | 31 +
drivers/fpga/xrt/include/xleaf/devctl.h | 43 ++
drivers/fpga/xrt/include/xleaf/icap.h | 29 +
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +
drivers/fpga/xrt/include/xmgmt-main.h | 37 +
drivers/fpga/xrt/include/xroot.h | 114 +++
drivers/fpga/xrt/lib/Kconfig | 16 +
drivers/fpga/xrt/lib/Makefile | 30 +
drivers/fpga/xrt/lib/cdev.c | 231 ++++++
drivers/fpga/xrt/lib/group.c | 265 +++++++
drivers/fpga/xrt/lib/main.c | 274 +++++++
drivers/fpga/xrt/lib/main.h | 17 +
drivers/fpga/xrt/lib/subdev.c | 871 +++++++++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 ++++++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 ++++++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 ++++++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 +++++++++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 ++++++
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++
drivers/fpga/xrt/lib/xroot.c | 598 ++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 524 ++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/fmgr-drv.c | 187 +++++
drivers/fpga/xrt/mgmt/fmgr.h | 28 +
drivers/fpga/xrt/mgmt/main-impl.h | 37 +
drivers/fpga/xrt/mgmt/main-region.c | 471 ++++++++++++
drivers/fpga/xrt/mgmt/main.c | 693 ++++++++++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++
include/uapi/linux/xrt/xmgmt-ioctl.h | 46 ++
53 files changed, 9957 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/main.c
create mode 100644 drivers/fpga/xrt/lib/main.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
create mode 100644 drivers/fpga/xrt/mgmt/main-impl.h
create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/main.c
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h
--
2.18.4
Please fix the indents all across this patchset. Doesn't checkpatch with
--strict complain about this?
Also more generally this looks like it should be a bus. Look at DFL for
reference.
- Moritz
+ * struct platform_driver, which contains it's binding name and driver/file ops.
+ * We also map it to the endpoint name in DTB as well, if it's different
+ * than the driver's binding name.
+ */
+struct xrt_drv_map {
+ struct list_head list;
+ enum xrt_subdev_id id;
+ struct platform_driver *drv;
+ struct xrt_subdev_endpoints *eps;
+ struct ida ida; /* manage driver instance and char dev minor */
+};
+
+static DEFINE_MUTEX(xrt_lib_lock); /* global lock protecting xrt_drv_maps list */
+static LIST_HEAD(xrt_drv_maps);
+struct class *xrt_class;
+
+static inline struct xrt_subdev_drvdata *
+xrt_drv_map2drvdata(struct xrt_drv_map *map)
+{
+ return (struct xrt_subdev_drvdata *)map->drv->id_table[0].driver_data;
+}
+
+static struct xrt_drv_map *
+xrt_drv_find_map_by_id_nolock(enum xrt_subdev_id id)
xrt_drv_register_driver failure is unhandled.
This is the only time xrt_drv_register_driver is called, consider expanding the function here and removing the call.
These constructor/destructor calls needs to be more dynamic.
calls are made even if there are no subdevices to go with the id's.
Also this list can not grow. How would a new id be added by a module ?
quoted hunk
+static void (*leaf_init_fini_cbs[])(bool) = {
+ group_leaf_init_fini,
+ vsec_leaf_init_fini,
+ devctl_leaf_init_fini,
+ axigate_leaf_init_fini,
+ icap_leaf_init_fini,
+ calib_leaf_init_fini,
+ clkfreq_leaf_init_fini,
+ clock_leaf_init_fini,
+ ucs_leaf_init_fini,
+};
+
+static __init int xrt_lib_init(void)
+{
+ int i;
+
+ xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
+ if (IS_ERR(xrt_class))
+ return PTR_ERR(xrt_class);
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](true);
+ return 0;
+}
+
+static __exit void xrt_lib_fini(void)
+{
+ struct xrt_drv_map *map;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](false);
+
+ mutex_lock(&xrt_lib_lock);
+
+ while (!list_empty(&xrt_drv_maps)) {
+ map = list_first_entry_or_null(&xrt_drv_maps, struct xrt_drv_map, list);
+ pr_err("Unloading module with %s still registered\n", XRT_DRVNAME(map->drv));
+ list_del(&map->list);
+ mutex_unlock(&xrt_lib_lock);
+ xrt_drv_unregister_driver(map);
+ vfree(map);
+ mutex_lock(&xrt_lib_lock);
+ }
+
+ mutex_unlock(&xrt_lib_lock);
+
+ class_destroy(xrt_class);
+}
+
+module_init(xrt_lib_init);
+module_exit(xrt_lib_fini);
+
+MODULE_VERSION(XRT_IPLIB_MODULE_VERSION);
+MODULE_AUTHOR("XRT Team [off-list ref]");
+MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
+MODULE_LICENSE("GPL v2");
To be self contained, the header defining enum xrt_subdev_id should be included.
This is subdev_id.h which comes in with patch 6
A dependency on a future patch breaks bisectablity.
It may make sense to collect these small headers into a single large header for the ip infra lib and bring them all in this patch.
Tom
Are these really ioctl calls?
Seems more like messages between nodes in a tree.
Consider changing to better jagon, maybe ioctl -> msg
+ */
+enum xrt_group_ioctl_cmd {
+ XRT_GROUP_GET_LEAF = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
XRT_LEAF_CUSTOM_BASE is a #define, while these are enums. To be consistent, the XRT_LEAF_CUSTOM_BASE should be an enum in xleaf, you can initialize it to 64 there.
+ * struct platform_driver, which contains it's binding name and driver/file ops.
+ * We also map it to the endpoint name in DTB as well, if it's different
+ * than the driver's binding name.
+ */
+struct xrt_drv_map {
+ struct list_head list;
+ enum xrt_subdev_id id;
+ struct platform_driver *drv;
+ struct xrt_subdev_endpoints *eps;
+ struct ida ida; /* manage driver instance and char dev minor */
+};
+
+static DEFINE_MUTEX(xrt_lib_lock); /* global lock protecting xrt_drv_maps list */
+static LIST_HEAD(xrt_drv_maps);
+struct class *xrt_class;
+
+static inline struct xrt_subdev_drvdata *
+xrt_drv_map2drvdata(struct xrt_drv_map *map)
+{
+ return (struct xrt_subdev_drvdata *)map->drv->id_table[0].driver_data;
+}
+
+static struct xrt_drv_map *
+xrt_drv_find_map_by_id_nolock(enum xrt_subdev_id id)
xrt_drv_register_driver failure is unhandled.
This is the only time xrt_drv_register_driver is called, consider expanding the function here and removing the call.
These constructor/destructor calls needs to be more dynamic.
calls are made even if there are no subdevices to go with the id's.
Also this list can not grow. How would a new id be added by a module ?
quoted
+static void (*leaf_init_fini_cbs[])(bool) = {
+ group_leaf_init_fini,
+ vsec_leaf_init_fini,
+ devctl_leaf_init_fini,
+ axigate_leaf_init_fini,
+ icap_leaf_init_fini,
+ calib_leaf_init_fini,
+ clkfreq_leaf_init_fini,
+ clock_leaf_init_fini,
+ ucs_leaf_init_fini,
+};
+
+static __init int xrt_lib_init(void)
+{
+ int i;
+
+ xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
+ if (IS_ERR(xrt_class))
+ return PTR_ERR(xrt_class);
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](true);
+ return 0;
+}
+
+static __exit void xrt_lib_fini(void)
+{
+ struct xrt_drv_map *map;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](false);
+
+ mutex_lock(&xrt_lib_lock);
+
+ while (!list_empty(&xrt_drv_maps)) {
+ map = list_first_entry_or_null(&xrt_drv_maps, struct xrt_drv_map, list);
+ pr_err("Unloading module with %s still registered\n", XRT_DRVNAME(map->drv));
+ list_del(&map->list);
+ mutex_unlock(&xrt_lib_lock);
+ xrt_drv_unregister_driver(map);
+ vfree(map);
+ mutex_lock(&xrt_lib_lock);
+ }
+
+ mutex_unlock(&xrt_lib_lock);
+
+ class_destroy(xrt_class);
+}
+
+module_init(xrt_lib_init);
+module_exit(xrt_lib_fini);
+
+MODULE_VERSION(XRT_IPLIB_MODULE_VERSION);
+MODULE_AUTHOR("XRT Team [off-list ref]");
+MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
+MODULE_LICENSE("GPL v2");
To be self contained, the header defining enum xrt_subdev_id should be included.
This is subdev_id.h which comes in with patch 6
A dependency on a future patch breaks bisectablity.
It may make sense to collect these small headers into a single large header for the ip infra lib and bring them all in this patch.
Please add the headers when you use them, do *not* do header only commits.
It's perfectly fine (and desirable) to add things over time to a header
as you use them.
Note *each* commit must compile and work standing on its own, so yes as
Tom pointed out, do not depend on future (later commit) files.
These #defines could be in alphabetical order
Some #define with embedded acronyms could be expanded
ex/ XRT_MD_NODE_CMC_REG , what is CMC ?
Will reorder. Expanding might make macro name too long. I will add
comment as below:
/*
* IP nodes
* AF: AXI Firewall
* CMC: Card Management Controller
* ERT: Embedded Runtime
* PLP: Provider Reconfigurable Partition
* ULP: User Reconfigurable Partition
*/
This structure is just a direct translation of firmware node. It is
simple description of hardware endpoint.
quoted
+
+/* Note: res_id is defined by leaf driver and must start with 0. */
+struct xrt_iores_map {
+ char *res_name;
+ int res_id;
+};
+
+static inline int xrt_md_res_name2id(const struct xrt_iores_map *res_map,
+ int entry_num, const char *res_name)
+{
+ int i;
+
+ for (i = 0; i < entry_num; i++) {
+ if (!strcmp(res_name, res_map->res_name))
Use the 'n' variant strncmp for better safety.
Fix generally.
A wrapping a single call seems like an unnecessary abstraction layer.
can this be reduced/removed ?
Will remove.
quoted
+}
+
+/*
+ * Firmware provides 128 bit hash string as unque id of partition/interface.
The firmware provides a 128 bit hash string as a unique id to the partition/interface.
Existing hw does not yet use the cononical form, so it is necessary to use a translation function.
Will fix
quoted
+ * This string will be canonical textual representation in the future.
+ * Before that, introducing these two functions below to translate
+ * hash string to uuid_t for released hardware.
Is there an existing version string the new hw will use to check which way to go ?
We do not have the new hw yet.
quoted
+ */
+static inline void xrt_md_trans_uuid2str(const uuid_t *uuid, char *uuidstr)
+{
+ int i, p;
+ u8 *u = (u8 *)uuid;
+
+ for (p = 0, i = sizeof(uuid_t) - 1; i >= 0; p++, i--)
This loop needs to be improved.
Consider if sizeof(uuid_t) changed, accessing u[] would overflow.
Will fix all the issues of uuid2str()/str2uuid() you pointed out. The
rough idea is to use export_uuid() and import_uuid().
+ * There can be unlimited number of instances of a subdev driver. A
unlimited? change to 'multiple'
+ * <subdev_id, subdev_instance> tuple should be a unique identification of
tuple is a unique
+ * a specific instance of a subdev driver.
+ * NOTE: PLEASE do not change the order of IDs. Sub devices in the same
+ * group are initialized by this order.
why does the order matter? the enums are all initialized
+#include <linux/libfdt_env.h>
+#include "libfdt.h"
+#include "subdev_id.h"
+#include "xroot.h"
+#include "events.h"
+
+/* All subdev drivers should use below common routines to print out msg. */
+#define DEV(pdev) (&(pdev)->dev)
+#define DEV_PDATA(pdev) \
+ ((struct xrt_subdev_platdata *)dev_get_platdata(DEV(pdev)))
+#define DEV_DRVDATA(pdev) \
+ ((struct xrt_subdev_drvdata *) \
+ platform_get_device_id(pdev)->driver_data)
+#define FMT_PRT(prt_fn, pdev, fmt, args...) \
+ ({typeof(pdev) (_pdev) = (pdev); \
+ prt_fn(DEV(_pdev), "%s %s: " fmt, \
+ DEV_PDATA(_pdev)->xsp_root_name, __func__, ##args); })
+#define xrt_err(pdev, fmt, args...) FMT_PRT(dev_err, pdev, fmt, ##args)
+#define xrt_warn(pdev, fmt, args...) FMT_PRT(dev_warn, pdev, fmt, ##args)
+#define xrt_info(pdev, fmt, args...) FMT_PRT(dev_info, pdev, fmt, ##args)
+#define xrt_dbg(pdev, fmt, args...) FMT_PRT(dev_dbg, pdev, fmt, ##args)
+
+/* Starting IOCTL for common IOCTLs implemented by all leaves. */
+#define XRT_XLEAF_COMMON_BASE 0
+/* Starting IOCTL for leaves' specific IOCTLs. */
+#define XRT_XLEAF_CUSTOM_BASE 64
+enum xrt_xleaf_common_ioctl_cmd {
+ XRT_XLEAF_EVENT = XRT_XLEAF_COMMON_BASE,
+};
+
+/*
+ * If populated by subdev driver, infra will handle the mechanics of
+ * char device (un)registration.
+ */
+enum xrt_subdev_file_mode {
+ /* Infra create cdev, default file name */
+ XRT_SUBDEV_FILE_DEFAULT = 0,
+ /* Infra create cdev, need to encode inst num in file name */
+ XRT_SUBDEV_FILE_MULTI_INST,
+ /* No auto creation of cdev by infra, leaf handles it by itself */
+ XRT_SUBDEV_FILE_NO_AUTO,
+};
+
+struct xrt_subdev_file_ops {
+ const struct file_operations xsf_ops;
+ dev_t xsf_dev_t;
+ const char *xsf_dev_name;
+ enum xrt_subdev_file_mode xsf_mode;
+};
+
+/*
+ * Subdev driver callbacks populated by subdev driver.
+ */
+struct xrt_subdev_drv_ops {
+ /*
+ * Per driver instance callback. The pdev points to the instance.
+ * If defined these are called by other leaf drivers.
If defined,
+ * Note that root driver may call into xsd_ioctl of a group driver.
+ */
+ int (*xsd_ioctl)(struct platform_device *pdev, u32 cmd, void *arg);
+};
+
+/*
+ * Defined and populated by subdev driver, exported as driver_data in
+ * struct platform_device_id.
+ */
+struct xrt_subdev_drvdata {
+ struct xrt_subdev_file_ops xsd_file_ops;
+ struct xrt_subdev_drv_ops xsd_dev_ops;
+};
+
+/*
+ * Partially initialized by the parent driver, then, passed in as subdev driver's
+ * platform data when creating subdev driver instance by calling platform
+ * device register API (platform_device_register_data() or the likes).
+ *
+ * Once device register API returns, platform driver framework makes a copy of
+ * this buffer and maintains its life cycle. The content of the buffer is
+ * completely owned by subdev driver.
+ *
+ * Thus, parent driver should be very careful when it touches this buffer
+ * again once it's handed over to subdev driver. And the data structure
+ * should not contain pointers pointing to buffers that is managed by
+ * other or parent drivers since it could have been freed before platform
+ * data buffer is freed by platform driver framework.
This sounds complicated and risky, why have two copies ?
+ */
+struct xrt_subdev_platdata {
+ /*
+ * Per driver instance callback. The pdev points to the instance.
+ * Should always be defined for subdev driver to get service from root.
+ */
+ xrt_subdev_root_cb_t xsp_root_cb;
+ void *xsp_root_cb_arg;
+
+ /* Something to associate w/ root for msg printing. */
+ const char *xsp_root_name;
+
+ /*
+ * Char dev support for this subdev instance.
+ * Initialized by subdev driver.
+ */
+ struct cdev xsp_cdev;
+ struct device *xsp_sysdev;
+ struct mutex xsp_devnode_lock; /* devnode lock */
+ struct completion xsp_devnode_comp;
+ int xsp_devnode_ref;
+ bool xsp_devnode_online;
+ bool xsp_devnode_excl;
+
+ /*
+ * Subdev driver specific init data. The buffer should be embedded
+ * in this data structure buffer after dtb, so that it can be freed
+ * together with platform data.
+ */
+ loff_t xsp_priv_off; /* Offset into this platform data buffer. */
+ size_t xsp_priv_len;
+
+ /*
+ * Populated by parent driver to describe the device tree for
+ * the subdev driver to handle. Should always be last one since it's
+ * of variable length.
+ */
+ char xsp_dtb[sizeof(struct fdt_header)];
could be xsp_dtb[1] and save including the fdt headers just to get a size that doesn't matter.
+};
+
+/*
+ * this struct define the endpoints belong to the same subdevice
+ */
+struct xrt_subdev_ep_names {
+ const char *ep_name;
+ const char *regmap_name;
+};
+
+struct xrt_subdev_endpoints {
+ struct xrt_subdev_ep_names *xse_names;
+ /* minimum number of endpoints to support the subdevice */
+ u32 xse_min_ep;
see earlier comment about needed a null entry and checking for it.
a 'size' element would be better here.
+#define INODE2PDATA(inode) \
+ container_of((inode)->i_cdev, struct xrt_subdev_platdata, xsp_cdev)
+#define INODE2PDEV(inode) \
+ to_platform_device(kobj_to_dev((inode)->i_cdev->kobj.parent))
+#define CDEV_NAME(sysdev) (strchr((sysdev)->kobj.name, '!') + 1)
+
+/* Allow it to be accessed from cdev. */
+static void xleaf_devnode_allowed(struct platform_device *pdev)
+{
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(pdev);
+
+ /* Allow new opens. */
+ mutex_lock(&pdata->xsp_devnode_lock);
+ pdata->xsp_devnode_online = true;
+ mutex_unlock(&pdata->xsp_devnode_lock);
+}
+
+/* Turn off access from cdev and wait for all existing user to go away. */
+static int xleaf_devnode_disallowed(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct xrt_subdev_platdata *pdata = DEV_PDATA(pdev);
+
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ /* Prevent new opens. */
+ pdata->xsp_devnode_online = false;
+ /* Wait for existing user to close. */
+ while (!ret && pdata->xsp_devnode_ref) {
+ int rc;
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+ rc = wait_for_completion_killable(&pdata->xsp_devnode_comp);
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ if (rc == -ERESTARTSYS) {
+ /* Restore online state. */
+ pdata->xsp_devnode_online = true;
+ xrt_err(pdev, "%s is in use, ref=%d",
+ CDEV_NAME(pdata->xsp_sysdev),
+ pdata->xsp_devnode_ref);
+ ret = -EBUSY;
+ }
+ }
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+
+ return ret;
+}
+
+static struct platform_device *
+__xleaf_devnode_open(struct inode *inode, bool excl)
+{
+ struct xrt_subdev_platdata *pdata = INODE2PDATA(inode);
+ struct platform_device *pdev = INODE2PDEV(inode);
+ bool opened = false;
+
+ mutex_lock(&pdata->xsp_devnode_lock);
+
+ if (pdata->xsp_devnode_online) {
+ if (excl && pdata->xsp_devnode_ref) {
+ xrt_err(pdev, "%s has already been opened exclusively",
+ CDEV_NAME(pdata->xsp_sysdev));
+ } else if (!excl && pdata->xsp_devnode_excl) {
+ xrt_err(pdev, "%s has been opened exclusively",
+ CDEV_NAME(pdata->xsp_sysdev));
+ } else {
+ pdata->xsp_devnode_ref++;
+ pdata->xsp_devnode_excl = excl;
+ opened = true;
+ xrt_info(pdev, "opened %s, ref=%d",
+ CDEV_NAME(pdata->xsp_sysdev),
+ pdata->xsp_devnode_ref);
+ }
+ } else {
+ xrt_err(pdev, "%s is offline", CDEV_NAME(pdata->xsp_sysdev));
+ }
+
+ mutex_unlock(&pdata->xsp_devnode_lock);
+
+ pdev = opened ? pdev : NULL;
+ return pdev;
+}
+
+struct platform_device *
+xleaf_devnode_open_excl(struct inode *inode)
+{
+ return __xleaf_devnode_open(inode, true);
+}
+
+struct platform_device *
+xleaf_devnode_open(struct inode *inode)
+{
+ return __xleaf_devnode_open(inode, false);
+}
+EXPORT_SYMBOL_GPL(xleaf_devnode_open);
generally
exported systems should have their decl's in include/linux/fpga/
These are in drivers/fpga/xrt/include/xleaf.h
as exported, they should have a better than average prefix.
maybe 'xrt_fpga_'
Shouldn't the root have no parent ?
Could then check if d->parent == NULL instead of bus type
+ return d;
+}
+
+/*
+ * It represents a holder of a subdev. One holder can repeatedly hold a subdev
+ * as long as there is a unhold corresponding to a hold.
+ */
+struct xrt_subdev_holder {
+ struct list_head xsh_holder_list;
+ struct device *xsh_holder;
+ int xsh_count;
+ struct kref xsh_kref;
general, i see this in struct xrt_subdev
guessing 'xsh' is xrt subdev holder.
why is this prefix needed for the elements ? consider removing it.
+};
+
+/*
+ * It represents a specific instance of platform driver for a subdev, which
+ * provides services to its clients (another subdev driver or root driver).
+ */
+struct xrt_subdev {
+ struct list_head xs_dev_list;
+ struct list_head xs_holder_list;
+ enum xrt_subdev_id xs_id; /* type of subdev */
+ struct platform_device *xs_pdev; /* a particular subdev inst */
+ struct completion xs_holder_comp;
+};
+
+static struct xrt_subdev *xrt_subdev_alloc(void)
+{
+ struct xrt_subdev *sdev = vzalloc(sizeof(*sdev));
+
+ /* Prepare platform data passed to subdev. */
+ pdata = vzalloc(pdata_sz);
+ if (!pdata)
+ goto fail;
+
+ pdata->xsp_root_cb = pcb;
+ pdata->xsp_root_cb_arg = pcb_arg;
+ memcpy(pdata->xsp_dtb, dtb, dtb_len);
+ if (id == XRT_SUBDEV_GRP) {
+ /* Group can only be created by root driver. */
+ pdata->xsp_root_name = dev_name(parent);
+ } else {
+ struct platform_device *grp = to_platform_device(parent);
+ /* Leaf can only be created by group driver. */
+ WARN_ON(strcmp(xrt_drv_name(XRT_SUBDEV_GRP), platform_get_device_id(grp)->name));
+ pdata->xsp_root_name = DEV_PDATA(grp)->xsp_root_name;
+ }
+
+ /* Obtain dev instance number. */
+ inst = xrt_drv_get_instance(id);
+ if (inst < 0) {
+ dev_err(parent, "failed to obtain instance: %d", inst);
+ goto fail;
+ }
+
+ /* Create subdev. */
+ if (id == XRT_SUBDEV_GRP) {
+ pdev = platform_device_register_data(parent, xrt_drv_name(XRT_SUBDEV_GRP),
+ inst, pdata, pdata_sz);
+ } else {
+ int rc = xrt_subdev_getres(parent, id, dtb, &res, &res_num);
+
+ if (rc) {
+ dev_err(parent, "failed to get resource for %s.%d: %d",
+ xrt_drv_name(id), inst, rc);
+ goto fail;
+ }
+ pdev = platform_device_register_resndata(parent, xrt_drv_name(id),
+ inst, res, res_num, pdata, pdata_sz);
+ vfree(res);
+ }
a small optimization
platform_device_register_data is a wrapper to platform_device_register_resndata.
with initial values for res, res_num, just one call need to be made.
+ if (IS_ERR(pdev)) {
+ dev_err(parent, "failed to create subdev for %s inst %d: %ld",
+ xrt_drv_name(id), inst, PTR_ERR(pdev));
+ goto fail;
+ }
+ sdev->xs_pdev = pdev;
+
+ if (device_attach(DEV(pdev)) != 1) {
+ xrt_err(pdev, "failed to attach");
+ goto fail;
+ }
+
+ if (sysfs_create_group(&DEV(pdev)->kobj, &xrt_subdev_attrgroup))
+ xrt_err(pdev, "failed to create sysfs group");
no failure ?
+
+ /*
+ * Create sysfs sym link under root for leaves
+ * under random groups for easy access to them.
+ */
+ if (id != XRT_SUBDEV_GRP) {
+ if (sysfs_create_link(&find_root(pdev)->kobj,
+ &DEV(pdev)->kobj, dev_name(DEV(pdev)))) {
+ xrt_err(pdev, "failed to create sysfs link");
+ }
+ }
+
+ /* All done, ready to handle req thru cdev. */
+ if (xrt_subdev_cdev_auto_creation(pdev))
+ xleaf_devnode_create(pdev, DEV_DRVDATA(pdev)->xsd_file_ops.xsf_dev_name, NULL);
+
+ vfree(pdata);
+ return sdev;
+
+fail:
Instead of adding checks in the error handling block, add more specific labels and gotos.
I think i have noticed this before, so apply this advice generally.
Instead of these clunky casts, why not make the type of the args void *
and leave it to the handler to cast.
this would unify the signature of these functions somewhat.
i am wondering about the locking here.
xsp_closing is only set to true in this function.
the unlocking then relocking in the loop is strange, why do you need to do this ?
Could this and similar looping be avoided by storing sdev in pdev ?
+ ret = xrt_subdev_release(sdev, holder_dev);
+ break;
+ }
+ mutex_unlock(lk);
+
+ return ret;
+}
+
+int xrt_subdev_pool_put(struct xrt_subdev_pool *spool, struct platform_device *pdev,
+ struct device *holder_dev)
+{
+ int ret = xrt_subdev_pool_put_impl(spool, pdev, holder_dev);
+
+ if (ret)
+ return ret;
+
+ if (!DEV_IS_PCI(holder_dev)) {
! root_dev() or similar.
If you really need to use DEV_IS_PCI, do it only once so when you need to change something you don not have to find all the calls to DEV_IS_PCI.
Header files should be self contained, a quick look at xroot.h makes me suspicious that device and mutex decls assume the includer has added their headers before this one
+ bool xsp_closing;
If you thing additional state will be needed, you could change this to a bitfield. sizewise with compiler padding i don't think the size would change.
+};
+
+/*
+ * Subdev pool API for root and group drivers only.
'API' makes me think these should go in include/linux/fpga
Do/will these functions get called outside of the drivers/fpga ?
Is this the best place to define some pci magic ?
It looks like the xroot is combination of the root of the device tree and the pci setup for the board.
Can the pci-ness be split and the root mostly handling how the subtrees are organized ?
scanning the code i expected to see ... && pdev->instance == a->instance
pdev->id == a->instance looks like a bug, a change to pdev->id element name to pdev->instance or in needed of a comment.
could this state and the error be moved to xrt_sbudev_pool_add where locking happens so atomics are not needed ?
+ ret = xrt_subdev_pool_add(&xr->grps.pool, XRT_SUBDEV_GRP,
+ xroot_root_cb, xr, dtb);
+ if (ret >= 0) {
+ schedule_work(&xr->grps.bringup_work);
+ } else {
+ atomic_dec(&xr->grps.bringup_pending);
+ atomic_inc(&xr->grps.bringup_failed);
+ xroot_err(xr, "failed to create group: %d", ret);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xroot_create_group);
+
+static int xroot_destroy_single_group(struct xroot *xr, int instance)
+{
A better name would be 'xroot_destroy_group'
+ struct platform_device *pdev = NULL;
+ int ret;
+
+ WARN_ON(instance < 0);
+ ret = xroot_get_group(xr, instance, &pdev);
+ if (ret)
+ return ret;
+
+ xroot_group_trigger_event(xr, instance, XRT_EVENT_PRE_REMOVAL);
+
+ /* Now tear down all children in this group. */
+ ret = xleaf_ioctl(pdev, XRT_GROUP_FINI_CHILDREN, NULL);
+ (void)xroot_put_group(xr, pdev);
+ if (!ret) {
+ ret = xrt_subdev_pool_del(&xr->grps.pool, XRT_SUBDEV_GRP,
+ instance);
+ }
+
+ return ret;
+}
+
+static int xroot_destroy_group(struct xroot *xr, int instance)
A better name would be 'xroot_destroy_groups'
+{
+ struct platform_device *target = NULL;
+ struct platform_device *deps = NULL;
+ int ret;
+
+ WARN_ON(instance < 0);
+ /*
+ * Make sure target group exists and can't go away before
+ * we remove it's dependents
+ */
+ ret = xroot_get_group(xr, instance, &target);
+ if (ret)
+ return ret;
+
+ /*
+ * Remove all groups depend on target one.
+ * Assuming subdevs in higher group ID can depend on ones in
+ * lower ID groups, we remove them in the reservse order.
+ */
+ while (xroot_get_group(xr, XROOT_GRP_LAST, &deps) != -ENOENT) {
+ int inst = deps->id;
+
+ xroot_put_group(xr, deps);
+ if (instance == inst)
+ break;
breaking in the middle does not seem correct.
please add a comment
looking at these two cases without any changes to arg but a cast, i think these and the next pass the void * onto the function and have the function manage the cast.
another instance = id, the variable and element names should be consistent.
earlier (id, instance) is used to uniquely determine a node. if that is so then using the names should be kept seperate.
From: Tom Rix <trix@redhat.com> Date: 2021-02-26 15:03:14
A question i do not know the answer to.
Seems like 'golden' is linked to a manufacturing (diagnostics?) image.
If the public will never see it, should handling it here be done ?
Moritz, do you know ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
The PCIE device driver which attaches to management function on Alveo
to the management
devices. It instantiates one or more partition drivers which in turn
more fpga partition / group ?
instantiate platform drivers. The instantiation of partition and platform
drivers is completely data driven.
data driven ? everything is data driven. do you mean dtb driven ?
demagic this table, look at dfl-pci for how to use existing #define for the vendor and create a new on for the device. If there are vf's add them at the same time.
What is a golden image ?
This is a bus call, not a device call.
Can this be changed into something like what hot reset does ?
+}
+
+static int xmgmt_match_slot_and_restore(struct device *dev, void *data)
+{
+ struct xmgmt *xm = data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) {
+ pci_restore_state(pdev);
+ pci_cfg_access_unlock(pdev);
+ }
+
+ return 0;
+}
+
+static void xmgmt_pci_restore_config_all(struct xmgmt *xm)
+{
+ bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_restore);
+}
+
+static void xmgmt_root_hot_reset(struct pci_dev *pdev)
+{
+ struct xmgmt *xm = pci_get_drvdata(pdev);
+ struct pci_bus *bus;
+ u8 pci_bctl;
+ u16 pci_cmd, devctl;
+ int i, ret;
+
+ xmgmt_info(xm, "hot reset start");
+
+ xmgmt_pci_save_config_all(xm);
+
+ pci_disable_device(pdev);
+
+ bus = pdev->bus;
+
+ /*
+ * When flipping the SBR bit, device can fall off the bus. This is
+ * usually no problem at all so long as drivers are working properly
+ * after SBR. However, some systems complain bitterly when the device
+ * falls off the bus.
+ * The quick solution is to temporarily disable the SERR reporting of
+ * switch port during SBR.
+ */
+
+ pci_read_config_word(bus->self, PCI_COMMAND, &pci_cmd);
+ pci_write_config_word(bus->self, PCI_COMMAND,
+ (pci_cmd & ~PCI_COMMAND_SERR));
+ pcie_capability_read_word(bus->self, PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL,
+ (devctl & ~PCI_EXP_DEVCTL_FERE));
+ pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl);
+ pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
how the pci config values are set and cleared should be consistent.
this call should be
pci_write_config_byte (... pci_bctl | PCI_BRIDGE_CTL_BUF_RESET )
and the next &= avoided
+
+ msleep(100);
+ pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+ ssleep(1);
+
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, devctl);
+ pci_write_config_word(bus->self, PCI_COMMAND, pci_cmd);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ xmgmt_err(xm, "failed to enable device, ret %d", ret);
+
+ for (i = 0; i < 300; i++) {
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd != 0xffff)
what happens with i == 300 and pci_cmd is still 0xffff ?
+ break;
+ msleep(20);
+ }
+
+ xmgmt_info(xm, "waiting for %d ms", i * 20);
+ xmgmt_pci_restore_config_all(xm);
+ xmgmt_config_pci(xm);
+}
+
+static int xmgmt_create_root_metadata(struct xmgmt *xm, char **root_dtb)
+{
+ char *dtb = NULL;
+ int ret;
+
+ ret = xrt_md_create(XMGMT_DEV(xm), &dtb);
+ if (ret) {
+ xmgmt_err(xm, "create metadata failed, ret %d", ret);
+ goto failed;
+ }
+
+ ret = xroot_add_vsec_node(xm->root, dtb);
+ if (ret == -ENOENT) {
+ /*
+ * We may be dealing with a MFG board.
+ * Try vsec-golden which will bring up all hard-coded leaves
+ * at hard-coded offsets.
+ */
+ ret = xroot_add_simple_node(xm->root, dtb, XRT_MD_NODE_VSEC_GOLDEN);
@@ -0,0 +1,37 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*HeaderfileforXilinxRuntime(XRT)driver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*ChengZhen<maxz@xilinx.com>+*/++#ifndef _XMGMT_MAIN_H_+#define _XMGMT_MAIN_H_++#include<linux/xrt/xclbin.h>+#include"xleaf.h"++enumxrt_mgmt_main_ioctl_cmd{+/* section needs to be vfree'd by caller */+XRT_MGMT_MAIN_GET_AXLF_SECTION=XRT_XLEAF_CUSTOM_BASE,/* See comments in xleaf.h */
the must free instructions should go with the pointer needing freeing
+ /* vbnv needs to be kfree'd by caller */
+ XRT_MGMT_MAIN_GET_VBNV,
+};
+
+enum provider_kind {
+ XMGMT_BLP,
+ XMGMT_PLP,
+ XMGMT_ULP,
this function is not needed, it is used only in a direct call from xmgmt_load_firmware.
looks like it is part of an error hander which will return this NOSUPPORT error instead of the real error from load_firmware_from disk
From: Moritz Fischer <mdf@kernel.org> Date: 2021-02-26 17:58:04
On Fri, Feb 26, 2021 at 07:01:05AM -0800, Tom Rix wrote:
A question i do not know the answer to.
Seems like 'golden' is linked to a manufacturing (diagnostics?) image.
From my brief history with Xilinx Ultrascale+ PCI cards I recall the golden
image being a sort of known good recovery image.
If we can't tell it should probably be explained better :)
If the public will never see it, should handling it here be done ?
Yes. We do want people to run their entire stack using mainline linux,
not just a part of it, if code is needed to get from recovery image to
full image or similar, then we should support that.
Moritz, do you know ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
The PCIE device driver which attaches to management function on Alveo
to the management
quoted
devices. It instantiates one or more partition drivers which in turn
more fpga partition / group ?
quoted
instantiate platform drivers. The instantiation of partition and platform
drivers is completely data driven.
data driven ? everything is data driven. do you mean dtb driven ?
demagic this table, look at dfl-pci for how to use existing #define for the vendor and create a new on for the device. If there are vf's add them at the same time.
What is a golden image ?
This is a bus call, not a device call.
Can this be changed into something like what hot reset does ?
quoted
+}
+
+static int xmgmt_match_slot_and_restore(struct device *dev, void *data)
+{
+ struct xmgmt *xm = data;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (XMGMT_DEV_ID(pdev) == XMGMT_DEV_ID(xm->pdev)) {
+ pci_restore_state(pdev);
+ pci_cfg_access_unlock(pdev);
+ }
+
+ return 0;
+}
+
+static void xmgmt_pci_restore_config_all(struct xmgmt *xm)
+{
+ bus_for_each_dev(&pci_bus_type, NULL, xm, xmgmt_match_slot_and_restore);
+}
+
+static void xmgmt_root_hot_reset(struct pci_dev *pdev)
+{
+ struct xmgmt *xm = pci_get_drvdata(pdev);
+ struct pci_bus *bus;
+ u8 pci_bctl;
+ u16 pci_cmd, devctl;
+ int i, ret;
+
+ xmgmt_info(xm, "hot reset start");
+
+ xmgmt_pci_save_config_all(xm);
+
+ pci_disable_device(pdev);
+
+ bus = pdev->bus;
+
+ /*
+ * When flipping the SBR bit, device can fall off the bus. This is
+ * usually no problem at all so long as drivers are working properly
+ * after SBR. However, some systems complain bitterly when the device
+ * falls off the bus.
+ * The quick solution is to temporarily disable the SERR reporting of
+ * switch port during SBR.
+ */
+
+ pci_read_config_word(bus->self, PCI_COMMAND, &pci_cmd);
+ pci_write_config_word(bus->self, PCI_COMMAND,
+ (pci_cmd & ~PCI_COMMAND_SERR));
+ pcie_capability_read_word(bus->self, PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL,
+ (devctl & ~PCI_EXP_DEVCTL_FERE));
+ pci_read_config_byte(bus->self, PCI_BRIDGE_CONTROL, &pci_bctl);
+ pci_bctl |= PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
how the pci config values are set and cleared should be consistent.
this call should be
pci_write_config_byte (... pci_bctl | PCI_BRIDGE_CTL_BUF_RESET )
and the next &= avoided
quoted
+
+ msleep(100);
+ pci_bctl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_byte(bus->self, PCI_BRIDGE_CONTROL, pci_bctl);
+ ssleep(1);
+
+ pcie_capability_write_word(bus->self, PCI_EXP_DEVCTL, devctl);
+ pci_write_config_word(bus->self, PCI_COMMAND, pci_cmd);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ xmgmt_err(xm, "failed to enable device, ret %d", ret);
+
+ for (i = 0; i < 300; i++) {
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd != 0xffff)
what happens with i == 300 and pci_cmd is still 0xffff ?
quoted
+ break;
+ msleep(20);
+ }
+
+ xmgmt_info(xm, "waiting for %d ms", i * 20);
+ xmgmt_pci_restore_config_all(xm);
+ xmgmt_config_pci(xm);
+}
+
+static int xmgmt_create_root_metadata(struct xmgmt *xm, char **root_dtb)
+{
+ char *dtb = NULL;
+ int ret;
+
+ ret = xrt_md_create(XMGMT_DEV(xm), &dtb);
+ if (ret) {
+ xmgmt_err(xm, "create metadata failed, ret %d", ret);
+ goto failed;
+ }
+
+ ret = xroot_add_vsec_node(xm->root, dtb);
+ if (ret == -ENOENT) {
+ /*
+ * We may be dealing with a MFG board.
+ * Try vsec-golden which will bring up all hard-coded leaves
+ * at hard-coded offsets.
+ */
+ ret = xroot_add_simple_node(xm->root, dtb, XRT_MD_NODE_VSEC_GOLDEN);
Alveo FPGA firmware and partial reconfigure file are in xclbin format.
This code enumerates and extracts
Will change this to
Alveo FPGA firmware and partial reconfigure file are in xclbin format. This
code enumerates and extracts sections from xclbin files. xclbin.h is cross
platform and used across all platforms and OS.
quoted
Add
code to enumerate and extract sections from xclbin files. xclbin.h is cross
platform and used across all platforms and OS
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xclbin-helper.h | 52 +++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++++++++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++++++++++++++
3 files changed, 854 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
#defines should have a prefix, maybe XRT_ or XCLBIN_
Will add prefix XCLBIN_
quoted
+
+enum axlf_section_kind;
+struct axlf;
+
+/**
+ * Bitstream header information as defined by Xilinx tools.
+ * Please note that this struct definition is not owned by the driver.
+ */
+struct hw_icap_bit_header {
File headers usually have fixed length fields like uint32_t
Is this a structure the real header is converted into ?
This is not real header. This structure saves the information extracted
from bitstream header.
quoted
+ unsigned int header_length; /* Length of header in 32 bit words */
+ unsigned int bitstream_length; /* Length of bitstream to read in bytes*/
+ unsigned char *design_name; /* Design name get from bitstream */
+ unsigned char *part_name; /* Part name read from bitstream */
+ unsigned char *date; /* Date read from bitstream header */
+ unsigned char *time; /* Bitstream creation time */
+ unsigned int magic_length; /* Length of the magic numbers */
+ unsigned char *version; /* Version string */
+};
+
+const char *xrt_xclbin_kind_to_string(enum axlf_section_kind kind);
Only add decl's that are using in multiple files.
This is only defined in xclbin.c, why does it need to be in the header ?
This check can be added to the function call..
or the sanity checking added to the earier call to *get_section_hdr
There a number of small functions that can be combined.
Will change this and combine the small functions.
quoted
+
+ err = xrt_xclbin_check_section_hdr(mem_header, xclbin_len);
+ if (err)
+ return err;
+
+ *offset = mem_header->m_sectionOffset;
+ *size = mem_header->m_sectionSize;
+
+ return 0;
+}
+
+/* caller should free the allocated memory for **data */
must free
This comment also needs to be with the *.h decl
a general comment
for exported function checking the validity of the inputs in more important.
here you assume **data is valid, really you should check.
Will add check for exported functions.
quoted
+ if (len)
+ *len = size;
len setting being optional, needs to be in the *.h comment
Instead of allocating new memory and making copies of bits of *data
why not have the points reference data ?
Agree. I will change to use this points reference data.
The size operations look like translating big endian data to little endian.
This will break on a big endian host.
It does not translate to little endian. It converts to host endian. We
can not use existing function because of the alignment.
quoted
+/* parse bitstream header */
+int xrt_xclbin_parse_bitstream_header(const unsigned char *data,
+ unsigned int size,
+ struct hw_icap_bit_header *header)
+{
+ unsigned int index;
+ unsigned int len;
+ unsigned int tmp;
+ unsigned int i;
+
+ memset(header, 0, sizeof(*header));
+ /* Start Index at start of bitstream */
+ index = 0;
+
+ /* Initialize HeaderLength. If header returned early inidicates
+ * failure.
This side effect should be documented in the *.h comment.
Also the multi line comment is a bit weird, not sure if it is ok
Will remove this. The function will return -EINVAL for failure cases.
quoted
+ */
+ header->header_length = XHI_BIT_HEADER_FAILURE;
+
+ /* Get "Magic" length */
+ header->magic_length = xhi_data_and_inc(data, &index, size);
+ header->magic_length = (header->magic_length << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Read in "magic" */
+ for (i = 0; i < header->magic_length - 1; i++) {
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (i % 2 == 0 && tmp != XHI_EVEN_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ if (i % 2 == 1 && tmp != XHI_ODD_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+ }
+
+ /* Read null end of magic data. */
+ tmp = xhi_data_and_inc(data, &index, size);
+
+ /* Read 0x01 (short) */
+ tmp = xhi_data_and_inc(data, &index, size);
+ tmp = (tmp << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Check the "0x01" half word */
+ if (tmp != 0x01)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Read 'a' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'a')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Design Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for design name and final null character. */
+ header->design_name = vmalloc(len);
+ if (!header->design_name)
+ return -ENOMEM;
+
+ /* Read in Design Name */
+ for (i = 0; i < len; i++)
+ header->design_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->design_name[len - 1] != '\0')
+ return -1;
+
+ header->version = strstr(header->design_name, "Version=") + strlen("Version=");
+
+ /* Read 'b' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'b')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Part Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for part name and final null character. */
+ header->part_name = vmalloc(len);
+ if (!header->part_name)
+ return -ENOMEM;
+
+ /* Read in part name */
+ for (i = 0; i < len; i++)
+ header->part_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->part_name[len - 1] != '\0')
+ return -1;
+
+ /* Read 'c' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'c')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get date length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for date and final null character. */
+ header->date = vmalloc(len);
+ if (!header->date)
+ return -ENOMEM;
+
+ /* Read in date name */
+ for (i = 0; i < len; i++)
+ header->date[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->date[len - 1] != '\0')
+ return -1;
generally -EINVAL is more meaningful than -1
Will fix this.
quoted
+
+ /* Read 'd' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'd')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get time length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for time and final null character. */
+ header->time = vmalloc(len);
+ if (!header->time)
+ return -ENOMEM;
+
+ /* Read in time name */
+ for (i = 0; i < len; i++)
+ header->time[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->time[len - 1] != '\0')
+ return -1;
+
+ /* Read 'e' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'e')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get byte length of bitstream */
+ header->bitstream_length = xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
generally a problem
This is confusing, collect the bytes in a temp[] and construct the header->bitstream_length in on statement.
This is a case where xhi_data_and_inc return is not checked and if it failed could blow up later.
Will restructure this function and check the possible overflow.
quoted
+
+ header->header_length = index;
index is not a good variable name if it going to be stored as a length.
consider changing it to something like current_length.
What is clock stuff doing in xclbin ?
I think clock needs its own file
xclbin file contains a section which describes the clock settings to
running this xclbin on hardware. The _clock_ functions convert clock
section to XRT driver metadata which is in device tree format.
quoted
+
+static const char *clock_type2clkfreq_name(u32 type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clock_desc); i++) {
+ if (clock_desc[i].clock_xclbin_type == type)
+ return clock_desc[i].clkfreq_ep_name;
+ }
+ return NULL;
+}
+
+static int xrt_xclbin_add_clock_metadata(struct device *dev,
+ const struct axlf *xclbin,
+ char *dtb)
+{
+ int i;
+ u16 freq;
+ struct clock_freq_topology *clock_topo;
+ int rc = xrt_xclbin_get_section(xclbin, CLOCK_FREQ_TOPOLOGY,
+ (void **)&clock_topo, NULL);
+
+ if (rc)
+ return 0;
failing is ok ?
If clock section does not exist in xclbin, that means the xclbin uses
default clock. And this is a valid case. I will add comment
/* if clock section does not exist, add nothing and return success */
Review these includes, some could be convenience includes.
removed uuid.h and version.h.
ex/ linux/version.h with no obvious use of version macros.
struct axlf_header {
+ uint64_t m_length; /* Total size of the xclbin file */
.. snip ..
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
As mentioned in an earlier patch, if uuid_t is larger than 16 bytes, axlf_header breaks.
while it is convenient to have this type here, it would be better this access was handled in another way.
Maybe a host specific function.
Agree. will change to "unsigned char uuid[16]".
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
This data structure is shared with other tools. And the structure is
well defined with reasonable alignment. It is compatible with all
compilers we have tested. So pragma pack is not necessary.
quoted
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * DOC: Container format for Xilinx FPGA images
+ * The container stores bitstreams, metadata and firmware images.
+ * xclbin/xsabin is ELF-like binary container format. It is structured
is an ELF-like file format. It is a structured
Will fix this.
quoted
+ * series of sections. There is a file header followed by several section
+ * headers which is followed by sections. A section header points to an
+ * actual section. There is an optional signature at the end. The
+ * following figure illustrates a typical xclbin:
+ *
+ * +---------------------+
+ * | |
+ * | HEADER |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | SIGNATURE |
+ * | (OPTIONAL) |
+ * +---------------------+
This ascii art is a mixture of tabs and spaces, for someone with tab = 2 spaces, this will look messed up.
convert the tabs to spaces
Will fix this.
quoted
+ */
+
+enum XCLBIN_MODE {
+ XCLBIN_FLAT,
generally
all enums used in a file format should be initialized.
This likely should be
XCLBIN_FLAT = 0,
i did not see the version checked earlier, which one is expected ?
Will check Major.
quoted
+ uint8_t m_versionMinor; /* Minor Version */
+ uint32_t m_mode; /* XCLBIN_MODE */
+ union {
+ struct {
+ uint64_t m_platformId; /* 64 bit platform ID: */
+ /* vendor-device-subvendor-subdev */
+ uint64_t m_featureId; /* 64 bit feature id */
+ } rom;
+ unsigned char rom_uuid[16]; /* feature ROM UUID for which */
+ /* this xclbin was generated */
+ };
+ unsigned char m_platformVBNV[64]; /* e.g. */
what is VBNV?
VBNV stands for Vendor, BoardID, Name, Version. It is a string like
<Vendor>:<BoardID>:<Name>:<Version> or <Vendor>_<BoardID>_<Name>_<Version>
quoted
+ /* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
+ char m_debug_bin[16]; /* Name of binary with debug */
+ /* information */
+ uint32_t m_numSections; /* Number of section headers */
+};
+
+struct axlf {
+ char m_magic[8]; /* Should be "xclbin2\0" */
+ int32_t m_signature_length; /* Length of the signature. */
+ /* -1 indicates no signature */
+ unsigned char reserved[28]; /* Note: Initialized to 0xFFs */
+
+ unsigned char m_keyBlock[256]; /* Signature for validation */
+ /* of binary */
+ uint64_t m_uniqueId; /* axlf's uniqueId, use it to */
+ /* skip redownload etc */
+ struct axlf_header m_header; /* Inline header */
+ struct axlf_section_header m_sections[1]; /* One or more section */
+ /* headers follow */
+};
+
+/* bitstream information */
+struct xlnx_bitstream {
+ uint8_t m_freq[8];
+ char bits[1];
+};
+
+/**** MEMORY TOPOLOGY SECTION ****/
+struct mem_data {
+ uint8_t m_type; /* enum corresponding to mem_type. */
+ uint8_t m_used; /* if 0 this bank is not present */
+ union {
+ uint64_t m_size; /* if mem_type DDR, then size in KB; */
+ uint64_t route_id; /* if streaming then "route_id" */
+ };
+ union {
+ uint64_t m_base_address;/* if DDR then the base address; */
+ uint64_t flow_id; /* if streaming then "flow id" */
+ };
+ unsigned char m_tag[16]; /* DDR: BANK0,1,2,3, has to be null */
+ /* terminated; if streaming then stream0, 1 etc */
+};
+
+struct mem_topology {
+ int32_t m_count; /* Number of mem_data */
+ struct mem_data m_mem_data[1]; /* Should be sorted on mem_type */
+};
+
+/**** CONNECTIVITY SECTION ****/
+/* Connectivity of each argument of Kernel. It will be in terms of argument
This section does not make sense.
Likely you mean some algorithm kernel, rather than the linux kernel.
The kernel here means hardware IP kernel. I will change the term to CU
(Compute Unit).
quoted
+ * index associated. For associating kernel instances with arguments and
+ * banks, start at the connectivity section. Using the m_ip_layout_index
+ * access the ip_data.m_name. Now we can associate this kernel instance
+ * with its original kernel name and get the connectivity as well. This
+ * enables us to form related groups of kernel instances.
+ */
+
+struct connection {
+ int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */
+ /* skipped */
+ int32_t m_ip_layout_index; /* index into the ip_layout section. */
+ /* ip_layout.m_ip_data[index].m_type == IP_KERNEL */
+ int32_t mem_data_index; /* index of the m_mem_data . Flag error is */
+ /* m_used false. */
+};
+
+struct connectivity {
+ int32_t m_count;
+ struct connection m_connection[1];
+};
+
+/**** IP_LAYOUT SECTION ****/
+
+/* IP Kernel */
+#define IP_INT_ENABLE_MASK 0x0001
+#define IP_INTERRUPT_ID_MASK 0x00FE
+#define IP_INTERRUPT_ID_SHIFT 0x1
+
+enum IP_CONTROL {
+ AP_CTRL_HS = 0,
+ AP_CTRL_CHAIN = 1,
+ AP_CTRL_NONE = 2,
+ AP_CTRL_ME = 3,
+ ACCEL_ADAPTER = 4
assigning beyond the first is not necessary unless there are dups or gaps
Will fix this.
quoted
+};
+
+#define IP_CONTROL_MASK 0xFF00
+#define IP_CONTROL_SHIFT 0x8
+
+/* IPs on AXI lite - their types, names, and base addresses.*/
+struct ip_data {
+ uint32_t m_type; /* map to IP_TYPE enum */
+ union {
+ uint32_t properties; /* Default: 32-bits to indicate ip */
+ /* specific property. */
+ /* m_type: IP_KERNEL
+ * m_int_enable : Bit - 0x0000_0001;
+ * m_interrupt_id : Bits - 0x0000_00FE;
+ * m_ip_control : Bits = 0x0000_FF00;
+ */
+ struct { /* m_type: IP_MEM_* */
+ uint16_t m_index;
+ uint8_t m_pc_index;
+ uint8_t unused;
+ } indices;
+ };
+ uint64_t m_base_address;
+ uint8_t m_name[64]; /* eg Kernel name corresponding to KERNEL */
+ /* instance, can embed CU name in future. */
+};
+
+struct ip_layout {
+ int32_t m_count;
+ struct ip_data m_ip_data[1]; /* All the ip_data needs to be sorted */
+ /* by m_base_address. */
general
doing the bla[1] for c++ ?
This file is cross platform and having an array with size 0 chokes
certain compilers.
Thanks,
Lizhi
Tom
quoted
+};
+
+/*** Debug IP section layout ****/
+enum DEBUG_IP_TYPE {
+ UNDEFINED = 0,
+ LAPC,
+ ILA,
+ AXI_MM_MONITOR,
+ AXI_TRACE_FUNNEL,
+ AXI_MONITOR_FIFO_LITE,
+ AXI_MONITOR_FIFO_FULL,
+ ACCEL_MONITOR,
+ AXI_STREAM_MONITOR,
+ AXI_STREAM_PROTOCOL_CHECKER,
+ TRACE_S2MM,
+ AXI_DMA,
+ TRACE_S2MM_FULL
+};
+
+struct debug_ip_data {
+ uint8_t m_type; /* type of enum DEBUG_IP_TYPE */
+ uint8_t m_index_lowbyte;
+ uint8_t m_properties;
+ uint8_t m_major;
+ uint8_t m_minor;
+ uint8_t m_index_highbyte;
+ uint8_t m_reserved[2];
+ uint64_t m_base_address;
+ char m_name[128];
+};
+
+struct debug_ip_layout {
+ uint16_t m_count;
+ struct debug_ip_data m_debug_ip_data[1];
+};
+
+/* Supported clock frequency types */
+enum CLOCK_TYPE {
+ CT_UNUSED = 0, /* Initialized value */
+ CT_DATA = 1, /* Data clock */
+ CT_KERNEL = 2, /* Kernel clock */
+ CT_SYSTEM = 3 /* System Clock */
+};
+
+/* Clock Frequency Entry */
+struct clock_freq {
+ uint16_t m_freq_Mhz; /* Frequency in MHz */
+ uint8_t m_type; /* Clock type (enum CLOCK_TYPE) */
+ uint8_t m_unused[5]; /* Not used - padding */
+ char m_name[128]; /* Clock Name */
+};
+
+/* Clock frequency section */
+struct clock_freq_topology {
+ int16_t m_count; /* Number of entries */
+ struct clock_freq m_clock_freq[1]; /* Clock array */
+};
+
+/* Supported MCS file types */
+enum MCS_TYPE {
+ MCS_UNKNOWN = 0, /* Initialized value */
+ MCS_PRIMARY = 1, /* The primary mcs file data */
+ MCS_SECONDARY = 2, /* The secondary mcs file data */
+};
+
+/* One chunk of MCS data */
+struct mcs_chunk {
+ uint8_t m_type; /* MCS data type */
+ uint8_t m_unused[7]; /* padding */
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size */
+};
+
+/* MCS data section */
+struct mcs {
+ int8_t m_count; /* Number of chunks */
+ int8_t m_unused[7]; /* padding */
+ struct mcs_chunk m_chunk[1]; /* MCS chunks followed by data */
+};
+
+/* bmc data section */
+struct bmc {
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size (bytes) */
+ char m_image_name[64]; /* Name of the image */
+ /* (e.g., MSP432P401R) */
+ char m_device_name[64]; /* Device ID (e.g., VCU1525) */
+ char m_version[64];
+ char m_md5value[33]; /* MD5 Expected Value */
+ /* (e.g., 56027182079c0bd621761b7dab5a27ca)*/
+ char m_padding[7]; /* Padding */
+};
+
+/* soft kernel data section, used by classic driver */
+struct soft_kernel {
+ /** Prefix Syntax:
+ * mpo - member, pointer, offset
+ * This variable represents a zero terminated string
+ * that is offseted from the beginning of the section.
+ * The pointer to access the string is initialized as follows:
+ * char * pCharString = (address_of_section) + (mpo value)
+ */
+ uint32_t mpo_name; /* Name of the soft kernel */
+ uint32_t m_image_offset; /* Image offset */
+ uint32_t m_image_size; /* Image size */
+ uint32_t mpo_version; /* Version */
+ uint32_t mpo_md5_value; /* MD5 checksum */
+ uint32_t mpo_symbol_name; /* Symbol name */
+ uint32_t m_num_instances; /* Number of instances */
+ uint8_t padding[36]; /* Reserved for future use */
+ uint8_t reservedExt[16]; /* Reserved for future extended data */
+};
+
+enum CHECKSUM_TYPE {
+ CST_UNKNOWN = 0,
+ CST_SDBM = 1,
+ CST_LAST
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
group driver that manages life cycle of a bunch of leaf driver instances
and bridges them with root.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/group.h | 27 ++++
drivers/fpga/xrt/lib/group.c | 265 +++++++++++++++++++++++++++++++
2 files changed, 292 insertions(+)
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/lib/group.c
Are these really ioctl calls?
Seems more like messages between nodes in a tree.
Consider changing to better jagon, maybe ioctl -> msg
You're right. They are not really ioctl calls. They are just calls b/w
leaf nodes. I changed to leaf calls/commands.
quoted
+ */
+enum xrt_group_ioctl_cmd {
+ XRT_GROUP_GET_LEAF = XRT_XLEAF_CUSTOM_BASE, /* See comments in xleaf.h */
XRT_LEAF_CUSTOM_BASE is a #define, while these are enums. To be consistent, the XRT_LEAF_CUSTOM_BASE should be an enum in xleaf, you can initialize it to 64 there.
This happens should be programming error, so print out some error message
The root driver does not remember which group has created leaves or not.
It'll just blindly make the call. The group driver itself should
remember and tell the root that it's done already or not. So, this is
not considered as an error.
quoted
+ return -EEXIST;
+ }
+
+ xrt_info(xg->pdev, "bringing up leaves...");
+
+ /* Create all leaves based on dtb. */
+ if (!pdata)
+ goto bail;
move to above the lock and fail with something like -EINVAL
this assumes the enpoints are in an array and accessed serially.
this is fragile.
convert to using just the xrt_drv_get_endpoints() call
It does not seem to be fragile? The endpoint data structure is private
data structure so it has to be an array as defined. It's usage is just
like an ID table as we have in other PCIE drivers.
The leaves already created will still be there until the group is
destroyed. This is not considered as fatal error.
quoted
+ continue;
+ }
+ for (i = 0; eps->xse_names[i].ep_name ||
this assumes that xse_names[] always has a guard.
why not use xse_min_ep ?
xse_min_ep is to tell caller the minimum number of end points needs to
be collected before instantiate the leaf driver. it does not indicate
exactly how many end points this leaf can handle (it might be able to
handle more). So, the guard is necessary to tell caller total number of
end points it can handle.
This setting of r_data and continuing is strange, add a comment.
or if you intend to do a pair of operations, do the pair within the if block and remove the strangeness.
+ put_device(&base_re->dev);
+
+ xmgmt_region_cleanup(base_re);
+ xmgmt_destroy_region(base_re);
+ }
+}
+
+/*
+ * Program a given region with given xclbin image. Bring up the subdevs and the
+
+ /* free bridges to allow reprogram */
+ if (re->get_bridges)
+ fpga_bridges_put(&re->bridge_list);
+
+ /*
+ * Next bringup the subdevs for this region which will be managed by
+ * its own group object.
+ */
+ r_data->grp_inst = xleaf_create_group(pdev, dtb);
+ if (r_data->grp_inst < 0) {
+ xrt_err(pdev, "failed to create group, rc %d",
+ r_data->grp_inst);
+ rc = r_data->grp_inst;
+ return rc;
+ }
+
+ rc = xleaf_wait_for_group_bringup(pdev);
+ if (rc)
+ xrt_err(pdev, "group bringup failed, rc %d", rc);
failed but no error handling, shouldn't the leaves and group be torn down ?
+ return rc;
+}
+
+static int xmgmt_get_bridges(struct fpga_region *re)
+{
+ struct xmgmt_region *r_data = re->priv;
+ struct device *dev = &r_data->pdev->dev;
+
+ return fpga_bridge_get_to_list(dev, re->info, &re->bridge_list);
+}
+
+/*
+ * Program/create FPGA regions based on input xclbin file. This is key function
+ * stitching the flow together:
'This is ' .. sentence does not make sense, but is not needed drop it.
+ * 1. Identify a matching existing region for this xclbin
+ * 2. Tear down any previous objects for the found region
+ * 3. Program this region with input xclbin
+ * 4. Iterate over this region's interface uuids to determine if it defines any
+ * child region. Create fpga_region for the child region.
+ */
+int xmgmt_process_xclbin(struct platform_device *pdev,
+ struct fpga_manager *fmgr,
+ const struct axlf *xclbin,
+ enum provider_kind kind)
+{
+ struct fpga_region *re, *compat_re = NULL;
+ struct xmgmt_region_match_arg arg;
From: Tom Rix <trix@redhat.com> Date: 2021-02-28 16:56:19
On 2/26/21 1:23 PM, Lizhi Hou wrote:
Hi Tom,
snip
quoted
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
This data structure is shared with other tools. And the structure is well defined with reasonable alignment. It is compatible with all compilers we have tested. So pragma pack is not necessary.
You can not have possibly tested all the configurations since the kernel supports many arches and compilers.
If the tested existing alignment is ok, pragma pack should be a noop on your tested configurations.
And help cover the untested configurations.
Tom
-----Original Message-----
From: Moritz Fischer <mdf@kernel.org>
Sent: Sunday, February 21, 2021 10:40 AM
To: Tom Rix <trix@redhat.com>
Cc: Lizhi Hou <redacted>; linux-kernel@vger.kernel.org;
mdf@kernel.org; Lizhi Hou [off-list ref]; linux-fpga@vger.kernel.org;
Max Zhen [off-list ref]; Sonal Santan [off-list ref]; Michal
Simek [off-list ref]; Stefano Stabellini [off-list ref];
devicetree@vger.kernel.org; robh@kernel.org; Max Zhen [off-list ref]
Subject: Re: [PATCH V3 XRT Alveo 18/18] fpga: xrt: Kconfig and Makefile
updates for XRT drivers
On Sun, Feb 21, 2021 at 06:57:31AM -0800, Tom Rix wrote:
quoted
As I am looking through the files, I have this comment.
fpga/ is currently a single directory, while files could be organized
in subdirectories like
dfl/pci.c
instead have the possible subdir name as a prefix to the filename.
dfl-pci.c
For consistency,
xrt/metadata/metadata.c
should be
xrt-metadata.c
Agreed. Keep the prefix.
quoted
Likewise the build infra needs to integrated within the existing files
fpga/Kconfig,Makefile
This is a bigish refactor, so let's get a second opinion.
In what sense? You mean adding a subdirectory? Maybe something like this
drivers/fpga
- dfl/
- xilinx/
- intel/
- lattice/
- xrt/
...
would generally make sense.
We didn't have enough drivers to prioritize that yet, but we can look into it.
If longer term we would like to reorganize the drivers/fpga directory structure
should we keep the current directory structure for XRT as in the patch series?
quoted
Moritz ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Update fpga Kconfig/Makefile and add Kconfig/Makefile for new drivers.
Expand the comment, there are several new configs that could use an
explanation
-----Original Message-----
From: Tom Rix <trix@redhat.com>
Sent: Friday, February 19, 2021 2:26 PM
To: Lizhi Hou <redacted>; linux-kernel@vger.kernel.org
Cc: Lizhi Hou <redacted>; linux-fpga@vger.kernel.org; Max Zhen
[off-list ref]; Sonal Santan [off-list ref]; Michal Simek
[off-list ref]; Stefano Stabellini [off-list ref];
devicetree@vger.kernel.org; mdf@kernel.org; robh@kernel.org; Max Zhen
[off-list ref]
Subject: Re: [PATCH V3 XRT Alveo 01/18] Documentation: fpga: Add a
document describing XRT Alveo drivers
From the documentation, there are a couple of big questions and a bunch of
word smithing.
pseudo-bus : do we need a bus ?
We are looking for guidance here.
xrt-lib real platform devices that aren't fpga, do they need to move to another
subsystem ?
Drivers for the IPs that show up in the Alveo shell are not generic enough. They
fit into the framework that XRT uses. Is the idea that that they can be used in a
different context?
Overall looks good, love the ascii art!
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Describe XRT driver architecture and provide basic overview of Xilinx
Alveo platform.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842
@@ -0,0 +1,842 @@+.. SPDX-License-Identifier: GPL-2.0++==================================+XRTV2 Linux Kernel Driver Overview+==================================++Authors:++* Sonal Santan <sonal.santan@xilinx.com>+* Max Zhen <max.zhen@xilinx.com>+* Lizhi Hou <lizhi.hou@xilinx.com>++XRTV2 drivers are second generation `XRT+<https://github.com/Xilinx/XRT>`_ drivers which support `Alveo+<https://www.xilinx.com/products/boards-and-kits/alveo.html>`_+PCIe platforms from Xilinx.++XRTV2 drivers support *subsystem* style data driven platforms where+driver's
where the driver's
quoted
+configuration and behavior is determined by meta data provided by the
+platform (in *device tree* format). Primary management physical
+function (MPF) driver is called **xmgmt**. Primary user physical
+function (UPF) driver is called
+**xuser** and is under development. xrt driver framework and HW
+subsystem drivers are packaged into a library module called
+**xrt-lib**, which is shared by **xmgmt** and **xuser** (under
+development). The xrt driver framework
xuser still under development ?
quoted
+implements a pseudo-bus which is used to discover HW subsystems and
+facilitate
I am wondering if we can phase in the migration to formal bus architecture
based on struct bus_type as a follow on set of patches?
quoted
+inter HW subsystem interaction.
+
+Driver Modules
+==============
+
+xrt-lib.ko
+----------
+
+Repository of all subsystem drivers and pure software modules that
+can potentially
subsystem drivers
drivers in fpga/ should be for managing just the fpganess of the fpga.
soft devices ex/ a soft tty should go to their respective subsystem location
Are there any in this patchset you think might move ?
We have already shrunk the patch to only include FPGA centric pieces
necessary to get the bitstream download implemented. Should we explore
the question of subsystem drivers when we add support for more features of
the Alveo shell?
Maybe we can defer reviewing those now.
quoted
+be shared between xmgmt and xuser. All these drivers are structured
+as Linux *platform driver* and are instantiated by xmgmt (or xuser
+under development) based on meta data associated with hardware. The
+metadata is in the form of device tree
with the hardware
form of a device tree
Will change
quoted
+as mentioned before. Each platform driver statically defines a
+subsystem node array by using node name or a string in its
+``compatible`` property. And this array is eventually translated to IOMEM
resources of the platform device.
quoted
+
+The xrt-lib core infrastructure provides hooks to platform drivers
+for device node management, user file operations and ioctl callbacks.
+The core also provides pseudo-bus functionality for platform driver
+registration, discovery and inter platform driver ioctl calls.
core infrastructure.
Will update.
The interfaces to the infrastructure are not in include/linux/fpga/
Maybe this needs to change.
Were you thinking of moving XRT infrastructure header files from
drivers/fpga/xrt/include to include/linux/fpga?
quoted
+
+.. note::
+ See code in ``include/xleaf.h``
+
+
+xmgmt.ko
+--------
+
+The xmgmt driver is a PCIe device driver driving MPF found on
+Xilinx's Alveo PCIE device. It consists of one *root* driver, one or
+more *group* drivers and one or more *xleaf* drivers. The root and
+MPF specific xleaf drivers are in xmgmt.ko. The group driver and other xleaf
drivers are in xrt-lib.ko.
I am not sure if *.ko is correct, these will also be intree.
quoted
+
+The instantiation of specific group driver or xleaf driver is
+completely data
of a specific
quoted
+driven based on meta data (mostly in device tree format) found
+through VSEC
mostly ? what is the deviation from device tree ?
quoted
+capability and inside firmware files, such as platform xsabin or user xclbin
file.
quoted
+The root driver manages life cycle of multiple group drivers, which,
+in turn,
the life cycle
quoted
+manages multiple xleaf drivers. This allows a single set of driver
+code to support
set of drivers
drop 'code'
Will update
quoted
+all kinds of subsystems exposed by different shells. The difference
+among all these subsystems will be handled in xleaf drivers with root
+and group drivers being part of the infrastructure and provide common
+services for all leaves found on all platforms.
+
+The driver object model looks like the following::
+
+ +-----------+
+ | xroot |
+ +-----+-----+
+ |
+ +-----------+-----------+
+ | |
+ v v
+ +-----------+ +-----------+
+ | group | ... | group |
+ +-----+-----+ +------+----+
+ | |
+ | |
+ +-----+----+ +-----+----+
+ | | | |
+ v v v v
+ +-------+ +-------+ +-------+ +-------+
+ | xleaf |..| xleaf | | xleaf |..| xleaf |
+ +-------+ +-------+ +-------+ +-------+
+
+As an example for Xilinx Alveo U50 before user xclbin download, the
+tree looks like the following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+
+ | | |
+ v v v
+ +--------+ +--------+ +--------+
+ | group0 | | group1 | | group2 |
+ +----+---+ +----+---+ +---+----+
+ | | |
+ | | |
+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
+ | | | | | | | | |
+ v v | v v | v v |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | |
+ | AXI-GATE0 | |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | +---------+ | +------+ +-----------+ |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
+ +---------+ | +------+ +-----------+
+ | +-------+
+ +->| CALIB |
+ +-------+
+
Nice ascii art!
quoted
+After an xclbin is download, group3 will be added and the tree looks
+like the
+following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+-----------------+
+ | | | |
+ v v v |
+ +--------+ +--------+ +--------+ |
+ | group0 | | group1 | | group2 | |
+ +----+---+ +----+---+ +---+----+ |
+ | | | |
+ | | | |
+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
+ | | | | | | | | | |
+ v v | v v | v v | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
|
quoted
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | +---------+ | +------+ +-----------+ | |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
+ +---------+ | +------+ +-----------+ |
+ | +-------+ |
+ +->| CALIB | |
+ +-------+ |
+ +---+----+ |
+ | group3 |<--------------------------------------------+
+ +--------+
+ |
+ |
+ +-------+--------+---+--+--------+------+-------+
+ | | | | | | |
+ v | v | v | v
+ +--------+ | +--------+ | +--------+ | +-----+
+ | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
+ +--------+ v +--------+ v +--------+ v +-----+
+ +-------------+ +-------------+ +-------------+
+ | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
+ +-------------+ +-------------+ +-------------+
+
+
+xmgmt-root
+^^^^^^^^^^
+
+The xmgmt-root driver is a PCIe device driver attached to MPF. It's
+part of the infrastructure of the MPF driver and resides in xmgmt.ko.
+This driver
+
+* manages one or more group drivers
+* provides access to functionalities that requires pci_dev, such as
+PCIE config
+ space access, to other xleaf drivers through root calls
+* together with group driver, facilities event callbacks for other
+xleaf drivers
+* together with group driver, facilities inter-leaf driver calls for
+other xleaf
Maybe drop 'together with group driver'
Will update
quoted
+ drivers
+
+When root driver starts, it will explicitly create an initial group
+instance, which contains xleaf drivers that will trigger the creation
+of other group instances. The root driver will wait for all group and
+leaves to be created before it returns from it's probe routine and
+claim success of the initialization of the entire xmgmt driver.
What happens if there a failure in one leaf ? Does the whole board go down ?
quoted
+
+.. note::
+ See code in ``lib/xroot.c`` and ``mgmt/root.c``
+
+
+group
+^^^^^
+
+The group driver is a platform device driver whose life cycle is
+managed by
Maybe call this a 'pseudo device'
Will update
quoted
+root and does not have real IO mem or IRQ resources. It's part of the
+infrastructure of the MPF driver and resides in xrt-lib.ko. This
+driver
+
+* manages one or more xleaf drivers so that multiple leaves can be
+managed as a
+ group
can drop 'so that multiple leaves can be managed as a group' to me, this is the
same as 'one or more'
Will do
quoted
+* provides access to root from leaves, so that root calls, event
+notifications
+ and inter-leaf calls can happen
+
+In xmgmt, an initial group driver instance will be created by root,
+which
by the root
quoted
+contains leaves that will trigger group instances to be created to
+manage groups of leaves found on different partitions on hardware,
+such as VSEC, Shell, and User.
+
+Every *fpga_region* has a group object associated with it. The group
+is created when xclbin image is loaded on the fpga_region. The
+existing group is destroyed when a new xclbin image is loaded. The
+fpga_region persists across xclbin downloads.
The connection of a 'group' node to a fpga region region is fairly important,
maybe move this section earlier. 'group' as an fpganess thing would be kept in
fpga/ subsystem.
Will update
quoted
+
+.. note::
+ See code in ``lib/group.c``
+
+
+xleaf
+^^^^^
+
+The xleaf driver is a platform device driver whose life cycle is
+managed by a group driver and may or may not have real IO mem or IRQ
+resources. They are the real meat of xmgmt and contains platform
+specific code to Shell and User found on a MPF.
+
Maybe a split is pseudo device leaves, those without real IO mem, stay in
fpga/ others go ?
This goes back to the earlier question of what minimal set of platform drivers
should stay in fpga subsystem. There are some like bridge or configuration
engine (also called icap) which have their own IO mem but do not have a life
outside of fpga subsystem.
quoted
+A xleaf driver may not have real hardware resources when it merely
+acts as a driver that manages certain in-memory states for xmgmt.
+These in-memory states could be shared by multiple other leaves.
+
This implies locking and some message passing.
quoted
+Leaf drivers assigned to specific hardware resources drive specific
+subsystem in
drive a specific
quoted
+the device. To manipulate the subsystem or carry out a task, a xleaf
+driver may ask help from root via root calls and/or from other leaves via
inter-leaf calls.
quoted
+
+A xleaf can also broadcast events through infrastructure code for
+other leaves to process. It can also receive event notification from
+infrastructure about certain events, such as post-creation or pre-exit of a
particular xleaf.
I would like to see some examples of how the inter node communications work.
Would update to show an example.
quoted
+
+.. note::
+ See code in ``lib/xleaf/*.c``
+
+
+FPGA Manager Interaction
+========================
+
+fpga_manager
+------------
+
+An instance of fpga_manager is created by xmgmt_main and is used for
+xclbin
for the xclbin
quoted
+image download. fpga_manager requires the full xclbin image before it
+can start programming the FPGA configuration engine via ICAP platform
driver.
via the ICAP
what is ICAP ?
Will update. ICAP stands for Internal Configuration Access Port used for configuring
the fpga.
quoted
+
+fpga_region
+-----------
+
+For every interface exposed by currently loaded xclbin/xsabin in the
+*parent*
by the currently
quoted
+fpga_region a new instance of fpga_region is created like a *child* region.
fpga_region,
quoted
+The device tree of the *parent* fpga_region defines the resources for
+a new instance of fpga_bridge which isolates the parent from
and isolates
quoted
+child fpga_region. This new instance of fpga_bridge will be used when
+a xclbin image is loaded on the child fpga_region. After the xclbin
+image is downloaded to the fpga_region, an instance of group is
+created for the fpga_region using the device tree obtained as part of
+xclbin. If this device
of the xclbin
quoted
+tree defines any child interfaces then it can trigger the creation of
interfaces, then
quoted
+fpga_bridge and fpga_region for the next region in the chain.
a fpga_bridge and a fpga_region
quoted
+
+fpga_bridge
+-----------
+
+Like fpga_region, matching fpga_bridge is also created by walking the
+device
Like the fpga_region, a matchin
quoted
+tree of the parent group.
+
+Driver Interfaces
+=================
+
+xmgmt Driver Ioctls
+-------------------
+
+Ioctls exposed by xmgmt driver to user space are enumerated in the
+following
+table:
+
+== ===================== ============================
==========================
quoted
+# Functionality ioctl request code data format
+== ===================== ============================
This data format is described below, maybe swap this section with that so
folks will know what xmgmnt_ioc_bitstream_axlf is before this section.
Will update.
quoted
+
+User xclbin can be downloaded by using xbmgmt tool from XRT open
+source suite. See
A user xclbin
using the xbmgmt
from the XRT
Will update
quoted
+example usage below::
+
+ xbmgmt partition --program --path
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xc
+ lbin --force
+
+xmgmt Driver Sysfs
+------------------
+
+xmgmt driver exposes a rich set of sysfs interfaces. Subsystem
+platform drivers export sysfs node for every platform instance.
+
+Every partition also exports its UUIDs. See below for examples::
+
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
+
+
+hwmon
+-----
+
+xmgmt driver exposes standard hwmon interface to report voltage,
+current, temperature, power, etc. These can easily be viewed using
+*sensors* command line utility.
+
+Alveo Platform Overview
+=======================
+
+Alveo platforms are architected as two physical FPGA partitions:
+*Shell* and *User*. The Shell provides basic infrastructure for the
+Alveo platform like PCIe connectivity, board management, Dynamic
+Function Exchange (DFX), sensors, clocking, reset, and security. User
+partition contains user compiled FPGA
the user compiled
quoted
+binary which is loaded by a process called DFX also known as partial
+reconfiguration.
+
+Physical partitions require strict HW compatibility with each other
+for DFX to work properly.
swap order
For DFX to work properly physical partitions ..
Will update
quoted
Every physical partition has two interface UUIDs: *parent* UUID
+and *child* UUID. For simple single stage platforms, Shell → User
+forms parent child relationship. For complex two stage platforms,
+Base → Shell → User forms the parent child relationship chain.
this bit is confusing. is this related to uuid?
quoted
+
+.. note::
+ Partition compatibility matching is key design component of Alveo
platforms
quoted
+ and XRT. Partitions have child and parent relationship. A loaded
+partition
have a child
quoted
+ exposes child partition UUID to advertise its compatibility
+ requirement for
the child's
can drop 'for child partition'
Will update
quoted
+ child partition. When loading a child partition the xmgmt
+ management driver
When loading a child partition,
quoted
+ matches parent UUID of the child partition against child UUID exported by
+ the parent. Parent and child partition UUIDs are stored in the *xclbin*
+ (for user) or *xsabin* (for base and shell).
this is confusing, is this part of the file image format ?
Maybe save/move till the image layout.
Yes these IDs are stored in xclbin image format. Will move the sections around
as suggested.
quoted
Except for root UUID, VSEC,
+ hardware itself does not know about UUIDs. UUIDs are stored in xsabin
and
quoted
+ xclbin.
This is confusing too, not sure how to untangle.
Will reword.
quoted
+
+
+The physical partitions and their loading is illustrated below::
+
+ SHELL USER
+ +-----------+ +-------------------+
+ | | | |
+ | VSEC UUID | CHILD PARENT | LOGIC UUID |
+ | o------->|<--------o |
+ | | UUID UUID | |
+ +-----+-----+ +--------+----------+
+ | |
+ . .
+ | |
+ +---+---+ +------+--------+
+ | POR | | USER COMPILED |
+ | FLASH | | XCLBIN |
+ +-------+ +---------------+
+
+
+Loading Sequence
+----------------
+
+The Shell partition is loaded from flash at system boot time. It
+establishes the PCIe link and exposes two physical functions to the
+BIOS. After OS boot, xmgmt
the OS boots, the xmgmt
quoted
+driver attaches to PCIe physical function 0 exposed by the Shell and
+then looks for VSEC in PCIe extended configuration space. Using VSEC
+it determines the logic
the PCIe
The driver uses VSEC to determine the UUID of Shell. The UUID is also used to
load a matching ...
Will update
quoted
+UUID of Shell and uses the UUID to load matching *xsabin* file from
+Linux firmware directory. The xsabin file contains metadata to
+discover peripherals that are part of Shell and firmware(s) for any
embedded soft processors in Shell.
the firmware needed for any ...
Will update
quoted
+
+The Shell exports child interface UUID which is used for
+compatibility check when
export a child
for a compatibility check
Will update
quoted
+loading user compiled xclbin over the User partition as part of DFX.
+When a user requests loading of a specific xclbin the xmgmt
+management driver reads the parent
xclbin, the
quoted
+interface UUID specified in the xclbin and matches it with child
+interface UUID exported by Shell to determine if xclbin is compatible
+with the Shell. If match fails loading of xclbin is denied.
+
+xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl command.
+When loading xclbin, xmgmt driver performs the following *logical*
operations:
quoted
+
+1. Copy xclbin from user to kernel memory 2. Sanity check the xclbin
+contents 3. Isolate the User partition 4. Download the bitstream
+using the FPGA config engine (ICAP) 5. De-isolate the User partition
+6. Program the clocks (ClockWiz) driving the User partition
+
+xsabin
+------
+
+Each Alveo platform comes packaged with its own xsabin. The xsabin is
+trusted
is a trusted
quoted
+component of the platform. For format details refer to
+:ref:`xsabin_xclbin_container_format`
+below. xsabin contains basic information like UUIDs, platform name
+and metadata in the form of device tree. See :ref:`device_tree_usage`
+tool set from Xilinx. The xclbin contains sections describing user
+compiled acceleration engines/kernels, memory subsystems, clocking
+information etc. It also contains bitstream for the user partition,
+UUIDs, platform name, etc. xclbin uses
bitstreams
quoted
+the same container format as xsabin which is described below.
+
+
+.. _xsabin_xclbin_container_format:
+
+xsabin/xclbin Container Format
+------------------------------
+
+xclbin/xsabin is ELF-like binary container format. It is structured
+as series of sections. There is a file header followed by several
+section headers which is followed by sections. A section header
+points to an actual section. There is an optional signature at the end. The
format is defined by header file ``xclbin.h``.
quoted
+The following figure illustrates a typical xclbin::
+
+
+ +---------------------+
+ | |
+ | HEADER |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | SIGNATURE |
+ | (OPTIONAL) |
+ +---------------------+
+
+
+xclbin/xsabin files can be packaged, un-packaged and inspected using
+XRT utility called **xclbinutil**. xclbinutil is part of XRT open
+source software stack. The source code for xclbinutil can be found at
+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbi
+nutil
+
Works, but maybe the location of a manpage or doc would be better.
quoted
+For example to enumerate the contents of a xclbin/xsabin use the
+*--info* switch as shown below::
+
+
+ xclbinutil --info --input
+ /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
+ xclbinutil --info --input
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsab
+ in
+
+
+.. _device_tree_usage:
+
+Device Tree Usage
+-----------------
+
+As mentioned previously xsabin stores metadata which advertise HW
+subsystems present in a partition. The metadata is stored in device tree
format with well defined schema.
quoted
+XRT management driver uses this information to bind *platform
+drivers* to the subsystem instantiations. The platform drivers are
+found in **xrt-lib.ko** kernel module defined later.
+
+Logic UUID
+^^^^^^^^^^
+A partition is identified uniquely through ``logic_uuid`` property::
+
+ /dts-v1/;
+ / {
+ logic_uuid = "0123456789abcdef0123456789abcdef";
+ ...
+ }
+
+Schema Version
+^^^^^^^^^^^^^^
+Schema version is defined through ``schema_version`` node. And it
+contains ``major`` and ``minor`` properties as below::
+
+ /dts-v1/;
+ / {
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+ ...
+ }
+
+Partition UUIDs
+^^^^^^^^^^^^^^^
+As said earlier, each partition may have parent and child UUIDs.
+These UUIDs are defined by ``interfaces`` node and ``interface_uuid``
property::
quoted
+
+ /dts-v1/;
+ / {
+ interfaces {
+ @0 {
+ interface_uuid = "0123456789abcdef0123456789abcdef";
+ };
+ @1 {
+ interface_uuid = "fedcba9876543210fedcba9876543210";
+ };
+ ...
+ };
+ ...
+ }
+
+
+Subsystem Instantiations
+^^^^^^^^^^^^^^^^^^^^^^^^
+Subsystem instantiations are captured as children of
+``addressable_endpoints``
+node::
+
+ /dts-v1/;
+ / {
+ addressable_endpoints {
+ abc {
+ ...
+ };
+ def {
+ ...
+ };
+ ...
+ }
+ }
+
+Subnode 'abc' and 'def' are the name of subsystem nodes
+
+Subsystem Node
+^^^^^^^^^^^^^^
+Each subsystem node and its properties define a hardware instance::
+
+
+ addressable_endpoints {
+ abc {
+ reg = <0xa 0xb>
+ pcie_physical_function = <0x0>;
+ pcie_bar_mapping = <0x2>;
+ compatible = "abc def";
+ firmware {
+ firmware_product_name = "abc"
+ firmware_branch_name = "def"
+ firmware_version_major = <1>
+ firmware_version_minor = <2>
+ };
+ }
+ ...
+ }
+
+:reg:
+ Property defines address range. '<0xa 0xb>' is BAR offset and length
+pair, both are 64-bit integer.
+:pcie_physical_function:
+ Property specifies which PCIe physical function the subsystem node resides.
+:pcie_bar_mapping:
+ Property specifies which PCIe BAR the subsystem node resides.
+'<0x2>' is BAR index and it is 0 if this property is not defined.
+:compatible:
+ Property is a list of strings. The first string in the list
+specifies the exact subsystem node. The following strings represent
+other devices that the device is compatible with.
+:firmware:
+ Subnode defines the firmware required by this subsystem node.
+
+Alveo U50 Platform Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+ /dts-v1/;
+
+ /{
+ logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
+
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+
+ interfaces {
+
+ @0 {
+ interface_uuid = "862c7020a250293e32036f19956669e5";
+ };
+ };
+
+ addressable_endpoints {
+
+ ep_blp_rom_00 {
+ reg = <0x00 0x1f04000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
Thanks for the detailed review of the document. I am working on incorporating the
feedback. One outstanding question is about usage of formal bus in XRT and if we
should phase that in as a follow-on. It would also determine if IP drivers should
move to other subsystems.
-Sonal
Hi Moritz,
On 02/21/2021 12:43 PM, Moritz Fischer wrote:
Lizhi,
On Wed, Feb 17, 2021 at 10:40:01PM -0800, Lizhi Hou wrote:
quoted
Hello,
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
XILINX ALVEO PLATFORM ARCHITECTURE
Alveo PCIe FPGA based platforms have a static *shell* partition and a partial
re-configurable *user* partition. The shell partition is automatically loaded from
flash when host is booted and PCIe is enumerated by BIOS. Shell cannot be changed
till the next cold reboot. The shell exposes two PCIe physical functions:
1. management physical function
2. user physical function
The patch series includes Documentation/xrt.rst which describes Alveo platform,
XRT driver architecture and deployment model in more detail.
Users compile their high level design in C/C++/OpenCL or RTL into FPGA image using
Vitis https://www.xilinx.com/products/design-tools/vitis/vitis-platform.html
tools. The compiled image is packaged as xclbin which contains partial bitstream
for the user partition and necessary metadata. Users can dynamically swap the image
running on the user partition in order to switch between different workloads by
loading different xclbins.
XRT DRIVERS FOR XILINX ALVEO
XRT Linux kernel driver *xmgmt* binds to management physical function of Alveo
platform. The modular driver framework is organized into several platform drivers
which primarily handle the following functionality:
1. Loading firmware container also called xsabin at driver attach time
2. Loading of user compiled xclbin with FPGA Manager integration
3. Clock scaling of image running on user partition
4. In-band sensors: temp, voltage, power, etc.
5. Device reset and rescan
The platform drivers are packaged into *xrt-lib* helper module with well
defined interfaces. The module provides a pseudo-bus implementation for the
platform drivers. More details on the driver model can be found in
Documentation/xrt.rst.
User physical function driver is not included in this patch series.
LIBFDT REQUIREMENT
XRT driver infrastructure uses Device Tree as a metadata format to discover
HW subsystems in the Alveo PCIe device. The Device Tree schema used by XRT
is documented in Documentation/xrt.rst. Unlike previous V1 and V2 version
of patch series, V3 version does not require export of libfdt symbols.
TESTING AND VALIDATION
xmgmt driver can be tested with full XRT open source stack which includes user
space libraries, board utilities and (out of tree) first generation user physical
function driver xocl. XRT open source runtime stack is available at
https://github.com/Xilinx/XRT
Complete documentation for XRT open source stack including sections on Alveo/XRT
security and platform architecture can be found here:
https://xilinx.github.io/XRT/master/html/index.htmlhttps://xilinx.github.io/XRT/master/html/security.htmlhttps://xilinx.github.io/XRT/master/html/platforms_partitions.html
Changes since v2:
- Streamlined the driver framework into *xleaf*, *group* and *xroot*
- Updated documentation to show the driver model with examples
- Addressed kernel test robot errors
- Added a selftest for basic driver framework
- Documented device tree schema
- Removed need to export libfdt symbols
Changes since v1:
- Updated the driver to use fpga_region and fpga_bridge for FPGA
programming
- Dropped platform drivers not related to PR programming to focus on XRT
core framework
- Updated Documentation/fpga/xrt.rst with information on XRT core framework
- Addressed checkpatch issues
- Dropped xrt- prefix from some header files
For reference V1 version of patch series can be found here:
https://lore.kernel.org/lkml/20201217075046.28553-1-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-2-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-3-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-4-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-5-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-6-sonals@xilinx.com/https://lore.kernel.org/lkml/20201217075046.28553-7-sonals@xilinx.com/
Lizhi Hou (18):
Documentation: fpga: Add a document describing XRT Alveo drivers
fpga: xrt: driver metadata helper functions
fpga: xrt: xclbin file helper functions
fpga: xrt: xrt-lib platform driver manager
fpga: xrt: group platform driver
fpga: xrt: platform driver infrastructure
fpga: xrt: management physical function driver (root)
fpga: xrt: main platform driver for management function device
fpga: xrt: fpga-mgr and region implementation for xclbin download
fpga: xrt: VSEC platform driver
fpga: xrt: UCS platform driver
fpga: xrt: ICAP platform driver
fpga: xrt: devctl platform driver
fpga: xrt: clock platform driver
fpga: xrt: clock frequence counter platform driver
fpga: xrt: DDR calibration platform driver
fpga: xrt: partition isolation platform driver
fpga: xrt: Kconfig and Makefile updates for XRT drivers
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842 ++++++++++++++++++++++
MAINTAINERS | 11 +
drivers/Makefile | 1 +
drivers/fpga/Kconfig | 2 +
drivers/fpga/Makefile | 4 +
drivers/fpga/xrt/Kconfig | 8 +
drivers/fpga/xrt/include/events.h | 48 ++
drivers/fpga/xrt/include/group.h | 27 +
drivers/fpga/xrt/include/metadata.h | 229 ++++++
drivers/fpga/xrt/include/subdev_id.h | 43 ++
drivers/fpga/xrt/include/xclbin-helper.h | 52 ++
drivers/fpga/xrt/include/xleaf.h | 276 +++++++
drivers/fpga/xrt/include/xleaf/axigate.h | 25 +
drivers/fpga/xrt/include/xleaf/calib.h | 30 +
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +
drivers/fpga/xrt/include/xleaf/clock.h | 31 +
drivers/fpga/xrt/include/xleaf/devctl.h | 43 ++
drivers/fpga/xrt/include/xleaf/icap.h | 29 +
drivers/fpga/xrt/include/xleaf/ucs.h | 24 +
drivers/fpga/xrt/include/xmgmt-main.h | 37 +
drivers/fpga/xrt/include/xroot.h | 114 +++
drivers/fpga/xrt/lib/Kconfig | 16 +
drivers/fpga/xrt/lib/Makefile | 30 +
drivers/fpga/xrt/lib/cdev.c | 231 ++++++
drivers/fpga/xrt/lib/group.c | 265 +++++++
drivers/fpga/xrt/lib/main.c | 274 +++++++
drivers/fpga/xrt/lib/main.h | 17 +
drivers/fpga/xrt/lib/subdev.c | 871 +++++++++++++++++++++++
drivers/fpga/xrt/lib/subdev_pool.h | 53 ++
drivers/fpga/xrt/lib/xclbin.c | 394 ++++++++++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 ++++++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 ++++++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 ++++++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 +++++++++
drivers/fpga/xrt/lib/xleaf/ucs.c | 235 ++++++
drivers/fpga/xrt/lib/xleaf/vsec.c | 359 ++++++++++
drivers/fpga/xrt/lib/xroot.c | 598 ++++++++++++++++
drivers/fpga/xrt/metadata/Kconfig | 12 +
drivers/fpga/xrt/metadata/Makefile | 16 +
drivers/fpga/xrt/metadata/metadata.c | 524 ++++++++++++++
drivers/fpga/xrt/mgmt/Kconfig | 15 +
drivers/fpga/xrt/mgmt/Makefile | 19 +
drivers/fpga/xrt/mgmt/fmgr-drv.c | 187 +++++
drivers/fpga/xrt/mgmt/fmgr.h | 28 +
drivers/fpga/xrt/mgmt/main-impl.h | 37 +
drivers/fpga/xrt/mgmt/main-region.c | 471 ++++++++++++
drivers/fpga/xrt/mgmt/main.c | 693 ++++++++++++++++++
drivers/fpga/xrt/mgmt/root.c | 342 +++++++++
include/uapi/linux/xrt/xclbin.h | 408 +++++++++++
include/uapi/linux/xrt/xmgmt-ioctl.h | 46 ++
53 files changed, 9957 insertions(+)
create mode 100644 Documentation/fpga/xrt.rst
create mode 100644 drivers/fpga/xrt/Kconfig
create mode 100644 drivers/fpga/xrt/include/events.h
create mode 100644 drivers/fpga/xrt/include/group.h
create mode 100644 drivers/fpga/xrt/include/metadata.h
create mode 100644 drivers/fpga/xrt/include/subdev_id.h
create mode 100644 drivers/fpga/xrt/include/xclbin-helper.h
create mode 100644 drivers/fpga/xrt/include/xleaf.h
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/include/xleaf/ucs.h
create mode 100644 drivers/fpga/xrt/include/xmgmt-main.h
create mode 100644 drivers/fpga/xrt/include/xroot.h
create mode 100644 drivers/fpga/xrt/lib/Kconfig
create mode 100644 drivers/fpga/xrt/lib/Makefile
create mode 100644 drivers/fpga/xrt/lib/cdev.c
create mode 100644 drivers/fpga/xrt/lib/group.c
create mode 100644 drivers/fpga/xrt/lib/main.c
create mode 100644 drivers/fpga/xrt/lib/main.h
create mode 100644 drivers/fpga/xrt/lib/subdev.c
create mode 100644 drivers/fpga/xrt/lib/subdev_pool.h
create mode 100644 drivers/fpga/xrt/lib/xclbin.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/ucs.c
create mode 100644 drivers/fpga/xrt/lib/xleaf/vsec.c
create mode 100644 drivers/fpga/xrt/lib/xroot.c
create mode 100644 drivers/fpga/xrt/metadata/Kconfig
create mode 100644 drivers/fpga/xrt/metadata/Makefile
create mode 100644 drivers/fpga/xrt/metadata/metadata.c
create mode 100644 drivers/fpga/xrt/mgmt/Kconfig
create mode 100644 drivers/fpga/xrt/mgmt/Makefile
create mode 100644 drivers/fpga/xrt/mgmt/fmgr-drv.c
create mode 100644 drivers/fpga/xrt/mgmt/fmgr.h
create mode 100644 drivers/fpga/xrt/mgmt/main-impl.h
create mode 100644 drivers/fpga/xrt/mgmt/main-region.c
create mode 100644 drivers/fpga/xrt/mgmt/main.c
create mode 100644 drivers/fpga/xrt/mgmt/root.c
create mode 100644 include/uapi/linux/xrt/xclbin.h
create mode 100644 include/uapi/linux/xrt/xmgmt-ioctl.h
--
2.18.4
Please fix the indents all across this patchset. Doesn't checkpatch with
--strict complain about this?
checkpatch --strict did not complain. And we will fix this.
Also more generally this looks like it should be a bus. Look at DFL for
reference.
Tom asked the same question in 01/18 and Sonal replied. We may discuss
with that thread.
Thanks,
Lizhi
From: Tom Rix <trix@redhat.com> Date: 2021-03-02 01:40:24
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Add VSEC driver. VSEC is a hardware function discovered by walking
PCI Express configure space. A platform device node will be created
for it. VSEC provides board logic UUID and few offset of other hardware
functions.
Is this vsec walking infra or is a general find a list of mmio regions that need to be mapped in and do the mapping in as a set of platform drivers ?
Hi Moritz,
On 2/21/21 12:39 PM, Moritz Fischer wrote:
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
Lizhi,
On Wed, Feb 17, 2021 at 10:40:05PM -0800, Lizhi Hou wrote:
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
On 2/26/21 1:23 PM, Lizhi Hou wrote:
quoted
Hi Tom,
snip
quoted
quoted
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
This data structure is shared with other tools. And the structure is well defined with reasonable alignment. It is compatible with all compilers we have tested. So pragma pack is not necessary.
You can not have possibly tested all the configurations since the kernel supports many arches and compilers.
If the tested existing alignment is ok, pragma pack should be a noop on your tested configurations.
And help cover the untested configurations.
From: Moritz Fischer <mdf@kernel.org> Date: 2021-03-02 17:52:30
On Mon, Mar 01, 2021 at 04:25:37PM -0800, Lizhi Hou wrote:
Hi Tom,
On 02/28/2021 08:54 AM, Tom Rix wrote:
quoted
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
On 2/26/21 1:23 PM, Lizhi Hou wrote:
quoted
Hi Tom,
snip
quoted
quoted
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
This data structure is shared with other tools. And the structure is well defined with reasonable alignment. It is compatible with all compilers we have tested. So pragma pack is not necessary.
You can not have possibly tested all the configurations since the kernel supports many arches and compilers.
If the tested existing alignment is ok, pragma pack should be a noop on your tested configurations.
And help cover the untested configurations.
Got it. I will add pragma pack(1).
Please do not use pragma pack(), add __packed to the structs in
question.
- Moritz
+
+ if (id != XRT_SUBDEV_CLOCK)
+ return;
+
+ leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_CLOCK, instance);
+ if (!leaf) {
+ xrt_err(pdev, "does not get clock subdev");
+ return;
+ }
+
+ xleaf_ioctl(leaf, XRT_CLOCK_VERIFY, NULL);
+ xleaf_put_leaf(pdev, leaf);
+}
+
+static void ucs_check(struct xrt_ucs *ucs, bool *latched)
+{
checking but not returning status, change to returning int.
this function is called but xrt_ucs_leaf_ioctl which does return status.
+ struct ucs_control_status_ch1 *ucs_status_ch1;
+ u32 status;
+
+ mutex_lock(&ucs->ucs_lock);
+ status = reg_rd(ucs, CHANNEL1_OFFSET);
+ ucs_status_ch1 = (struct ucs_control_status_ch1 *)&status;
+ if (ucs_status_ch1->shutdown_clocks_latched) {
+ UCS_ERR(ucs,
+ "Critical temperature or power event, kernel clocks have been stopped.");
+ UCS_ERR(ucs,
+ "run 'xbutil valiate -q' to continue. See AR 73398 for more details.");
This error message does not seem like it would be useful, please review.
Hi Moritz,
On 02/21/2021 12:24 PM, Moritz Fischer wrote:
On Wed, Feb 17, 2021 at 10:40:13PM -0800, Lizhi Hou wrote:
quoted
Add ICAP driver. ICAP is a hardware function discovered by walking
firmware metadata. A platform device node will be created for it.
FPGA bitstream is written to hardware through ICAP.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/icap.h | 29 +++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 ++++++++++++++++++++++++++
2 files changed, 346 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
Do we really need two layers of indirection here? What's wrong with
dev_{info,dbg,...} ?
In case that we would change the massage 'fmt' in the future we can
change it at one place. And it does not expose any interface or
introduce performance issue. Could we just keep these Macros?
From: Joe Perches <joe@perches.com> Date: 2021-03-03 12:58:06
On Sun, 2021-02-21 at 12:43 -0800, Moritz Fischer wrote:
On Wed, Feb 17, 2021 at 10:40:01PM -0800, Lizhi Hou wrote:
quoted
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
[]
Please fix the indents all across this patchset. Doesn't checkpatch with
--strict complain about this?
I glanced at a couple bits of these patches and didn't
notice any of what I consider poor indentation style.
What indent is wrong here?
From: Tom Rix <trix@redhat.com> Date: 2021-03-03 18:42:13
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Add ICAP driver. ICAP is a hardware function discovered by walking
What does ICAP stand for ?
quoted hunk
firmware metadata. A platform device node will be created for it.
FPGA bitstream is written to hardware through ICAP.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/icap.h | 29 +++
drivers/fpga/xrt/lib/xleaf/icap.c | 317 ++++++++++++++++++++++++++
2 files changed, 346 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/icap.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/icap.c
+
+failed:
+ mutex_unlock(&icap->icap_lock);
+
+ return err;
+}
+
+/*
+ * Run the following sequence of canned commands to obtain IDCODE of the FPGA
+ */
+static void icap_probe_chip(struct icap *icap)
+{
+ u32 w;
De magic this.
If this is a documented startup sequence, please add a link to the document.
Else add a comment about what you are doing here.
Where possible, convert the hex values to #defines.
Tom
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Not sure if 'main' is a good base name for something going into a lib.
These files are the main file for xrt-lib.ko. I'm not sure what name you
prefer, but I've changed them to lib-drv.[c|h]. Let me know if you don't
like them...
+ * struct platform_driver, which contains it's binding name and driver/file ops.
+ * We also map it to the endpoint name in DTB as well, if it's different
+ * than the driver's binding name.
+ */
+struct xrt_drv_map {
+ struct list_head list;
+ enum xrt_subdev_id id;
+ struct platform_driver *drv;
+ struct xrt_subdev_endpoints *eps;
+ struct ida ida; /* manage driver instance and char dev minor */
+};
+
+static DEFINE_MUTEX(xrt_lib_lock); /* global lock protecting xrt_drv_maps list */
+static LIST_HEAD(xrt_drv_maps);
+struct class *xrt_class;
+
+static inline struct xrt_subdev_drvdata *
+xrt_drv_map2drvdata(struct xrt_drv_map *map)
+{
+ return (struct xrt_subdev_drvdata *)map->drv->id_table[0].driver_data;
+}
+
+static struct xrt_drv_map *
+xrt_drv_find_map_by_id_nolock(enum xrt_subdev_id id)
+ * driver should only be unregistered when driver module is being unloaded,
+ * which means that the driver should not be used by then.
+ */
+ return map;
+}
+
+static int xrt_drv_register_driver(struct xrt_drv_map *map)
+{
+ struct xrt_subdev_drvdata *drvdata;
+ int rc = 0;
+ const char *drvname = XRT_DRVNAME(map->drv);
+
+ rc = platform_driver_register(map->drv);
+ if (rc) {
+ pr_err("register %s platform driver failed\n", drvname);
+ return rc;
+ }
+
+ drvdata = xrt_drv_map2drvdata(map);
+ if (drvdata) {
+ /* Initialize dev_t for char dev node. */
+ if (xleaf_devnode_enabled(drvdata)) {
+ rc = alloc_chrdev_region(&drvdata->xsd_file_ops.xsf_dev_t, 0,
+ XRT_MAX_DEVICE_NODES, drvname);
+ if (rc) {
+ platform_driver_unregister(map->drv);
+ pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc);
+ return rc;
+ }
+ } else {
+ drvdata->xsd_file_ops.xsf_dev_t = (dev_t)-1;
+ }
+ }
+
+ ida_init(&map->ida);
+
+ pr_info("%s registered successfully\n", drvname);
+
+ return 0;
+}
+
+static void xrt_drv_unregister_driver(struct xrt_drv_map *map)
+{
+ const char *drvname = XRT_DRVNAME(map->drv);
+ struct xrt_subdev_drvdata *drvdata;
+
+ ida_destroy(&map->ida);
+
+ drvdata = xrt_drv_map2drvdata(map);
+ if (drvdata && drvdata->xsd_file_ops.xsf_dev_t != (dev_t)-1) {
+ unregister_chrdev_region(drvdata->xsd_file_ops.xsf_dev_t,
+ XRT_MAX_DEVICE_NODES);
+ }
+
+ platform_driver_unregister(map->drv);
+
+ pr_info("%s unregistered successfully\n", drvname);
+}
+
+int xleaf_register_driver(enum xrt_subdev_id id,
+ struct platform_driver *drv,
+ struct xrt_subdev_endpoints *eps)
+{
+ struct xrt_drv_map *map;
+
+ mutex_lock(&xrt_lib_lock);
Trying to minimize length of lock being held.
Could holding this lock be split or the alloc moved above ?
We don't want to do memory allocation unless the mapping can't be found.
So, finding the mapping and allocating memory for new entry is done as
one atomic operation.
The code path here is far from being in critical path, so holding a lock
here for some longer time should not be a problem. I'd like to keep the
code straightforward and easy to follow by holding the lock from start
to end.
These constructor/destructor calls needs to be more dynamic.
calls are made even if there are no subdevices to go with the id's.
Also this list can not grow. How would a new id be added by a module ?
We do not support dynamically adding drivers/IDs. All drivers needs to
be added statically after thoroughly tested. We do not intend to build
an open infrastructure to support random hardware and driver anyway.
quoted
+static void (*leaf_init_fini_cbs[])(bool) = {
+ group_leaf_init_fini,
+ vsec_leaf_init_fini,
+ devctl_leaf_init_fini,
+ axigate_leaf_init_fini,
+ icap_leaf_init_fini,
+ calib_leaf_init_fini,
+ clkfreq_leaf_init_fini,
+ clock_leaf_init_fini,
+ ucs_leaf_init_fini,
+};
+
+static __init int xrt_lib_init(void)
+{
+ int i;
+
+ xrt_class = class_create(THIS_MODULE, XRT_IPLIB_MODULE_NAME);
+ if (IS_ERR(xrt_class))
+ return PTR_ERR(xrt_class);
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](true);
+ return 0;
+}
+
+static __exit void xrt_lib_fini(void)
+{
+ struct xrt_drv_map *map;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(leaf_init_fini_cbs); i++)
+ leaf_init_fini_cbs[i](false);
+
+ mutex_lock(&xrt_lib_lock);
+
+ while (!list_empty(&xrt_drv_maps)) {
+ map = list_first_entry_or_null(&xrt_drv_maps, struct xrt_drv_map, list);
+ pr_err("Unloading module with %s still registered\n", XRT_DRVNAME(map->drv));
+ list_del(&map->list);
+ mutex_unlock(&xrt_lib_lock);
+ xrt_drv_unregister_driver(map);
+ vfree(map);
+ mutex_lock(&xrt_lib_lock);
+ }
+
+ mutex_unlock(&xrt_lib_lock);
+
+ class_destroy(xrt_class);
+}
+
+module_init(xrt_lib_init);
+module_exit(xrt_lib_fini);
+
+MODULE_VERSION(XRT_IPLIB_MODULE_VERSION);
+MODULE_AUTHOR("XRT Team [off-list ref]");
+MODULE_DESCRIPTION("Xilinx Alveo IP Lib driver");
+MODULE_LICENSE("GPL v2");
To be self contained, the header defining enum xrt_subdev_id should be included.
This is subdev_id.h which comes in with patch 6
A dependency on a future patch breaks bisectablity.
It may make sense to collect these small headers into a single large header for the ip infra lib and bring them all in this patch.
Yes, we will reconsider where to put these headers in next patch set.
Thanks,
Max
From: Moritz Fischer <mdf@kernel.org> Date: 2021-03-04 00:16:57
On Tue, Mar 02, 2021 at 10:49:43PM -0800, Joe Perches wrote:
On Sun, 2021-02-21 at 12:43 -0800, Moritz Fischer wrote:
quoted
On Wed, Feb 17, 2021 at 10:40:01PM -0800, Lizhi Hou wrote:
quoted
This is V3 of patch series which adds management physical function driver for Xilinx
Alveo PCIe accelerator cards, https://www.xilinx.com/products/boards-and-kits/alveo.html
This driver is part of Xilinx Runtime (XRT) open source stack.
[]
quoted
Please fix the indents all across this patchset. Doesn't checkpatch with
--strict complain about this?
I glanced at a couple bits of these patches and didn't
notice any of what I consider poor indentation style.
What indent is wrong here?
Maybe I dreamed it, or confused it with the CamelCase issues instead?
Sorry for the noise in that case,
- Moritz
From: Tom Rix <trix@redhat.com> Date: 2021-03-04 13:42:26
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted hunk
Add devctl driver. devctl is a type of hardware function which only has
few registers to read or write. They are discovered by walking firmware
metadata. A platform device node will be created for them.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/devctl.h | 43 +++++
drivers/fpga/xrt/lib/xleaf/devctl.c | 206 ++++++++++++++++++++++++
2 files changed, 249 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/devctl.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/devctl.c
This setting of r_data and continuing is strange, add a comment.
or if you intend to do a pair of operations, do the pair within the if block and remove the strangeness.
Thanks for pointing it out. This is poor implementation. I will
re-implement the loop and add comment.
+ put_device(&base_re->dev);
+
+ xmgmt_region_cleanup(base_re);
+ xmgmt_destroy_region(base_re);
+ }
+}
+
+/*
+ * Program a given region with given xclbin image. Bring up the subdevs and the
info will be freed outside by xmgmt_destroy_region().
quoted
+
+ /* free bridges to allow reprogram */
+ if (re->get_bridges)
+ fpga_bridges_put(&re->bridge_list);
+
+ /*
+ * Next bringup the subdevs for this region which will be managed by
+ * its own group object.
+ */
+ r_data->grp_inst = xleaf_create_group(pdev, dtb);
+ if (r_data->grp_inst < 0) {
+ xrt_err(pdev, "failed to create group, rc %d",
+ r_data->grp_inst);
+ rc = r_data->grp_inst;
+ return rc;
+ }
+
+ rc = xleaf_wait_for_group_bringup(pdev);
+ if (rc)
+ xrt_err(pdev, "group bringup failed, rc %d", rc);
failed but no error handling, shouldn't the leaves and group be torn down ?
Group is torn down outside by xmgmt_destroy_region().
quoted
+ return rc;
+}
+
+static int xmgmt_get_bridges(struct fpga_region *re)
+{
+ struct xmgmt_region *r_data = re->priv;
+ struct device *dev = &r_data->pdev->dev;
+
+ return fpga_bridge_get_to_list(dev, re->info, &re->bridge_list);
+}
+
+/*
+ * Program/create FPGA regions based on input xclbin file. This is key function
+ * stitching the flow together:
'This is ' .. sentence does not make sense, but is not needed drop it.
Will drop it.
quoted
+ * 1. Identify a matching existing region for this xclbin
+ * 2. Tear down any previous objects for the found region
+ * 3. Program this region with input xclbin
+ * 4. Iterate over this region's interface uuids to determine if it defines any
+ * child region. Create fpga_region for the child region.
+ */
+int xmgmt_process_xclbin(struct platform_device *pdev,
+ struct fpga_manager *fmgr,
+ const struct axlf *xclbin,
+ enum provider_kind kind)
+{
+ struct fpga_region *re, *compat_re = NULL;
+ struct xmgmt_region_match_arg arg;
Hi Moritz,
On 03/02/2021 07:14 AM, Moritz Fischer wrote:
On Mon, Mar 01, 2021 at 04:25:37PM -0800, Lizhi Hou wrote:
quoted
Hi Tom,
On 02/28/2021 08:54 AM, Tom Rix wrote:
quoted
CAUTION: This message has originated from an External Source. Please use proper judgment and caution when opening attachments, clicking links, or responding to this email.
On 2/26/21 1:23 PM, Lizhi Hou wrote:
quoted
Hi Tom,
snip
quoted
quoted
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
This data structure is shared with other tools. And the structure is well defined with reasonable alignment. It is compatible with all compilers we have tested. So pragma pack is not necessary.
You can not have possibly tested all the configurations since the kernel supports many arches and compilers.
If the tested existing alignment is ok, pragma pack should be a noop on your tested configurations.
And help cover the untested configurations.
Got it. I will add pragma pack(1).
Please do not use pragma pack(), add __packed to the structs in
question.
From: Tom Rix <trix@redhat.com> Date: 2021-03-05 15:24:19
why are clock and clkfeq separated ?
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted hunk
Add clock driver. Clock is a hardware function discovered by walking
xclbin metadata. A platform device node will be created for it. Other
part of driver configures clock through clock driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clock.h | 31 ++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++++++++++
2 files changed, 679 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
@@ -0,0 +1,648 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAClockWizardDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*SonalSantan<sonals@xilinx.com>+*DavidZhang<davidzha@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/delay.h>+#include<linux/device.h>+#include<linux/io.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/clock.h"+#include"xleaf/clkfreq.h"++/* CLOCK_MAX_NUM_CLOCKS should be a concept from XCLBIN_ in the future */+#define CLOCK_MAX_NUM_CLOCKS 4+#define OCL_CLKWIZ_STATUS_OFFSET 0x4
OCL_CLKWIZ does not match the name of this file, change to something like XRT_CLOCK
Add VSEC driver. VSEC is a hardware function discovered by walking
PCI Express configure space. A platform device node will be created
for it. VSEC provides board logic UUID and few offset of other hardware
functions.
Is this vsec walking infra or is a general find a list of mmio regions that need to be mapped in and do the mapping in as a set of platform drivers ?
vsec is pointed by PCIe vender-specific capability. And vsec itself
locates on PCI BAR. vsec has a list of minimum IPs (mmio regions)
required for driver to load firmware and communicate with the other pcie
function. After firmware is loaded, xrt will look into the fireware
metadata to get the information of rest IPs.
vsec driver notifies the root driver for the list of minimum IPs been
discovered. Then the root driver will create platform device nodes and
bring up drivers based on vsec's notification.
This is a static list, how would a new type be added to this ?
Because the list will only change when there is major hardware change,
the list will be update manually if hardware introduces a new type.
quoted
+};
+
+struct xrt_vsec {
+ struct platform_device *pdev;
+ void *base;
+ ulong length;
+
+ char *metadata;
+ char uuid[VSEC_UUID_LEN];
+};
+
+static char *type2epname(u32 type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsec_devs); i++) {
+ if (vsec_devs[i].type == type)
+ return (vsec_devs[i].ep_name);
+ }
+
+ return NULL;
+}
+
+static ulong type2size(u32 type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsec_devs); i++) {
+ if (vsec_devs[i].type == type)
+ return (vsec_devs[i].size);
+ }
+
+ return 0;
+}
+
+static char *type2regmap(u32 type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsec_devs); i++) {
+ if (vsec_devs[i].type == type)
+ return (vsec_devs[i].regmap);
+ }
+
+ return NULL;
+}
+
+static int xrt_vsec_add_node(struct xrt_vsec *vsec,
+ void *md_blob, struct xrt_vsec_entry *p_entry)
+{
+ struct xrt_md_endpoint ep;
+ char regmap_ver[64];
+ int ret;
+
+ if (!type2epname(p_entry->type))
+ return -EINVAL;
+
+ /*
+ * VSEC may have more than 1 mailbox instance for the card
+ * which has more than 1 physical function.
+ * This is not supported for now. Assuming only one mailbox
+ */
are multiple uuid types allowed ?
No. And there will be only one uuid in vsec list.
this says assume 1, but logic will recreate 1+
can you check if a mbx ep exists before creating ?
Maybe the comment is confusing. All current Alveo boards only have one
mailbox in vsec list. In theory, there could be more than 1 mailboxes in
the future. And how it will present in vsec list is undetermined.
#defines should have a prefix, maybe XRT_ or XCLBIN_
quoted
+
+enum axlf_section_kind;
+struct axlf;
+
+/**
+ * Bitstream header information as defined by Xilinx tools.
+ * Please note that this struct definition is not owned by the driver.
+ */
+struct hw_icap_bit_header {
File headers usually have fixed length fields like uint32_t
Is this a structure the real header is converted into ?
quoted
+ unsigned int header_length; /* Length of header in 32 bit words */
+ unsigned int bitstream_length; /* Length of bitstream to read in bytes*/
+ unsigned char *design_name; /* Design name get from bitstream */
+ unsigned char *part_name; /* Part name read from bitstream */
+ unsigned char *date; /* Date read from bitstream header */
+ unsigned char *time; /* Bitstream creation time */
+ unsigned int magic_length; /* Length of the magic numbers */
+ unsigned char *version; /* Version string */
+};
+
+const char *xrt_xclbin_kind_to_string(enum axlf_section_kind kind);
Only add decl's that are using in multiple files.
This is only defined in xclbin.c, why does it need to be in the header ?
The return value of this funtion is not always checked, at the least add a dev_err here
quoted
+
+ data = d[*i];
+ (*i)++;
+
+ return data;
+}
+
+static const struct axlf_section_header *
+xrt_xclbin_get_section_hdr(const struct axlf *xclbin,
+ enum axlf_section_kind kind)
+{
+ int i = 0;
+
+ for (i = 0; i < xclbin->m_header.m_numSections; i++) {
+ if (xclbin->m_sections[i].m_sectionKind == kind)
+ return &xclbin->m_sections[i];
+ }
+
+ return NULL;
+}
+
+static int
+xrt_xclbin_check_section_hdr(const struct axlf_section_header *header,
+ u64 xclbin_len)
+{
+ int ret;
+
+ ret = (header->m_sectionOffset + header->m_sectionSize) > xclbin_len ? -EINVAL : 0;
Tristate is harder to read, consider replacing with if()
int ret = 0
if ()
ret =
Why not just:
if (header->m_section_offset + header->m_section_size)
return -EINVAL;
return 0;
Also please fix the camelCase throughout the entire patchset.
This check can be added to the function call..
or the sanity checking added to the earier call to *get_section_hdr
There a number of small functions that can be combined.
quoted
+
+ err = xrt_xclbin_check_section_hdr(mem_header, xclbin_len);
+ if (err)
+ return err;
+
+ *offset = mem_header->m_sectionOffset;
+ *size = mem_header->m_sectionSize;
+
+ return 0;
+}
+
+/* caller should free the allocated memory for **data */
must free
This comment also needs to be with the *.h decl
a general comment
for exported function checking the validity of the inputs in more important.
here you assume **data is valid, really you should check.
quoted
+ if (len)
+ *len = size;
len setting being optional, needs to be in the *.h comment
Instead of allocating new memory and making copies of bits of *data
why not have the points reference data ?
The size operations look like translating big endian data to little endian.
This will break on a big endian host.
quoted
+/* parse bitstream header */
+int xrt_xclbin_parse_bitstream_header(const unsigned char *data,
+ unsigned int size,
+ struct hw_icap_bit_header *header)
+{
+ unsigned int index;
+ unsigned int len;
+ unsigned int tmp;
+ unsigned int i;
+
+ memset(header, 0, sizeof(*header));
+ /* Start Index at start of bitstream */
+ index = 0;
+
+ /* Initialize HeaderLength. If header returned early inidicates
+ * failure.
This side effect should be documented in the *.h comment.
Also the multi line comment is a bit weird, not sure if it is ok
quoted
+ */
+ header->header_length = XHI_BIT_HEADER_FAILURE;
+
+ /* Get "Magic" length */
+ header->magic_length = xhi_data_and_inc(data, &index, size);
+ header->magic_length = (header->magic_length << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Read in "magic" */
+ for (i = 0; i < header->magic_length - 1; i++) {
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (i % 2 == 0 && tmp != XHI_EVEN_MAGIC_BYTE)
if !(i % 2) ...
Will change it.
quoted
quoted
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ if (i % 2 == 1 && tmp != XHI_ODD_MAGIC_BYTE)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+ }
+
+ /* Read null end of magic data. */
+ tmp = xhi_data_and_inc(data, &index, size);
+
+ /* Read 0x01 (short) */
+ tmp = xhi_data_and_inc(data, &index, size);
+ tmp = (tmp << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* Check the "0x01" half word */
+ if (tmp != 0x01)
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Read 'a' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'a')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Design Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for design name and final null character. */
+ header->design_name = vmalloc(len);
+ if (!header->design_name)
+ return -ENOMEM;
+
+ /* Read in Design Name */
+ for (i = 0; i < len; i++)
+ header->design_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->design_name[len - 1] != '\0')
+ return -1;
+
+ header->version = strstr(header->design_name, "Version=") + strlen("Version=");
+
+ /* Read 'b' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'b')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get Part Name length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for part name and final null character. */
+ header->part_name = vmalloc(len);
+ if (!header->part_name)
+ return -ENOMEM;
+
+ /* Read in part name */
+ for (i = 0; i < len; i++)
+ header->part_name[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->part_name[len - 1] != '\0')
+ return -1;
+
+ /* Read 'c' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'c')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get date length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for date and final null character. */
+ header->date = vmalloc(len);
+ if (!header->date)
+ return -ENOMEM;
+
+ /* Read in date name */
+ for (i = 0; i < len; i++)
+ header->date[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->date[len - 1] != '\0')
+ return -1;
generally -EINVAL is more meaningful than -1
quoted
+
+ /* Read 'd' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'd')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get time length */
+ len = xhi_data_and_inc(data, &index, size);
+ len = (len << 8) | xhi_data_and_inc(data, &index, size);
+
+ /* allocate space for time and final null character. */
+ header->time = vmalloc(len);
+ if (!header->time)
+ return -ENOMEM;
+
+ /* Read in time name */
+ for (i = 0; i < len; i++)
+ header->time[i] = xhi_data_and_inc(data, &index, size);
+
+ if (header->time[len - 1] != '\0')
+ return -1;
+
+ /* Read 'e' */
+ tmp = xhi_data_and_inc(data, &index, size);
+ if (tmp != 'e')
+ return -1; /* INVALID_FILE_HEADER_ERROR */
+
+ /* Get byte length of bitstream */
+ header->bitstream_length = xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
+ header->bitstream_length = (header->bitstream_length << 8) |
+ xhi_data_and_inc(data, &index, size);
generally a problem
This is confusing, collect the bytes in a temp[] and construct the header->bitstream_length in on statement.
This is a case where xhi_data_and_inc return is not checked and if it failed could blow up later.
quoted
+
+ header->header_length = index;
index is not a good variable name if it going to be stored as a length.
consider changing it to something like current_length.
Review these includes, some could be convenience includes.
ex/ linux/version.h with no obvious use of version macros.
struct axlf_header {
+ uint64_t m_length; /* Total size of the xclbin file */
.. snip ..
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
As mentioned in an earlier patch, if uuid_t is larger than 16 bytes, axlf_header breaks.
while it is convenient to have this type here, it would be better this access was handled in another way.
Maybe a host specific function.
I also do not see a pragma pack, usually this is set of 1 so the compiler does not shuffle elements, increase size etc.
quoted
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * DOC: Container format for Xilinx FPGA images
+ * The container stores bitstreams, metadata and firmware images.
+ * xclbin/xsabin is ELF-like binary container format. It is structured
is an ELF-like file format. It is a structured
quoted
+ * series of sections. There is a file header followed by several section
+ * headers which is followed by sections. A section header points to an
+ * actual section. There is an optional signature at the end. The
+ * following figure illustrates a typical xclbin:
+ *
+ * +---------------------+
+ * | |
+ * | HEADER |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION HEADER |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | ... |
+ * | |
+ * +---------------------+
+ * | SECTION |
+ * | |
+ * +---------------------+
+ * | SIGNATURE |
+ * | (OPTIONAL) |
+ * +---------------------+
This ascii art is a mixture of tabs and spaces, for someone with tab = 2 spaces, this will look messed up.
convert the tabs to spaces
quoted
+ */
+
+enum XCLBIN_MODE {
+ XCLBIN_FLAT,
generally
all enums used in a file format should be initialized.
This likely should be
XCLBIN_FLAT = 0,
i did not see the version checked earlier, which one is expected ?
quoted
+ uint8_t m_versionMinor; /* Minor Version */
+ uint32_t m_mode; /* XCLBIN_MODE */
+ union {
+ struct {
+ uint64_t m_platformId; /* 64 bit platform ID: */
+ /* vendor-device-subvendor-subdev */
+ uint64_t m_featureId; /* 64 bit feature id */
+ } rom;
+ unsigned char rom_uuid[16]; /* feature ROM UUID for which */
+ /* this xclbin was generated */
+ };
+ unsigned char m_platformVBNV[64]; /* e.g. */
what is VBNV?
quoted
+ /* xilinx:xil-accel-rd-ku115:4ddr-xpr:3.4: null terminated */
+ union {
+ char m_next_axlf[16]; /* Name of next xclbin file */
+ /* in the daisy chain */
+ uuid_t uuid; /* uuid of this xclbin*/
+ };
+ char m_debug_bin[16]; /* Name of binary with debug */
+ /* information */
+ uint32_t m_numSections; /* Number of section headers */
+};
+
+struct axlf {
+ char m_magic[8]; /* Should be "xclbin2\0" */
+ int32_t m_signature_length; /* Length of the signature. */
+ /* -1 indicates no signature */
+ unsigned char reserved[28]; /* Note: Initialized to 0xFFs */
+
+ unsigned char m_keyBlock[256]; /* Signature for validation */
+ /* of binary */
+ uint64_t m_uniqueId; /* axlf's uniqueId, use it to */
+ /* skip redownload etc */
+ struct axlf_header m_header; /* Inline header */
+ struct axlf_section_header m_sections[1]; /* One or more section */
+ /* headers follow */
+};
+
+/* bitstream information */
+struct xlnx_bitstream {
+ uint8_t m_freq[8];
+ char bits[1];
+};
+
+/**** MEMORY TOPOLOGY SECTION ****/
+struct mem_data {
+ uint8_t m_type; /* enum corresponding to mem_type. */
+ uint8_t m_used; /* if 0 this bank is not present */
+ union {
+ uint64_t m_size; /* if mem_type DDR, then size in KB; */
+ uint64_t route_id; /* if streaming then "route_id" */
+ };
+ union {
+ uint64_t m_base_address;/* if DDR then the base address; */
+ uint64_t flow_id; /* if streaming then "flow id" */
+ };
+ unsigned char m_tag[16]; /* DDR: BANK0,1,2,3, has to be null */
+ /* terminated; if streaming then stream0, 1 etc */
+};
+
+struct mem_topology {
+ int32_t m_count; /* Number of mem_data */
+ struct mem_data m_mem_data[1]; /* Should be sorted on mem_type */
+};
+
+/**** CONNECTIVITY SECTION ****/
+/* Connectivity of each argument of Kernel. It will be in terms of argument
This section does not make sense.
Likely you mean some algorithm kernel, rather than the linux kernel.
quoted
+ * index associated. For associating kernel instances with arguments and
+ * banks, start at the connectivity section. Using the m_ip_layout_index
+ * access the ip_data.m_name. Now we can associate this kernel instance
+ * with its original kernel name and get the connectivity as well. This
+ * enables us to form related groups of kernel instances.
+ */
+
+struct connection {
+ int32_t arg_index; /* From 0 to n, may not be contiguous as scalars */
+ /* skipped */
+ int32_t m_ip_layout_index; /* index into the ip_layout section. */
+ /* ip_layout.m_ip_data[index].m_type == IP_KERNEL */
+ int32_t mem_data_index; /* index of the m_mem_data . Flag error is */
+ /* m_used false. */
+};
+
+struct connectivity {
+ int32_t m_count;
+ struct connection m_connection[1];
+};
+
+/**** IP_LAYOUT SECTION ****/
+
+/* IP Kernel */
+#define IP_INT_ENABLE_MASK 0x0001
+#define IP_INTERRUPT_ID_MASK 0x00FE
+#define IP_INTERRUPT_ID_SHIFT 0x1
+
+enum IP_CONTROL {
+ AP_CTRL_HS = 0,
+ AP_CTRL_CHAIN = 1,
+ AP_CTRL_NONE = 2,
+ AP_CTRL_ME = 3,
+ ACCEL_ADAPTER = 4
assigning beyond the first is not necessary unless there are dups or gaps
quoted
+};
+
+#define IP_CONTROL_MASK 0xFF00
+#define IP_CONTROL_SHIFT 0x8
+
+/* IPs on AXI lite - their types, names, and base addresses.*/
+struct ip_data {
+ uint32_t m_type; /* map to IP_TYPE enum */
+ union {
+ uint32_t properties; /* Default: 32-bits to indicate ip */
+ /* specific property. */
+ /* m_type: IP_KERNEL
+ * m_int_enable : Bit - 0x0000_0001;
+ * m_interrupt_id : Bits - 0x0000_00FE;
+ * m_ip_control : Bits = 0x0000_FF00;
+ */
+ struct { /* m_type: IP_MEM_* */
+ uint16_t m_index;
+ uint8_t m_pc_index;
+ uint8_t unused;
+ } indices;
+ };
+ uint64_t m_base_address;
+ uint8_t m_name[64]; /* eg Kernel name corresponding to KERNEL */
+ /* instance, can embed CU name in future. */
+};
+
+struct ip_layout {
+ int32_t m_count;
+ struct ip_data m_ip_data[1]; /* All the ip_data needs to be sorted */
+ /* by m_base_address. */
general
doing the bla[1] for c++ ?
Tom
quoted
+};
+
+/*** Debug IP section layout ****/
+enum DEBUG_IP_TYPE {
+ UNDEFINED = 0,
+ LAPC,
+ ILA,
+ AXI_MM_MONITOR,
+ AXI_TRACE_FUNNEL,
+ AXI_MONITOR_FIFO_LITE,
+ AXI_MONITOR_FIFO_FULL,
+ ACCEL_MONITOR,
+ AXI_STREAM_MONITOR,
+ AXI_STREAM_PROTOCOL_CHECKER,
+ TRACE_S2MM,
+ AXI_DMA,
+ TRACE_S2MM_FULL
+};
+
+struct debug_ip_data {
+ uint8_t m_type; /* type of enum DEBUG_IP_TYPE */
+ uint8_t m_index_lowbyte;
+ uint8_t m_properties;
+ uint8_t m_major;
+ uint8_t m_minor;
+ uint8_t m_index_highbyte;
+ uint8_t m_reserved[2];
+ uint64_t m_base_address;
+ char m_name[128];
+};
+
+struct debug_ip_layout {
+ uint16_t m_count;
+ struct debug_ip_data m_debug_ip_data[1];
+};
+
+/* Supported clock frequency types */
+enum CLOCK_TYPE {
+ CT_UNUSED = 0, /* Initialized value */
+ CT_DATA = 1, /* Data clock */
+ CT_KERNEL = 2, /* Kernel clock */
+ CT_SYSTEM = 3 /* System Clock */
+};
+
+/* Clock Frequency Entry */
+struct clock_freq {
+ uint16_t m_freq_Mhz; /* Frequency in MHz */
+ uint8_t m_type; /* Clock type (enum CLOCK_TYPE) */
+ uint8_t m_unused[5]; /* Not used - padding */
+ char m_name[128]; /* Clock Name */
+};
+
+/* Clock frequency section */
+struct clock_freq_topology {
+ int16_t m_count; /* Number of entries */
+ struct clock_freq m_clock_freq[1]; /* Clock array */
+};
+
+/* Supported MCS file types */
+enum MCS_TYPE {
+ MCS_UNKNOWN = 0, /* Initialized value */
+ MCS_PRIMARY = 1, /* The primary mcs file data */
+ MCS_SECONDARY = 2, /* The secondary mcs file data */
+};
+
+/* One chunk of MCS data */
+struct mcs_chunk {
+ uint8_t m_type; /* MCS data type */
just call them type, unused, offset. Drop the m_*
Will remove all 'm_'
Thanks,
Lizhi
quoted
quoted
+ uint8_t m_unused[7]; /* padding */
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size */
+};
+
+/* MCS data section */
+struct mcs {
+ int8_t m_count; /* Number of chunks */
+ int8_t m_unused[7]; /* padding */
+ struct mcs_chunk m_chunk[1]; /* MCS chunks followed by data */
+};
+
+/* bmc data section */
+struct bmc {
+ uint64_t m_offset; /* data offset from the start of */
+ /* the section */
+ uint64_t m_size; /* data size (bytes) */
+ char m_image_name[64]; /* Name of the image */
+ /* (e.g., MSP432P401R) */
+ char m_device_name[64]; /* Device ID (e.g., VCU1525) */
+ char m_version[64];
+ char m_md5value[33]; /* MD5 Expected Value */
+ /* (e.g., 56027182079c0bd621761b7dab5a27ca)*/
+ char m_padding[7]; /* Padding */
+};
+
+/* soft kernel data section, used by classic driver */
+struct soft_kernel {
+ /** Prefix Syntax:
+ * mpo - member, pointer, offset
+ * This variable represents a zero terminated string
+ * that is offseted from the beginning of the section.
+ * The pointer to access the string is initialized as follows:
+ * char * pCharString = (address_of_section) + (mpo value)
+ */
+ uint32_t mpo_name; /* Name of the soft kernel */
+ uint32_t m_image_offset; /* Image offset */
+ uint32_t m_image_size; /* Image size */
+ uint32_t mpo_version; /* Version */
+ uint32_t mpo_md5_value; /* MD5 checksum */
+ uint32_t mpo_symbol_name; /* Symbol name */
+ uint32_t m_num_instances; /* Number of instances */
+ uint8_t padding[36]; /* Reserved for future use */
+ uint8_t reservedExt[16]; /* Reserved for future extended data */
+};
+
+enum CHECKSUM_TYPE {
+ CST_UNKNOWN = 0,
+ CST_SDBM = 1,
+ CST_LAST
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
I'll take a closer look, these were just random things I bumped into.
- Moritz
From: Tom Rix <trix@redhat.com> Date: 2021-03-06 15:26:16
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted hunk
Add clock frequence counter driver. Clock frequence counter is
a hardware function discovered by walking xclbin metadata. A platform
device node will be created for it. Other part of driver can read the
actual clock frequence through clock frequence counter driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 +++++++++++++++++++++++
2 files changed, 244 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c
From: Tom Rix <trix@redhat.com> Date: 2021-03-06 15:35:56
On 2/17/21 10:40 PM, Lizhi Hou wrote:
Add DDR calibration driver. DDR calibration is a hardware function
discovered by walking firmware metadata. A platform device node will
be created for it. Hardware provides DDR calibration status through
this function.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/calib.h | 30 ++++
drivers/fpga/xrt/lib/xleaf/calib.c | 226 +++++++++++++++++++++++++
2 files changed, 256 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/calib.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/calib.c
calib is not descriptive, change filename to ddr_calibration
+{
+ return -EOPNOTSUPP;
+}
+
+static int calib_calibration(struct calib *calib)
+{
+ int i;
+
+ for (i = 0; i < 20; i++) {
20 is a config parameter so should have a #define
There a couple of busy wait blocks in xrt/ some count up, some count down.
It would be good if they were consistent.
From: Tom Rix <trix@redhat.com> Date: 2021-03-06 15:55:37
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted hunk
Add partition isolation platform driver. partition isolation is
a hardware function discovered by walking firmware metadata.
A platform device node will be created for it. Partition isolation
function isolate the different fpga regions
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/axigate.h | 25 ++
drivers/fpga/xrt/lib/xleaf/axigate.c | 298 +++++++++++++++++++++++
2 files changed, 323 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/axigate.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/axigate.c
From: Moritz Fischer <mdf@kernel.org> Date: 2021-03-06 17:20:00
On Mon, Mar 01, 2021 at 06:48:46AM +0000, Sonal Santan wrote:
Hello Tom,
quoted
-----Original Message-----
From: Tom Rix <trix@redhat.com>
Sent: Friday, February 19, 2021 2:26 PM
To: Lizhi Hou <redacted>; linux-kernel@vger.kernel.org
Cc: Lizhi Hou <redacted>; linux-fpga@vger.kernel.org; Max Zhen
[off-list ref]; Sonal Santan [off-list ref]; Michal Simek
[off-list ref]; Stefano Stabellini [off-list ref];
devicetree@vger.kernel.org; mdf@kernel.org; robh@kernel.org; Max Zhen
[off-list ref]
Subject: Re: [PATCH V3 XRT Alveo 01/18] Documentation: fpga: Add a
document describing XRT Alveo drivers
From the documentation, there are a couple of big questions and a bunch of
word smithing.
pseudo-bus : do we need a bus ?
We are looking for guidance here.
quoted
xrt-lib real platform devices that aren't fpga, do they need to move to another
subsystem ?
Drivers for the IPs that show up in the Alveo shell are not generic enough. They
fit into the framework that XRT uses. Is the idea that that they can be used in a
different context?
quoted
Overall looks good, love the ascii art!
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Describe XRT driver architecture and provide basic overview of Xilinx
Alveo platform.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842
@@ -0,0 +1,842 @@+.. SPDX-License-Identifier: GPL-2.0++==================================+XRTV2 Linux Kernel Driver Overview+==================================++Authors:++* Sonal Santan <sonal.santan@xilinx.com>+* Max Zhen <max.zhen@xilinx.com>+* Lizhi Hou <lizhi.hou@xilinx.com>++XRTV2 drivers are second generation `XRT+<https://github.com/Xilinx/XRT>`_ drivers which support `Alveo+<https://www.xilinx.com/products/boards-and-kits/alveo.html>`_+PCIe platforms from Xilinx.++XRTV2 drivers support *subsystem* style data driven platforms where+driver's
where the driver's
quoted
+configuration and behavior is determined by meta data provided by the
+platform (in *device tree* format). Primary management physical
+function (MPF) driver is called **xmgmt**. Primary user physical
+function (UPF) driver is called
+**xuser** and is under development. xrt driver framework and HW
+subsystem drivers are packaged into a library module called
+**xrt-lib**, which is shared by **xmgmt** and **xuser** (under
+development). The xrt driver framework
xuser still under development ?
quoted
+implements a pseudo-bus which is used to discover HW subsystems and
+facilitate
I am wondering if we can phase in the migration to formal bus architecture
based on struct bus_type as a follow on set of patches?
I'd rather have it done early on. If we know there's a better way of
doing something and we don't the code should go into staging.
This gives us an out if it doesn't happen, otherwise the kernel
community would depend on corporate goodwill to appropriately staff it.
Note, I'm not trying to say there's any ill will anywhere, Xilinx has
been traditionally good about this :)
quoted
quoted
+inter HW subsystem interaction.
+
+Driver Modules
+==============
+
+xrt-lib.ko
+----------
+
+Repository of all subsystem drivers and pure software modules that
+can potentially
subsystem drivers
drivers in fpga/ should be for managing just the fpganess of the fpga.
soft devices ex/ a soft tty should go to their respective subsystem location
Are there any in this patchset you think might move ?
We have already shrunk the patch to only include FPGA centric pieces
necessary to get the bitstream download implemented. Should we explore
the question of subsystem drivers when we add support for more features of
the Alveo shell?
quoted
Maybe we can defer reviewing those now.
quoted
+be shared between xmgmt and xuser. All these drivers are structured
+as Linux *platform driver* and are instantiated by xmgmt (or xuser
+under development) based on meta data associated with hardware. The
+metadata is in the form of device tree
with the hardware
form of a device tree
Will change
quoted
quoted
+as mentioned before. Each platform driver statically defines a
+subsystem node array by using node name or a string in its
+``compatible`` property. And this array is eventually translated to IOMEM
resources of the platform device.
quoted
+
+The xrt-lib core infrastructure provides hooks to platform drivers
+for device node management, user file operations and ioctl callbacks.
+The core also provides pseudo-bus functionality for platform driver
+registration, discovery and inter platform driver ioctl calls.
core infrastructure.
Will update.
quoted
The interfaces to the infrastructure are not in include/linux/fpga/
Maybe this needs to change.
Were you thinking of moving XRT infrastructure header files from
drivers/fpga/xrt/include to include/linux/fpga?
quoted
quoted
+
+.. note::
+ See code in ``include/xleaf.h``
+
+
+xmgmt.ko
+--------
+
+The xmgmt driver is a PCIe device driver driving MPF found on
+Xilinx's Alveo PCIE device. It consists of one *root* driver, one or
+more *group* drivers and one or more *xleaf* drivers. The root and
+MPF specific xleaf drivers are in xmgmt.ko. The group driver and other xleaf
drivers are in xrt-lib.ko.
I am not sure if *.ko is correct, these will also be intree.
quoted
+
+The instantiation of specific group driver or xleaf driver is
+completely data
of a specific
quoted
+driven based on meta data (mostly in device tree format) found
+through VSEC
mostly ? what is the deviation from device tree ?
quoted
+capability and inside firmware files, such as platform xsabin or user xclbin
file.
quoted
+The root driver manages life cycle of multiple group drivers, which,
+in turn,
the life cycle
quoted
+manages multiple xleaf drivers. This allows a single set of driver
+code to support
set of drivers
drop 'code'
Will update
quoted
quoted
+all kinds of subsystems exposed by different shells. The difference
+among all these subsystems will be handled in xleaf drivers with root
+and group drivers being part of the infrastructure and provide common
+services for all leaves found on all platforms.
+
+The driver object model looks like the following::
+
+ +-----------+
+ | xroot |
+ +-----+-----+
+ |
+ +-----------+-----------+
+ | |
+ v v
+ +-----------+ +-----------+
+ | group | ... | group |
+ +-----+-----+ +------+----+
+ | |
+ | |
+ +-----+----+ +-----+----+
+ | | | |
+ v v v v
+ +-------+ +-------+ +-------+ +-------+
+ | xleaf |..| xleaf | | xleaf |..| xleaf |
+ +-------+ +-------+ +-------+ +-------+
+
+As an example for Xilinx Alveo U50 before user xclbin download, the
+tree looks like the following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+
+ | | |
+ v v v
+ +--------+ +--------+ +--------+
+ | group0 | | group1 | | group2 |
+ +----+---+ +----+---+ +---+----+
+ | | |
+ | | |
+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
+ | | | | | | | | |
+ v v | v v | v v |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | |
+ | AXI-GATE0 | |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | +---------+ | +------+ +-----------+ |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
+ +---------+ | +------+ +-----------+
+ | +-------+
+ +->| CALIB |
+ +-------+
+
Nice ascii art!
quoted
+After an xclbin is download, group3 will be added and the tree looks
+like the
+following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+-----------------+
+ | | | |
+ v v v |
+ +--------+ +--------+ +--------+ |
+ | group0 | | group1 | | group2 | |
+ +----+---+ +----+---+ +---+----+ |
+ | | | |
+ | | | |
+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
+ | | | | | | | | | |
+ v v | v v | v v | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | | AXI-GATE0 | |
|
quoted
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | +---------+ | +------+ +-----------+ | |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
+ +---------+ | +------+ +-----------+ |
+ | +-------+ |
+ +->| CALIB | |
+ +-------+ |
+ +---+----+ |
+ | group3 |<--------------------------------------------+
+ +--------+
+ |
+ |
+ +-------+--------+---+--+--------+------+-------+
+ | | | | | | |
+ v | v | v | v
+ +--------+ | +--------+ | +--------+ | +-----+
+ | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
+ +--------+ v +--------+ v +--------+ v +-----+
+ +-------------+ +-------------+ +-------------+
+ | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
+ +-------------+ +-------------+ +-------------+
+
+
+xmgmt-root
+^^^^^^^^^^
+
+The xmgmt-root driver is a PCIe device driver attached to MPF. It's
+part of the infrastructure of the MPF driver and resides in xmgmt.ko.
+This driver
+
+* manages one or more group drivers
+* provides access to functionalities that requires pci_dev, such as
+PCIE config
+ space access, to other xleaf drivers through root calls
+* together with group driver, facilities event callbacks for other
+xleaf drivers
+* together with group driver, facilities inter-leaf driver calls for
+other xleaf
Maybe drop 'together with group driver'
Will update
quoted
quoted
+ drivers
+
+When root driver starts, it will explicitly create an initial group
+instance, which contains xleaf drivers that will trigger the creation
+of other group instances. The root driver will wait for all group and
+leaves to be created before it returns from it's probe routine and
+claim success of the initialization of the entire xmgmt driver.
What happens if there a failure in one leaf ? Does the whole board go down ?
quoted
+
+.. note::
+ See code in ``lib/xroot.c`` and ``mgmt/root.c``
+
+
+group
+^^^^^
+
+The group driver is a platform device driver whose life cycle is
+managed by
Maybe call this a 'pseudo device'
Will update
quoted
quoted
+root and does not have real IO mem or IRQ resources. It's part of the
+infrastructure of the MPF driver and resides in xrt-lib.ko. This
+driver
+
+* manages one or more xleaf drivers so that multiple leaves can be
+managed as a
+ group
can drop 'so that multiple leaves can be managed as a group' to me, this is the
same as 'one or more'
Will do
quoted
quoted
+* provides access to root from leaves, so that root calls, event
+notifications
+ and inter-leaf calls can happen
+
+In xmgmt, an initial group driver instance will be created by root,
+which
by the root
quoted
+contains leaves that will trigger group instances to be created to
+manage groups of leaves found on different partitions on hardware,
+such as VSEC, Shell, and User.
+
+Every *fpga_region* has a group object associated with it. The group
+is created when xclbin image is loaded on the fpga_region. The
+existing group is destroyed when a new xclbin image is loaded. The
+fpga_region persists across xclbin downloads.
The connection of a 'group' node to a fpga region region is fairly important,
maybe move this section earlier. 'group' as an fpganess thing would be kept in
fpga/ subsystem.
Will update
quoted
quoted
+
+.. note::
+ See code in ``lib/group.c``
+
+
+xleaf
+^^^^^
+
+The xleaf driver is a platform device driver whose life cycle is
+managed by a group driver and may or may not have real IO mem or IRQ
+resources. They are the real meat of xmgmt and contains platform
+specific code to Shell and User found on a MPF.
+
Maybe a split is pseudo device leaves, those without real IO mem, stay in
fpga/ others go ?
This goes back to the earlier question of what minimal set of platform drivers
should stay in fpga subsystem. There are some like bridge or configuration
engine (also called icap) which have their own IO mem but do not have a life
outside of fpga subsystem.
quoted
quoted
+A xleaf driver may not have real hardware resources when it merely
+acts as a driver that manages certain in-memory states for xmgmt.
+These in-memory states could be shared by multiple other leaves.
+
This implies locking and some message passing.
quoted
+Leaf drivers assigned to specific hardware resources drive specific
+subsystem in
drive a specific
quoted
+the device. To manipulate the subsystem or carry out a task, a xleaf
+driver may ask help from root via root calls and/or from other leaves via
inter-leaf calls.
quoted
+
+A xleaf can also broadcast events through infrastructure code for
+other leaves to process. It can also receive event notification from
+infrastructure about certain events, such as post-creation or pre-exit of a
particular xleaf.
I would like to see some examples of how the inter node communications work.
Would update to show an example.
quoted
quoted
+
+.. note::
+ See code in ``lib/xleaf/*.c``
+
+
+FPGA Manager Interaction
+========================
+
+fpga_manager
+------------
+
+An instance of fpga_manager is created by xmgmt_main and is used for
+xclbin
for the xclbin
quoted
+image download. fpga_manager requires the full xclbin image before it
+can start programming the FPGA configuration engine via ICAP platform
driver.
via the ICAP
what is ICAP ?
Will update. ICAP stands for Internal Configuration Access Port used for configuring
the fpga.
quoted
quoted
+
+fpga_region
+-----------
+
+For every interface exposed by currently loaded xclbin/xsabin in the
+*parent*
by the currently
quoted
+fpga_region a new instance of fpga_region is created like a *child* region.
fpga_region,
quoted
+The device tree of the *parent* fpga_region defines the resources for
+a new instance of fpga_bridge which isolates the parent from
and isolates
quoted
+child fpga_region. This new instance of fpga_bridge will be used when
+a xclbin image is loaded on the child fpga_region. After the xclbin
+image is downloaded to the fpga_region, an instance of group is
+created for the fpga_region using the device tree obtained as part of
+xclbin. If this device
of the xclbin
quoted
+tree defines any child interfaces then it can trigger the creation of
interfaces, then
quoted
+fpga_bridge and fpga_region for the next region in the chain.
a fpga_bridge and a fpga_region
quoted
+
+fpga_bridge
+-----------
+
+Like fpga_region, matching fpga_bridge is also created by walking the
+device
Like the fpga_region, a matchin
quoted
+tree of the parent group.
+
+Driver Interfaces
+=================
+
+xmgmt Driver Ioctls
+-------------------
+
+Ioctls exposed by xmgmt driver to user space are enumerated in the
+following
+table:
+
+== ===================== ============================
==========================
quoted
+# Functionality ioctl request code data format
+== ===================== ============================
This data format is described below, maybe swap this section with that so
folks will know what xmgmnt_ioc_bitstream_axlf is before this section.
Will update.
quoted
quoted
+
+User xclbin can be downloaded by using xbmgmt tool from XRT open
+source suite. See
A user xclbin
using the xbmgmt
from the XRT
Will update
quoted
quoted
+example usage below::
+
+ xbmgmt partition --program --path
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verify.xc
+ lbin --force
+
+xmgmt Driver Sysfs
+------------------
+
+xmgmt driver exposes a rich set of sysfs interfaces. Subsystem
+platform drivers export sysfs node for every platform instance.
+
+Every partition also exports its UUIDs. See below for examples::
+
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
+
+
+hwmon
+-----
+
+xmgmt driver exposes standard hwmon interface to report voltage,
+current, temperature, power, etc. These can easily be viewed using
+*sensors* command line utility.
+
+Alveo Platform Overview
+=======================
+
+Alveo platforms are architected as two physical FPGA partitions:
+*Shell* and *User*. The Shell provides basic infrastructure for the
+Alveo platform like PCIe connectivity, board management, Dynamic
+Function Exchange (DFX), sensors, clocking, reset, and security. User
+partition contains user compiled FPGA
the user compiled
quoted
+binary which is loaded by a process called DFX also known as partial
+reconfiguration.
+
+Physical partitions require strict HW compatibility with each other
+for DFX to work properly.
swap order
For DFX to work properly physical partitions ..
Will update
quoted
quoted
Every physical partition has two interface UUIDs: *parent* UUID
+and *child* UUID. For simple single stage platforms, Shell → User
+forms parent child relationship. For complex two stage platforms,
+Base → Shell → User forms the parent child relationship chain.
this bit is confusing. is this related to uuid?
quoted
+
+.. note::
+ Partition compatibility matching is key design component of Alveo
platforms
quoted
+ and XRT. Partitions have child and parent relationship. A loaded
+partition
have a child
quoted
+ exposes child partition UUID to advertise its compatibility
+ requirement for
the child's
can drop 'for child partition'
Will update
quoted
quoted
+ child partition. When loading a child partition the xmgmt
+ management driver
When loading a child partition,
quoted
+ matches parent UUID of the child partition against child UUID exported by
+ the parent. Parent and child partition UUIDs are stored in the *xclbin*
+ (for user) or *xsabin* (for base and shell).
this is confusing, is this part of the file image format ?
Maybe save/move till the image layout.
Yes these IDs are stored in xclbin image format. Will move the sections around
as suggested.
quoted
quoted
Except for root UUID, VSEC,
+ hardware itself does not know about UUIDs. UUIDs are stored in xsabin
and
quoted
+ xclbin.
This is confusing too, not sure how to untangle.
Will reword.
quoted
quoted
+
+
+The physical partitions and their loading is illustrated below::
+
+ SHELL USER
+ +-----------+ +-------------------+
+ | | | |
+ | VSEC UUID | CHILD PARENT | LOGIC UUID |
+ | o------->|<--------o |
+ | | UUID UUID | |
+ +-----+-----+ +--------+----------+
+ | |
+ . .
+ | |
+ +---+---+ +------+--------+
+ | POR | | USER COMPILED |
+ | FLASH | | XCLBIN |
+ +-------+ +---------------+
+
+
+Loading Sequence
+----------------
+
+The Shell partition is loaded from flash at system boot time. It
+establishes the PCIe link and exposes two physical functions to the
+BIOS. After OS boot, xmgmt
the OS boots, the xmgmt
quoted
+driver attaches to PCIe physical function 0 exposed by the Shell and
+then looks for VSEC in PCIe extended configuration space. Using VSEC
+it determines the logic
the PCIe
The driver uses VSEC to determine the UUID of Shell. The UUID is also used to
load a matching ...
Will update
quoted
quoted
+UUID of Shell and uses the UUID to load matching *xsabin* file from
+Linux firmware directory. The xsabin file contains metadata to
+discover peripherals that are part of Shell and firmware(s) for any
embedded soft processors in Shell.
the firmware needed for any ...
Will update
quoted
quoted
+
+The Shell exports child interface UUID which is used for
+compatibility check when
export a child
for a compatibility check
Will update
quoted
quoted
+loading user compiled xclbin over the User partition as part of DFX.
+When a user requests loading of a specific xclbin the xmgmt
+management driver reads the parent
xclbin, the
quoted
+interface UUID specified in the xclbin and matches it with child
+interface UUID exported by Shell to determine if xclbin is compatible
+with the Shell. If match fails loading of xclbin is denied.
+
+xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl command.
+When loading xclbin, xmgmt driver performs the following *logical*
operations:
quoted
+
+1. Copy xclbin from user to kernel memory 2. Sanity check the xclbin
+contents 3. Isolate the User partition 4. Download the bitstream
+using the FPGA config engine (ICAP) 5. De-isolate the User partition
+6. Program the clocks (ClockWiz) driving the User partition
+
+xsabin
+------
+
+Each Alveo platform comes packaged with its own xsabin. The xsabin is
+trusted
is a trusted
quoted
+component of the platform. For format details refer to
+:ref:`xsabin_xclbin_container_format`
+below. xsabin contains basic information like UUIDs, platform name
+and metadata in the form of device tree. See :ref:`device_tree_usage`
+tool set from Xilinx. The xclbin contains sections describing user
+compiled acceleration engines/kernels, memory subsystems, clocking
+information etc. It also contains bitstream for the user partition,
+UUIDs, platform name, etc. xclbin uses
bitstreams
quoted
+the same container format as xsabin which is described below.
+
+
+.. _xsabin_xclbin_container_format:
+
+xsabin/xclbin Container Format
+------------------------------
+
+xclbin/xsabin is ELF-like binary container format. It is structured
+as series of sections. There is a file header followed by several
+section headers which is followed by sections. A section header
+points to an actual section. There is an optional signature at the end. The
format is defined by header file ``xclbin.h``.
quoted
+The following figure illustrates a typical xclbin::
+
+
+ +---------------------+
+ | |
+ | HEADER |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | SIGNATURE |
+ | (OPTIONAL) |
+ +---------------------+
+
+
+xclbin/xsabin files can be packaged, un-packaged and inspected using
+XRT utility called **xclbinutil**. xclbinutil is part of XRT open
+source software stack. The source code for xclbinutil can be found at
+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/xclbi
+nutil
+
Works, but maybe the location of a manpage or doc would be better.
quoted
+For example to enumerate the contents of a xclbin/xsabin use the
+*--info* switch as shown below::
+
+
+ xclbinutil --info --input
+ /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
+ xclbinutil --info --input
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.xsab
+ in
+
+
+.. _device_tree_usage:
+
+Device Tree Usage
+-----------------
+
+As mentioned previously xsabin stores metadata which advertise HW
+subsystems present in a partition. The metadata is stored in device tree
format with well defined schema.
quoted
+XRT management driver uses this information to bind *platform
+drivers* to the subsystem instantiations. The platform drivers are
+found in **xrt-lib.ko** kernel module defined later.
+
+Logic UUID
+^^^^^^^^^^
+A partition is identified uniquely through ``logic_uuid`` property::
+
+ /dts-v1/;
+ / {
+ logic_uuid = "0123456789abcdef0123456789abcdef";
+ ...
+ }
+
+Schema Version
+^^^^^^^^^^^^^^
+Schema version is defined through ``schema_version`` node. And it
+contains ``major`` and ``minor`` properties as below::
+
+ /dts-v1/;
+ / {
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+ ...
+ }
+
+Partition UUIDs
+^^^^^^^^^^^^^^^
+As said earlier, each partition may have parent and child UUIDs.
+These UUIDs are defined by ``interfaces`` node and ``interface_uuid``
property::
quoted
+
+ /dts-v1/;
+ / {
+ interfaces {
+ @0 {
+ interface_uuid = "0123456789abcdef0123456789abcdef";
+ };
+ @1 {
+ interface_uuid = "fedcba9876543210fedcba9876543210";
+ };
+ ...
+ };
+ ...
+ }
+
+
+Subsystem Instantiations
+^^^^^^^^^^^^^^^^^^^^^^^^
+Subsystem instantiations are captured as children of
+``addressable_endpoints``
+node::
+
+ /dts-v1/;
+ / {
+ addressable_endpoints {
+ abc {
+ ...
+ };
+ def {
+ ...
+ };
+ ...
+ }
+ }
+
+Subnode 'abc' and 'def' are the name of subsystem nodes
+
+Subsystem Node
+^^^^^^^^^^^^^^
+Each subsystem node and its properties define a hardware instance::
+
+
+ addressable_endpoints {
+ abc {
+ reg = <0xa 0xb>
+ pcie_physical_function = <0x0>;
+ pcie_bar_mapping = <0x2>;
+ compatible = "abc def";
+ firmware {
+ firmware_product_name = "abc"
+ firmware_branch_name = "def"
+ firmware_version_major = <1>
+ firmware_version_minor = <2>
+ };
+ }
+ ...
+ }
+
+:reg:
+ Property defines address range. '<0xa 0xb>' is BAR offset and length
+pair, both are 64-bit integer.
+:pcie_physical_function:
+ Property specifies which PCIe physical function the subsystem node resides.
+:pcie_bar_mapping:
+ Property specifies which PCIe BAR the subsystem node resides.
+'<0x2>' is BAR index and it is 0 if this property is not defined.
+:compatible:
+ Property is a list of strings. The first string in the list
+specifies the exact subsystem node. The following strings represent
+other devices that the device is compatible with.
+:firmware:
+ Subnode defines the firmware required by this subsystem node.
+
+Alveo U50 Platform Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+ /dts-v1/;
+
+ /{
+ logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
+
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+
+ interfaces {
+
+ @0 {
+ interface_uuid = "862c7020a250293e32036f19956669e5";
+ };
+ };
+
+ addressable_endpoints {
+
+ ep_blp_rom_00 {
+ reg = <0x00 0x1f04000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible = "xilinx.com,reg_abs-axi_bram_ctrl-
Thanks for the detailed review of the document. I am working on incorporating the
feedback. One outstanding question is about usage of formal bus in XRT and if we
should phase that in as a follow-on. It would also determine if IP drivers should
move to other subsystems.
-Sonal
-----Original Message-----
From: Moritz Fischer <mdf@kernel.org>
Sent: Saturday, March 6, 2021 9:19 AM
To: Sonal Santan <redacted>
Cc: Tom Rix <trix@redhat.com>; Lizhi Hou <redacted>; linux-
kernel@vger.kernel.org; linux-fpga@vger.kernel.org; Max Zhen
[off-list ref]; Michal Simek [off-list ref]; Stefano Stabellini
[off-list ref]; devicetree@vger.kernel.org; mdf@kernel.org;
robh@kernel.org
Subject: Re: [PATCH V3 XRT Alveo 01/18] Documentation: fpga: Add a
document describing XRT Alveo drivers
On Mon, Mar 01, 2021 at 06:48:46AM +0000, Sonal Santan wrote:
quoted
Hello Tom,
quoted
-----Original Message-----
From: Tom Rix <trix@redhat.com>
Sent: Friday, February 19, 2021 2:26 PM
To: Lizhi Hou <redacted>; linux-kernel@vger.kernel.org
Cc: Lizhi Hou <redacted>; linux-fpga@vger.kernel.org; Max
Zhen [off-list ref]; Sonal Santan [off-list ref]; Michal
Simek [off-list ref]; Stefano Stabellini
[off-list ref]; devicetree@vger.kernel.org; mdf@kernel.org;
robh@kernel.org; Max Zhen [off-list ref]
Subject: Re: [PATCH V3 XRT Alveo 01/18] Documentation: fpga: Add a
document describing XRT Alveo drivers
From the documentation, there are a couple of big questions and a
bunch of word smithing.
pseudo-bus : do we need a bus ?
We are looking for guidance here.
quoted
xrt-lib real platform devices that aren't fpga, do they need to move
to another subsystem ?
Drivers for the IPs that show up in the Alveo shell are not generic
enough. They fit into the framework that XRT uses. Is the idea that
that they can be used in a different context?
quoted
Overall looks good, love the ascii art!
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Describe XRT driver architecture and provide basic overview of
Xilinx Alveo platform.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
Documentation/fpga/index.rst | 1 +
Documentation/fpga/xrt.rst | 842
@@ -8,6 +8,7 @@ fpga:maxdepth: 1 dfl+ xrt..only:: subproject and html
diff --git a/Documentation/fpga/xrt.rst
b/Documentation/fpga/xrt.rst new file mode 100644 index
000000000000..9bc2d2785cb9
--- /dev/null+++ b/Documentation/fpga/xrt.rst
@@ -0,0 +1,842 @@+.. SPDX-License-Identifier: GPL-2.0++==================================+XRTV2 Linux Kernel Driver Overview+==================================++Authors:++* Sonal Santan <sonal.santan@xilinx.com>+* Max Zhen <max.zhen@xilinx.com>+* Lizhi Hou <lizhi.hou@xilinx.com>++XRTV2 drivers are second generation `XRT+<https://github.com/Xilinx/XRT>`_ drivers which support `Alveo+<https://www.xilinx.com/products/boards-and-kits/alveo.html>`_+PCIe platforms from Xilinx.++XRTV2 drivers support *subsystem* style data driven platforms+where driver's
where the driver's
quoted
+configuration and behavior is determined by meta data provided by
+the platform (in *device tree* format). Primary management
+physical function (MPF) driver is called **xmgmt**. Primary user
+physical function (UPF) driver is called
+**xuser** and is under development. xrt driver framework and HW
+subsystem drivers are packaged into a library module called
+**xrt-lib**, which is shared by **xmgmt** and **xuser** (under
+development). The xrt driver framework
xuser still under development ?
quoted
+implements a pseudo-bus which is used to discover HW subsystems
+and facilitate
I am wondering if we can phase in the migration to formal bus
architecture based on struct bus_type as a follow on set of patches?
I'd rather have it done early on. If we know there's a better way of doing
something and we don't the code should go into staging.
This gives us an out if it doesn't happen, otherwise the kernel community would
depend on corporate goodwill to appropriately staff it.
Note, I'm not trying to say there's any ill will anywhere, Xilinx has been
traditionally good about this :)
Thanks for the suggestion. Since the bus change is a bigger change we will
incorporate it into V5 version of the patch series. Meanwhile we will post V4
version of patch series which will addresses other feedback we have received
so far.
quoted
quoted
quoted
+inter HW subsystem interaction.
+
+Driver Modules
+==============
+
+xrt-lib.ko
+----------
+
+Repository of all subsystem drivers and pure software modules
+that can potentially
subsystem drivers
drivers in fpga/ should be for managing just the fpganess of the fpga.
soft devices ex/ a soft tty should go to their respective subsystem
location
Are there any in this patchset you think might move ?
We have already shrunk the patch to only include FPGA centric pieces
necessary to get the bitstream download implemented. Should we explore
the question of subsystem drivers when we add support for more
features of the Alveo shell?
quoted
Maybe we can defer reviewing those now.
quoted
+be shared between xmgmt and xuser. All these drivers are
+structured as Linux *platform driver* and are instantiated by
+xmgmt (or xuser under development) based on meta data associated
+with hardware. The metadata is in the form of device tree
with the hardware
form of a device tree
Will change
quoted
quoted
+as mentioned before. Each platform driver statically defines a
+subsystem node array by using node name or a string in its
+``compatible`` property. And this array is eventually translated
+to IOMEM
resources of the platform device.
quoted
+
+The xrt-lib core infrastructure provides hooks to platform
+drivers for device node management, user file operations and ioctl
callbacks.
quoted
quoted
quoted
+The core also provides pseudo-bus functionality for platform
+driver registration, discovery and inter platform driver ioctl calls.
core infrastructure.
Will update.
quoted
The interfaces to the infrastructure are not in include/linux/fpga/
Maybe this needs to change.
Were you thinking of moving XRT infrastructure header files from
drivers/fpga/xrt/include to include/linux/fpga?
quoted
quoted
+
+.. note::
+ See code in ``include/xleaf.h``
+
+
+xmgmt.ko
+--------
+
+The xmgmt driver is a PCIe device driver driving MPF found on
+Xilinx's Alveo PCIE device. It consists of one *root* driver, one
+or more *group* drivers and one or more *xleaf* drivers. The root
+and MPF specific xleaf drivers are in xmgmt.ko. The group driver
+and other xleaf
drivers are in xrt-lib.ko.
I am not sure if *.ko is correct, these will also be intree.
quoted
+
+The instantiation of specific group driver or xleaf driver is
+completely data
of a specific
quoted
+driven based on meta data (mostly in device tree format) found
+through VSEC
mostly ? what is the deviation from device tree ?
quoted
+capability and inside firmware files, such as platform xsabin or
+user xclbin
file.
quoted
+The root driver manages life cycle of multiple group drivers,
+which, in turn,
the life cycle
quoted
+manages multiple xleaf drivers. This allows a single set of
+driver code to support
set of drivers
drop 'code'
Will update
quoted
quoted
+all kinds of subsystems exposed by different shells. The
+difference among all these subsystems will be handled in xleaf
+drivers with root and group drivers being part of the
+infrastructure and provide common services for all leaves found on all
platforms.
quoted
quoted
quoted
+
+The driver object model looks like the following::
+
+ +-----------+
+ | xroot |
+ +-----+-----+
+ |
+ +-----------+-----------+
+ | |
+ v v
+ +-----------+ +-----------+
+ | group | ... | group |
+ +-----+-----+ +------+----+
+ | |
+ | |
+ +-----+----+ +-----+----+
+ | | | |
+ v v v v
+ +-------+ +-------+ +-------+ +-------+
+ | xleaf |..| xleaf | | xleaf |..| xleaf |
+ +-------+ +-------+ +-------+ +-------+
+
+As an example for Xilinx Alveo U50 before user xclbin download,
+the tree looks like the following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+
+ | | |
+ v v v
+ +--------+ +--------+ +--------+
+ | group0 | | group1 | | group2 |
+ +----+---+ +----+---+ +---+----+
+ | | |
+ | | |
+ +-----+-----+ +----+-----+---+ +-----+-----+----+--------+
+ | | | | | | | | |
+ v v | v v | v v |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | |
+ | AXI-GATE0 | |
+ +------------+ +------+ | +------+ +------+ | +------+
+ +------------+ +-----------+ |
+ | +---------+ | +------+ +-----------+ |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+
+ +---------+ | +------+ +-----------+
+ | +-------+
+ +->| CALIB |
+ +-------+
+
Nice ascii art!
quoted
+After an xclbin is download, group3 will be added and the tree
+looks like the
+following::
+
+ +-----------+
+ | xmgmt |
+ +-----+-----+
+ |
+ +-------------------------+--------------------+-----------------+
+ | | | |
+ v v v |
+ +--------+ +--------+ +--------+ |
+ | group0 | | group1 | | group2 | |
+ +----+---+ +----+---+ +---+----+ |
+ | | | |
+ | | | |
+ +-----+-----+ +-----+-----+---+ +-----+-----+----+--------+ |
+ | | | | | | | | | |
+ v v | v v | v v | |
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | xmgmt_main | | VSEC | | | GPIO | | QSPI | | | CMC | |
+ | AXI-GATE0 | |
|
quoted
+ +------------+ +------+ | +------+ +------+ | +------+ +-----------+ | |
+ | +---------+ | +------+ +-----------+ | |
+ +>| MAILBOX | +->| ICAP | | AXI-GATE1 |<+ |
+ +---------+ | +------+ +-----------+ |
+ | +-------+ |
+ +->| CALIB | |
+ +-------+ |
+ +---+----+ |
+ | group3 |<--------------------------------------------+
+ +--------+
+ |
+ |
+ +-------+--------+---+--+--------+------+-------+
+ | | | | | | |
+ v | v | v | v
+ +--------+ | +--------+ | +--------+ | +-----+
+ | CLOCK0 | | | CLOCK1 | | | CLOCK2 | | | UCS |
+ +--------+ v +--------+ v +--------+ v +-----+
+ +-------------+ +-------------+ +-------------+
+ | CLOCK-FREQ0 | | CLOCK-FREQ1 | | CLOCK-FREQ2 |
+ +-------------+ +-------------+ +-------------+
+
+
+xmgmt-root
+^^^^^^^^^^
+
+The xmgmt-root driver is a PCIe device driver attached to MPF.
+It's part of the infrastructure of the MPF driver and resides in xmgmt.ko.
+This driver
+
+* manages one or more group drivers
+* provides access to functionalities that requires pci_dev, such
+as PCIE config
+ space access, to other xleaf drivers through root calls
+* together with group driver, facilities event callbacks for
+other xleaf drivers
+* together with group driver, facilities inter-leaf driver calls
+for other xleaf
Maybe drop 'together with group driver'
Will update
quoted
quoted
+ drivers
+
+When root driver starts, it will explicitly create an initial
+group instance, which contains xleaf drivers that will trigger
+the creation of other group instances. The root driver will wait
+for all group and leaves to be created before it returns from
+it's probe routine and claim success of the initialization of the entire
xmgmt driver.
quoted
quoted
What happens if there a failure in one leaf ? Does the whole board go down
?
quoted
quoted
quoted
+
+.. note::
+ See code in ``lib/xroot.c`` and ``mgmt/root.c``
+
+
+group
+^^^^^
+
+The group driver is a platform device driver whose life cycle is
+managed by
Maybe call this a 'pseudo device'
Will update
quoted
quoted
+root and does not have real IO mem or IRQ resources. It's part of
+the infrastructure of the MPF driver and resides in xrt-lib.ko.
+This driver
+
+* manages one or more xleaf drivers so that multiple leaves can
+be managed as a
+ group
can drop 'so that multiple leaves can be managed as a group' to me,
this is the same as 'one or more'
Will do
quoted
quoted
+* provides access to root from leaves, so that root calls, event
+notifications
+ and inter-leaf calls can happen
+
+In xmgmt, an initial group driver instance will be created by
+root, which
by the root
quoted
+contains leaves that will trigger group instances to be created
+to manage groups of leaves found on different partitions on
+hardware, such as VSEC, Shell, and User.
+
+Every *fpga_region* has a group object associated with it. The
+group is created when xclbin image is loaded on the fpga_region.
+The existing group is destroyed when a new xclbin image is
+loaded. The fpga_region persists across xclbin downloads.
The connection of a 'group' node to a fpga region region is fairly
important, maybe move this section earlier. 'group' as an fpganess
thing would be kept in fpga/ subsystem.
Will update
quoted
quoted
+
+.. note::
+ See code in ``lib/group.c``
+
+
+xleaf
+^^^^^
+
+The xleaf driver is a platform device driver whose life cycle is
+managed by a group driver and may or may not have real IO mem or
+IRQ resources. They are the real meat of xmgmt and contains
+platform specific code to Shell and User found on a MPF.
+
Maybe a split is pseudo device leaves, those without real IO mem,
stay in fpga/ others go ?
This goes back to the earlier question of what minimal set of platform
drivers should stay in fpga subsystem. There are some like bridge or
configuration engine (also called icap) which have their own IO mem
but do not have a life outside of fpga subsystem.
quoted
quoted
+A xleaf driver may not have real hardware resources when it
+merely acts as a driver that manages certain in-memory states for
xmgmt.
quoted
quoted
quoted
+These in-memory states could be shared by multiple other leaves.
+
This implies locking and some message passing.
quoted
+Leaf drivers assigned to specific hardware resources drive
+specific subsystem in
drive a specific
quoted
+the device. To manipulate the subsystem or carry out a task, a
+xleaf driver may ask help from root via root calls and/or from
+other leaves via
inter-leaf calls.
quoted
+
+A xleaf can also broadcast events through infrastructure code for
+other leaves to process. It can also receive event notification
+from infrastructure about certain events, such as post-creation
+or pre-exit of a
particular xleaf.
I would like to see some examples of how the inter node communications
work.
quoted
Would update to show an example.
quoted
quoted
+
+.. note::
+ See code in ``lib/xleaf/*.c``
+
+
+FPGA Manager Interaction
+========================
+
+fpga_manager
+------------
+
+An instance of fpga_manager is created by xmgmt_main and is used
+for xclbin
for the xclbin
quoted
+image download. fpga_manager requires the full xclbin image
+before it can start programming the FPGA configuration engine via
+ICAP platform
driver.
via the ICAP
what is ICAP ?
Will update. ICAP stands for Internal Configuration Access Port used
for configuring the fpga.
quoted
quoted
+
+fpga_region
+-----------
+
+For every interface exposed by currently loaded xclbin/xsabin in
+the
+*parent*
by the currently
quoted
+fpga_region a new instance of fpga_region is created like a *child*
region.
quoted
quoted
fpga_region,
quoted
+The device tree of the *parent* fpga_region defines the resources
+for a new instance of fpga_bridge which isolates the parent from
and isolates
quoted
+child fpga_region. This new instance of fpga_bridge will be used
+when a xclbin image is loaded on the child fpga_region. After the
+xclbin image is downloaded to the fpga_region, an instance of
+group is created for the fpga_region using the device tree
+obtained as part of xclbin. If this device
of the xclbin
quoted
+tree defines any child interfaces then it can trigger the
+creation of
interfaces, then
quoted
+fpga_bridge and fpga_region for the next region in the chain.
a fpga_bridge and a fpga_region
quoted
+
+fpga_bridge
+-----------
+
+Like fpga_region, matching fpga_bridge is also created by walking
+the device
Like the fpga_region, a matchin
quoted
+tree of the parent group.
+
+Driver Interfaces
+=================
+
+xmgmt Driver Ioctls
+-------------------
+
+Ioctls exposed by xmgmt driver to user space are enumerated in
+the following
+table:
+
+== ===================== ============================
==========================
quoted
+# Functionality ioctl request code data format
+== ===================== ============================
This data format is described below, maybe swap this section with
that so
folks will know what xmgmnt_ioc_bitstream_axlf is before this section.
Will update.
quoted
quoted
+
+User xclbin can be downloaded by using xbmgmt tool from XRT open
+source suite. See
A user xclbin
using the xbmgmt
from the XRT
Will update
quoted
quoted
+example usage below::
+
+ xbmgmt partition --program --path
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/test/verif
+ y.xc
+ lbin --force
+
+xmgmt Driver Sysfs
+------------------
+
+xmgmt driver exposes a rich set of sysfs interfaces. Subsystem
+platform drivers export sysfs node for every platform instance.
+
+Every partition also exports its UUIDs. See below for examples::
+
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/interface_uuids
+ /sys/bus/pci/devices/0000:06:00.0/xmgmt_main.0/logic_uuids
+
+
+hwmon
+-----
+
+xmgmt driver exposes standard hwmon interface to report voltage,
+current, temperature, power, etc. These can easily be viewed
+using
+*sensors* command line utility.
+
+Alveo Platform Overview
+=======================
+
+Alveo platforms are architected as two physical FPGA partitions:
+*Shell* and *User*. The Shell provides basic infrastructure for
+the Alveo platform like PCIe connectivity, board management,
+Dynamic Function Exchange (DFX), sensors, clocking, reset, and
+security. User partition contains user compiled FPGA
the user compiled
quoted
+binary which is loaded by a process called DFX also known as
+partial reconfiguration.
+
+Physical partitions require strict HW compatibility with each
+other for DFX to work properly.
swap order
For DFX to work properly physical partitions ..
Will update
quoted
quoted
Every physical partition has two interface UUIDs: *parent* UUID
+and *child* UUID. For simple single stage platforms, Shell → User
+forms parent child relationship. For complex two stage platforms,
+Base → Shell → User forms the parent child relationship chain.
this bit is confusing. is this related to uuid?
quoted
+
+.. note::
+ Partition compatibility matching is key design component of
+Alveo
platforms
quoted
+ and XRT. Partitions have child and parent relationship. A
+loaded partition
have a child
quoted
+ exposes child partition UUID to advertise its compatibility
+ requirement for
the child's
can drop 'for child partition'
Will update
quoted
quoted
+ child partition. When loading a child partition the xmgmt
+ management driver
When loading a child partition,
quoted
+ matches parent UUID of the child partition against child UUID exported
by
quoted
quoted
quoted
+ the parent. Parent and child partition UUIDs are stored in the *xclbin*
+ (for user) or *xsabin* (for base and shell).
this is confusing, is this part of the file image format ?
Maybe save/move till the image layout.
Yes these IDs are stored in xclbin image format. Will move the
sections around as suggested.
quoted
quoted
Except for root UUID, VSEC,
+ hardware itself does not know about UUIDs. UUIDs are stored in
+ xsabin
and
quoted
+ xclbin.
This is confusing too, not sure how to untangle.
Will reword.
quoted
quoted
+
+
+The physical partitions and their loading is illustrated below::
+
+ SHELL USER
+ +-----------+ +-------------------+
+ | | | |
+ | VSEC UUID | CHILD PARENT | LOGIC UUID |
+ | o------->|<--------o |
+ | | UUID UUID | |
+ +-----+-----+ +--------+----------+
+ | |
+ . .
+ | |
+ +---+---+ +------+--------+
+ | POR | | USER COMPILED |
+ | FLASH | | XCLBIN |
+ +-------+ +---------------+
+
+
+Loading Sequence
+----------------
+
+The Shell partition is loaded from flash at system boot time. It
+establishes the PCIe link and exposes two physical functions to
+the BIOS. After OS boot, xmgmt
the OS boots, the xmgmt
quoted
+driver attaches to PCIe physical function 0 exposed by the Shell
+and then looks for VSEC in PCIe extended configuration space.
+Using VSEC it determines the logic
the PCIe
The driver uses VSEC to determine the UUID of Shell. The UUID is
also used to load a matching ...
Will update
quoted
quoted
+UUID of Shell and uses the UUID to load matching *xsabin* file
+from Linux firmware directory. The xsabin file contains metadata
+to discover peripherals that are part of Shell and firmware(s)
+for any
embedded soft processors in Shell.
the firmware needed for any ...
Will update
quoted
quoted
+
+The Shell exports child interface UUID which is used for
+compatibility check when
export a child
for a compatibility check
Will update
quoted
quoted
+loading user compiled xclbin over the User partition as part of DFX.
+When a user requests loading of a specific xclbin the xmgmt
+management driver reads the parent
xclbin, the
quoted
+interface UUID specified in the xclbin and matches it with child
+interface UUID exported by Shell to determine if xclbin is
+compatible with the Shell. If match fails loading of xclbin is denied.
+
+xclbin loading is requested using ICAP_DOWNLOAD_AXLF ioctl
command.
quoted
quoted
quoted
+When loading xclbin, xmgmt driver performs the following
+*logical*
operations:
quoted
+
+1. Copy xclbin from user to kernel memory 2. Sanity check the
+xclbin contents 3. Isolate the User partition 4. Download the
+bitstream using the FPGA config engine (ICAP) 5. De-isolate the
+User partition 6. Program the clocks (ClockWiz) driving the User
+partition
+
+xsabin
+------
+
+Each Alveo platform comes packaged with its own xsabin. The
+xsabin is trusted
is a trusted
quoted
+component of the platform. For format details refer to
+:ref:`xsabin_xclbin_container_format`
+below. xsabin contains basic information like UUIDs, platform
+name and metadata in the form of device tree. See
+:ref:`device_tree_usage`
+tool set from Xilinx. The xclbin contains sections describing
+user compiled acceleration engines/kernels, memory subsystems,
+clocking information etc. It also contains bitstream for the user
+partition, UUIDs, platform name, etc. xclbin uses
bitstreams
quoted
+the same container format as xsabin which is described below.
+
+
+.. _xsabin_xclbin_container_format:
+
+xsabin/xclbin Container Format
+------------------------------
+
+xclbin/xsabin is ELF-like binary container format. It is
+structured as series of sections. There is a file header followed
+by several section headers which is followed by sections. A
+section header points to an actual section. There is an optional
+signature at the end. The
format is defined by header file ``xclbin.h``.
quoted
+The following figure illustrates a typical xclbin::
+
+
+ +---------------------+
+ | |
+ | HEADER |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION HEADER |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | ... |
+ | |
+ +---------------------+
+ | SECTION |
+ | |
+ +---------------------+
+ | SIGNATURE |
+ | (OPTIONAL) |
+ +---------------------+
+
+
+xclbin/xsabin files can be packaged, un-packaged and inspected
+using XRT utility called **xclbinutil**. xclbinutil is part of
+XRT open source software stack. The source code for xclbinutil
+can be found at
+https://github.com/Xilinx/XRT/tree/master/src/runtime_src/tools/x
+clbi
+nutil
+
Works, but maybe the location of a manpage or doc would be better.
quoted
+For example to enumerate the contents of a xclbin/xsabin use the
+*--info* switch as shown below::
+
+
+ xclbinutil --info --input
+ /opt/xilinx/firmware/u50/gen3x16-xdma/blp/test/bandwidth.xclbin
+ xclbinutil --info --input
+ /lib/firmware/xilinx/862c7020a250293e32036f19956669e5/partition.
+ xsab
+ in
+
+
+.. _device_tree_usage:
+
+Device Tree Usage
+-----------------
+
+As mentioned previously xsabin stores metadata which advertise HW
+subsystems present in a partition. The metadata is stored in
+device tree
format with well defined schema.
quoted
+XRT management driver uses this information to bind *platform
+drivers* to the subsystem instantiations. The platform drivers
+are found in **xrt-lib.ko** kernel module defined later.
+
+Logic UUID
+^^^^^^^^^^
+A partition is identified uniquely through ``logic_uuid`` property::
+
+ /dts-v1/;
+ / {
+ logic_uuid = "0123456789abcdef0123456789abcdef";
+ ...
+ }
+
+Schema Version
+^^^^^^^^^^^^^^
+Schema version is defined through ``schema_version`` node. And it
+contains ``major`` and ``minor`` properties as below::
+
+ /dts-v1/;
+ / {
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+ ...
+ }
+
+Partition UUIDs
+^^^^^^^^^^^^^^^
+As said earlier, each partition may have parent and child UUIDs.
+These UUIDs are defined by ``interfaces`` node and
+``interface_uuid``
property::
quoted
+
+ /dts-v1/;
+ / {
+ interfaces {
+ @0 {
+ interface_uuid = "0123456789abcdef0123456789abcdef";
+ };
+ @1 {
+ interface_uuid = "fedcba9876543210fedcba9876543210";
+ };
+ ...
+ };
+ ...
+ }
+
+
+Subsystem Instantiations
+^^^^^^^^^^^^^^^^^^^^^^^^
+Subsystem instantiations are captured as children of
+``addressable_endpoints``
+node::
+
+ /dts-v1/;
+ / {
+ addressable_endpoints {
+ abc {
+ ...
+ };
+ def {
+ ...
+ };
+ ...
+ }
+ }
+
+Subnode 'abc' and 'def' are the name of subsystem nodes
+
+Subsystem Node
+^^^^^^^^^^^^^^
+Each subsystem node and its properties define a hardware instance::
+
+
+ addressable_endpoints {
+ abc {
+ reg = <0xa 0xb>
+ pcie_physical_function = <0x0>;
+ pcie_bar_mapping = <0x2>;
+ compatible = "abc def";
+ firmware {
+ firmware_product_name = "abc"
+ firmware_branch_name = "def"
+ firmware_version_major = <1>
+ firmware_version_minor = <2>
+ };
+ }
+ ...
+ }
+
+:reg:
+ Property defines address range. '<0xa 0xb>' is BAR offset and
+length pair, both are 64-bit integer.
+:pcie_physical_function:
+ Property specifies which PCIe physical function the subsystem node
resides.
quoted
quoted
quoted
+:pcie_bar_mapping:
+ Property specifies which PCIe BAR the subsystem node resides.
+'<0x2>' is BAR index and it is 0 if this property is not defined.
+:compatible:
+ Property is a list of strings. The first string in the list
+specifies the exact subsystem node. The following strings
+represent other devices that the device is compatible with.
+:firmware:
+ Subnode defines the firmware required by this subsystem node.
+
+Alveo U50 Platform Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+ /dts-v1/;
+
+ /{
+ logic_uuid = "f465b0a3ae8c64f619bc150384ace69b";
+
+ schema_version {
+ major = <0x01>;
+ minor = <0x00>;
+ };
+
+ interfaces {
+
+ @0 {
+ interface_uuid = "862c7020a250293e32036f19956669e5";
+ };
+ };
+
+ addressable_endpoints {
+
+ ep_blp_rom_00 {
+ reg = <0x00 0x1f04000 0x00 0x1000>;
+ pcie_physical_function = <0x00>;
+ compatible =
+ "xilinx.com,reg_abs-axi_bram_ctrl-
Thanks for the detailed review of the document. I am working on
incorporating the feedback. One outstanding question is about usage of
formal bus in XRT and if we should phase that in as a follow-on. It
would also determine if IP drivers should move to other subsystems.
-Sonal
Will change to if (e != XRT_EVENT_POST_CREATION) return -EINVAL
quoted
+
+ if (id != XRT_SUBDEV_CLOCK)
+ return;
+
+ leaf = xleaf_get_leaf_by_id(pdev, XRT_SUBDEV_CLOCK, instance);
+ if (!leaf) {
+ xrt_err(pdev, "does not get clock subdev");
+ return;
+ }
+
+ xleaf_ioctl(leaf, XRT_CLOCK_VERIFY, NULL);
+ xleaf_put_leaf(pdev, leaf);
+}
+
+static void ucs_check(struct xrt_ucs *ucs, bool *latched)
+{
checking but not returning status, change to returning int.
this function is called but xrt_ucs_leaf_ioctl which does return status.
Will remove ucs_check() because it is not used in this patch set.
quoted
+ struct ucs_control_status_ch1 *ucs_status_ch1;
+ u32 status;
+
+ mutex_lock(&ucs->ucs_lock);
+ status = reg_rd(ucs, CHANNEL1_OFFSET);
+ ucs_status_ch1 = (struct ucs_control_status_ch1 *)&status;
+ if (ucs_status_ch1->shutdown_clocks_latched) {
+ UCS_ERR(ucs,
+ "Critical temperature or power event, kernel clocks have been stopped.");
+ UCS_ERR(ucs,
+ "run 'xbutil valiate -q' to continue. See AR 73398 for more details.");
This error message does not seem like it would be useful, please review.
clock and clkfreq are two different IPs. clkfreq is a simple counter to
confirm the clock output is expected.
On 2/17/21 10:40 PM, Lizhi Hou wrote:
quoted
Add clock driver. Clock is a hardware function discovered by walking
xclbin metadata. A platform device node will be created for it. Other
part of driver configures clock through clock driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clock.h | 31 ++
drivers/fpga/xrt/lib/xleaf/clock.c | 648 +++++++++++++++++++++++++
2 files changed, 679 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clock.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clock.c
@@ -0,0 +1,648 @@+// SPDX-License-Identifier: GPL-2.0+/*+*XilinxAlveoFPGAClockWizardDriver+*+*Copyright(C)2020-2021Xilinx,Inc.+*+*Authors:+*LizhiHou<Lizhi.Hou@xilinx.com>+*SonalSantan<sonals@xilinx.com>+*DavidZhang<davidzha@xilinx.com>+*/++#include<linux/mod_devicetable.h>+#include<linux/platform_device.h>+#include<linux/delay.h>+#include<linux/device.h>+#include<linux/io.h>+#include"metadata.h"+#include"xleaf.h"+#include"xleaf/clock.h"+#include"xleaf/clkfreq.h"++/* CLOCK_MAX_NUM_CLOCKS should be a concept from XCLBIN_ in the future */+#define CLOCK_MAX_NUM_CLOCKS 4+#define OCL_CLKWIZ_STATUS_OFFSET 0x4
OCL_CLKWIZ does not match the name of this file, change to something like XRT_CLOCK
Add clock frequence counter driver. Clock frequence counter is
a hardware function discovered by walking xclbin metadata. A platform
device node will be created for it. Other part of driver can read the
actual clock frequence through clock frequence counter driver.
Signed-off-by: Sonal Santan <redacted>
Signed-off-by: Max Zhen <redacted>
Signed-off-by: Lizhi Hou <redacted>
---
drivers/fpga/xrt/include/xleaf/clkfreq.h | 23 +++
drivers/fpga/xrt/lib/xleaf/clkfreq.c | 221 +++++++++++++++++++++++
2 files changed, 244 insertions(+)
create mode 100644 drivers/fpga/xrt/include/xleaf/clkfreq.h
create mode 100644 drivers/fpga/xrt/lib/xleaf/clkfreq.c