diff --git a/acs-drv/files/MakefileBSA b/acs-drv/files/MakefileBSA index 0abf5ac685c3e00dabdbc22f49ab9e2a506dafae..b169898ae85a70b9737f8ddc0cb5c651aa7725c4 100755 --- a/acs-drv/files/MakefileBSA +++ b/acs-drv/files/MakefileBSA @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # - # Copyright (C) 2016-2018,2021, 2024, Arm Limited + # Copyright (C) 2016-2018,2021, 2024-2025, Arm Limited # ## @@ -25,9 +25,9 @@ ACS_DIR ?= ./ obj-m += bsa_acs.o bsa_acs-objs += bsa_acs_drv.o \ - bsa_acs_val.o \ - bsa_acs_test.o \ - bsa_acs_pal.o + val/bsa_acs_val.o \ + test_pool/bsa_acs_test.o \ + platform/pal_linux/files/bsa_acs_pal.o ccflags-y=-I$(PWD)/$(ACS_DIR)/include -I$(PWD)/$(ACS_DIR)/ -DTARGET_LINUX -Wall -Werror diff --git a/acs-drv/files/MakefileSBSA b/acs-drv/files/MakefileSBSA index 730639fdd0aa543f58c021188840dc6a6ce3a881..f42cdf0188f69d508a738b8ad8ba8bda0a818968 100644 --- a/acs-drv/files/MakefileSBSA +++ b/acs-drv/files/MakefileSBSA @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # - # Copyright (C) 2016-2018 Arm Limited + # Copyright (C) 2016-2018, 2025 Arm Limited # # Author: Prasanth Pulla # @@ -27,9 +27,9 @@ ACS_DIR ?= ./ obj-m += sbsa_acs.o sbsa_acs-objs += sbsa_acs_drv.o \ - sbsa_acs_val.o \ - sbsa_acs_test.o \ - sbsa_acs_pal.o + val/sbsa_acs_val.o \ + test_pool/sbsa_acs_test.o \ + platform/pal_linux/files/sbsa_acs_pal.o ccflags-y=-I$(PWD)/$(ACS_DIR)/include -I$(PWD)/$(ACS_DIR)/ -DTARGET_LINUX -Wall -Werror diff --git a/acs-drv/files/build.sh b/acs-drv/files/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..03a522a8bf54af11a1488921eb44494ddf84411e --- /dev/null +++ b/acs-drv/files/build.sh @@ -0,0 +1,380 @@ +#!/bin/bash + +WRK_DIR=$PWD + +export LINUX_KERNEL_VERSION=6.8 +export GCC_TOOLS_VERSION=13.2.rel1 +export GCC=tools/arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- +export BSA_PATH=$WRK_DIR/bsa-acs +export SBSA_PATH=$WRK_DIR/sbsa-acs +export BUILD_PATH=$WRK_DIR/build + +print_help() { + + echo "" + echo "================ ACS Build Environment Info ==================" + echo "" + echo "Working Directory : $WRK_DIR" + echo "BSA ACS Path : $BSA_PATH" + echo "SBSA ACS Path : $SBSA_PATH" + echo "Build Output Path : $BUILD_PATH" + echo "Linux Kernel Version : $LINUX_KERNEL_VERSION" + echo "GCC Tools Version : $GCC_TOOLS_VERSION" + echo "Cross Compiler PATH : $GCC" + echo "" + echo "==============================================================" + echo "" + echo "NOTE:" + echo "To change the compiler or Linux version used during the build," + echo "edit the corresponding variables at the top of this script." + echo "" + echo "Example:" + echo " export LINUX_KERNEL_VERSION=6.8" + echo " export GCC_TOOLS_VERSION=12.3.rel1" + echo "" + echo "Important:" + echo " The Linux kernel source and toolchain will only be cloned" + echo " if the machine architecture is NOT 'aarch64'." + echo "" + echo "Usage:" + echo "" + echo " ./build.sh Download all needed repos based on platform and build" + echo " ./build.sh --help Show this help message" + echo " ./build.sh --clean Build folder (modules and app)" + echo " ./build.sh --clean_all Remove all downloaded repos along with Build folder" + echo " ./build.sh --version The Linux Kernel Version value (Default value : 6.8) " + echo " ./build.sh --GCC_TOOLS The GCC Tools Version value (Default value : 13.2.rel1) " + echo "" +} + +check_repos_status() { + echo "" + echo "=========== ACS Repository Status ===========" + platform=$(uname -m) + echo "Platform: $platform" + echo "Note: On non-AArch64 platforms, Linux source and toolchain must be present." + + [[ -d "$BSA_PATH" ]] && echo "BSA repo - OK" || echo "BSA repo - Not present" + [[ -d "$SBSA_PATH" ]] && echo "SBSA repo - OK" || echo "SBSA repo - Not present" + [[ -d "$WRK_DIR/linux-acs" ]] && echo "linux acs repo - OK" || echo "linux acs repo - Not present" + [[ -d "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}" ]] && echo "Linux source - OK" || echo "Linux source - Not present" + [[ -d "$WRK_DIR/tools/arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-aarch64-none-linux-gnu" ]] && echo "Cross compiler - OK" || echo "Cross compiler - Not present" + + echo "=============================================" + echo "" +} + +clean_build_outputs() { + + if [[ "$1" == "--clean" ]]; then + echo "๐Ÿงน Cleaning build path: $BUILD_PATH" + rm -rf "$BUILD_PATH" + return + elif [[ "$1" == "--clean_all" ]]; then + echo "๐Ÿงน Cleaning everything under: $WRK_DIR" + rm -rf "$WRK_DIR/bsa-acs" + rm -rf "$WRK_DIR/sbsa-acs" + rm -rf "$WRK_DIR/linux-acs" + rm -rf "$WRK_DIR/build" + [[ -d "$WRK_DIR/tools" ]] && rm -rf "$WRK_DIR/tools" + [[ -d "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}" ]] && rm -rf "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}" + return + fi +} + +while [[ $# -gt 0 ]]; do + key="$1" + next="$2" # The next argument (should be a value) + + case $key in + -v|--version) + if [[ -z "$next" || "$next" == -* ]]; then + echo "โŒ Error: Missing or invalid value for $key" + exit 1 + fi + LINUX_KERNEL_VERSION="$next" + echo "Linux Kernel Version is set to $LINUX_KERNEL_VERSION" + shift 2 + ;; + -GCC_TOOLS|--GCC_TOOLS) + if [[ -z "$next" || "$next" == -* ]]; then + echo "โŒ Error: Missing or invalid value for $key" + exit 1 + fi + GCC_TOOLS_VERSION="$next" + echo "Gcc Tools Version is set to $GCC_TOOLS_VERSION" + shift 2 + ;; + --help) + print_help + exit 0 + ;; + --clean) + clean_build_outputs "$key" + exit 0 + ;; + --clean_all) + clean_build_outputs "$key" + exit 0 + ;; + *) + echo "โŒ Error: Unknown option $key" + exit 1 + ;; + esac +done + +get_bsa_acs() +{ + echo "Downloading BSA ACS" + + if [[ -d "$WRK_DIR/bsa-acs" ]]; then + echo "Directory '$WRK_DIR/bsa-acs' already exists. Skipping clone." + else + git clone https://github.com/ajayswar-s/bsa-acs.git bsa-acs + if [[ $? -ne 0 ]]; then + echo "ERROR: Failed to download BSA ACS repository." + return + fi + pushd $WRK_DIR/bsa-acs + git checkout linux_porting + popd + fi +} + +get_sbsa_acs() +{ + echo "Downloading SBSA ACS" + + if [[ -d "$WRK_DIR/sbsa-acs" ]]; then + echo "Directory '$WRK_DIR/sbsa-acs' already exists. Skipping clone." + else + git clone https://github.com/ajayswar-s/sbsa-acs.git sbsa-acs + if [[ $? -ne 0 ]]; then + echo "ERROR: Failed to download SBSA ACS repository." + return + fi + fi + +} + +get_linux_acs() +{ + echo "Downloading Linux ACS" + + if [[ -d "$WRK_DIR/linux-acs" ]]; then + echo "Directory '$WRK_DIR/linux-acs' already exists. Skipping clone." + else + git clone https://git.gitlab.arm.com/linux-arm/linux-acs.git linux-acs + if [[ $? -ne 0 ]]; then + echo "ERROR: Failed to download LINUX ACS repository." + return + fi + pushd $WRK_DIR/linux-acs + git checkout linux_porting + popd + fi +} + +get_linux_source() +{ + echo "Downloading Linux source" + if [ $(uname -m) != "aarch64" ]; then + if [[ -d "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}" ]]; then + echo "Directory '$WRK_DIR/sbsa-acs' already exists. Skipping clone." + else + git clone --depth 1 --branch v$LINUX_KERNEL_VERSION https://github.com/torvalds/linux.git linux-${LINUX_KERNEL_VERSION} + if [[ $? -ne 0 ]]; then + echo "ERROR: Failed to download LINUX ${LINUX_KERNEL_VERSION} VERSION repository." + return + fi + fi + else + echo "==================================================================" + echo "INFO: AARCH64 native build, No Linux Source code needed" + echo "==================================================================" + fi + +} + +get_cross_compiler() +{ + + echo "Downloading cross compiler. Version : ${GCC_TOOLS_VERSION}" + if [ $(uname -m) == "aarch64" ]; then + echo "==================================================================" + echo "aarch64 native build" + echo "WARNING: no cross compiler needed, GCC version recommended: ${GCC_TOOLS_VERSION}" + echo "==================================================================" + else + TAG=aarch64-none-linux-gnu + if [[ -d "$WRK_DIR/tools/arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-aarch64-none-linux-gnu" ]]; then + echo "Directory '$WRK_DIR/tools/arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-aarch64-none-linux-gnu' already exists. Skipping clone." + else + mkdir -p tools + pushd $WRK_DIR/tools + wget https://developer.arm.com/-/media/Files/downloads/gnu/${GCC_TOOLS_VERSION}/binrel/arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-aarch64-none-linux-gnu.tar.xz --no-check-certificate + if [[ $? -ne 0 ]]; then + echo "ERROR: Failed to download CROSS COMPILER ${GCC_TOOLS_VERSION}" + return + fi + + tar -xf arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-${TAG}.tar.xz + mv arm-gnu-toolchain-13.2.Rel1-x86_64-aarch64-none-linux-gnu arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-linux-gnu + rm arm-gnu-toolchain-${GCC_TOOLS_VERSION}-x86_64-${TAG}.tar.xz + popd + fi + fi +} + +do_linux_build () +{ + export ARCH=arm64 + export LINUX_OUT_DIR=out + + if [[ -d "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}/$LINUX_OUT_DIR" ]]; then + echo "Output directory '$WRK_DIR/linux-${LINUX_KERNEL_VERSION}/$LINUX_OUT_DIR' already exists." + read -p "Do you want to remove it and rebuild? [y/N]: " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + echo "Removing $LINUX_OUT_DIR..." + rm -rf "$WRK_DIR/linux-${LINUX_KERNEL_VERSION}/$LINUX_OUT_DIR" + else + echo "Skipping Linux build." + return + fi + fi + + pushd $WRK_DIR/linux-${LINUX_KERNEL_VERSION} + mkdir -p $LINUX_OUT_DIR + echo "Building using defconfig..." + cp arch/arm64/configs/defconfig $LINUX_OUT_DIR/.config + + make ARCH=arm64 CROSS_COMPILE=$WRK_DIR/$GCC O=$LINUX_OUT_DIR olddefconfig + make ARCH=arm64 CROSS_COMPILE=$WRK_DIR/$GCC O=$LINUX_OUT_DIR -j$(nproc) + + popd +} + +check_and_install_kernel_headers() { + export KERNEL_SRC="/lib/modules/$(uname -r)/build" + + if [[ -d "$KERNEL_SRC" ]]; then + echo "Kernel headers are present at $KERNEL_SRC" + else + echo "Kernel headers not found for kernel version $(uname -r)" + echo "Attempting to install kernel headers..." + if ! sudo apt-get install -y linux-headers-$(uname -r); then + echo "ERROR: Failed to install Linux kernel headers." + return + else + echo "Successfully installed Linux kernel headers for $(uname -r)." + fi + fi +} + + +do_build_bsa_module() +{ + echo "Building BSA Module....." + mkdir -p $BUILD_PATH + + pushd $WRK_DIR/linux-acs/acs-drv/files + + if [ $(uname -m) != "aarch64" ]; then + export KERNEL_SRC=$WRK_DIR/linux-${LINUX_KERNEL_VERSION}/out + export CROSS_COMPILE=$WRK_DIR/$GCC + + else + check_and_install_kernel_headers + export KERNEL_SRC="/lib/modules/$(uname -r)/build" + fi + + ./bsa_setup.sh $BSA_PATH + ./linux_bsa_acs.sh + cp bsa_acs.ko $BUILD_PATH/ + popd +} + +do_build_sbsa_module() +{ + echo "Building SBSA Module....." + mkdir -p $BUILD_PATH + + pushd $WRK_DIR/linux-acs/acs-drv/files + + if [ $(uname -m) != "aarch64" ]; then + export KERNEL_SRC=$WRK_DIR/linux-${LINUX_KERNEL_VERSION}/out + export CROSS_COMPILE=$WRK_DIR/$GCC + else + check_and_install_kernel_headers + export KERNEL_SRC="/lib/modules/$(uname -r)/build" + fi + + ./sbsa_setup.sh $BSA_PATH $SBSA_PATH + ./linux_sbsa_acs.sh + cp sbsa_acs.ko $BUILD_PATH + + popd +} + +do_build_sbsa_app() +{ + pushd $SBSA_PATH/linux_app/sbsa-acs-app + make + cp sbsa $BUILD_PATH/sbsa_app + popd +} + +do_build_bsa_app() +{ + pushd $BSA_PATH/linux_app/bsa-acs-app + make + cp bsa $BUILD_PATH/bsa_app + popd +} + +check_build_outputs() { + # Check for output files in $BUILD_PATH/build/ + check_file() { + local file="$1" + if [[ -f "$BUILD_PATH/$file" ]]; then + echo "ok" + else + echo "fail" + fi + } + + # Run checks + local bsa_module=$(check_file "bsa_acs.ko") + local bsa_app=$(check_file "bsa_app") + local sbsa_module=$(check_file "sbsa_acs.ko") + local sbsa_app=$(check_file "sbsa_app") + + # Print status table + echo "----------------------------------------" + printf "%-6s %-10s %-10s\n" "Repo" "Module " "App" + echo "----------------------------------------" + printf "%-6s %-10s %-10s\n" "BSA" "$bsa_module" "$bsa_app" + printf "%-6s %-10s %-10s\n" "SBSA" "$sbsa_module" "$sbsa_app" + echo "----------------------------------------" +} + +get_bsa_acs +get_sbsa_acs +get_linux_acs +get_linux_source +get_cross_compiler + +check_repos_status + + +if [ $(uname -m) != "aarch64" ]; then + do_linux_build +fi + +do_build_bsa_module +do_build_sbsa_module +do_build_bsa_app +do_build_sbsa_app + +check_build_outputs diff --git a/acs-drv/files/platform/pal_linux/files/MakefileBSA b/acs-drv/files/platform/pal_linux/files/MakefileBSA index 59fc068bebae55563ef474a4208bba5277f8c028..9b7115b430f7453c3a07d92d7986531423463279 100644 --- a/acs-drv/files/platform/pal_linux/files/MakefileBSA +++ b/acs-drv/files/platform/pal_linux/files/MakefileBSA @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # - # Copyright (C) 2016-2018, 2021 Arm Limited + # Copyright (C) 2016-2018, 2021, 2025, Arm Limited # ## @@ -30,7 +30,7 @@ bsa_acs_pal-objs += $(COMMON_PAL_SRC)/pal_misc.o \ $(COMMON_PAL_SRC)/pal_pe.o $(COMMON_PAL_SRC)/pal_pcie.o $(COMMON_PAL_SRC)/pal_pcie_enumeration.o \ $(COMMON_PAL_SRC)/pal_smmu.o $(COMMON_PAL_SRC)/pal_iovirt.o $(COMMON_PAL_SRC)/pal_peripherals.o \ $(COMMON_PAL_SRC)/pal_dma.o $(COMMON_PAL_SRC)/pal_acpi.o $(COMMON_PAL_SRC)/pal_gic.o \ - $(BSA_PAL_SRC)/bsa_pal_dt.o $(BSA_PAL_SRC)/bsa_pal_acpi.o $(BSA_PAL_SRC)/bsa_pal_exerciser.o \ + $(BSA_PAL_SRC)/bsa_pal_dt.o $(BSA_PAL_SRC)/bsa_pal_acpi.o $(BSA_PAL_SRC)/bsa_pal_exerciser.o $(COMMON_PAL_SRC)/bsa-dma-iommu.o \ $(COMMON_PAL_SRC)/pal_exerciser.o ccflags-y=-I$(PWD)/$(ACS_DIR)/val/common/include -I$(PWD)/$(ACS_DIR)/val/bsa/include -I$(PWD)/$(ACS_DIR)/common/include -I$(PWD)/$(ACS_DIR)/bsa/include -I$(PWD) -I$(PWD)/../../ -I$(PWD)/../../../ -DTARGET_LINUX -Wall -Werror diff --git a/acs-drv/files/platform/pal_linux/files/MakefileSBSA b/acs-drv/files/platform/pal_linux/files/MakefileSBSA index 95500ba79f86c39496b69a6d57afa7cde742b65a..6f1a6bce621153b939af3e824b45bd2f6d5214f4 100644 --- a/acs-drv/files/platform/pal_linux/files/MakefileSBSA +++ b/acs-drv/files/platform/pal_linux/files/MakefileSBSA @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # - # Copyright (C) 2016-2018 Arm Limited + # Copyright (C) 2016-2018, 2025, Arm Limited # ## @@ -29,7 +29,7 @@ obj-m += sbsa_acs_pal.o sbsa_acs_pal-objs += $(COMMON_PAL_SRC)/pal_misc.o \ $(COMMON_PAL_SRC)/pal_pe.o $(COMMON_PAL_SRC)/pal_pcie.o $(COMMON_PAL_SRC)/pal_pcie_enumeration.o \ $(COMMON_PAL_SRC)/pal_smmu.o $(COMMON_PAL_SRC)/pal_iovirt.o $(COMMON_PAL_SRC)/pal_peripherals.o \ - $(COMMON_PAL_SRC)/pal_dma.o $(COMMON_PAL_SRC)/pal_acpi.o $(COMMON_PAL_SRC)/pal_gic.o \ + $(COMMON_PAL_SRC)/pal_dma.o $(COMMON_PAL_SRC)/pal_acpi.o $(COMMON_PAL_SRC)/pal_gic.o $(COMMON_PAL_SRC)/bsa-dma-iommu.o \ $(SBSA_PAL_SRC)/sbsa_pal_iovirt.o $(COMMON_PAL_SRC)/pal_exerciser.o $(SBSA_PAL_SRC)/sbsa_pal_pcie.o ccflags-y=-I$(PWD)/$(ACS_DIR)/val/include -I$(PWD)/$(ACS_DIR)/include -I$(PWD) -I$(PWD)/../../ -I$(PWD)/../../../ -DTARGET_LINUX -DBUILD_SBSA -Wall -Werror diff --git a/acs-drv/files/platform/pal_linux/files/common/include/bsa-iommu.h b/acs-drv/files/platform/pal_linux/files/common/include/bsa-iommu.h new file mode 100644 index 0000000000000000000000000000000000000000..964cea92c97a4abcb686ef80804dc0f63abeb8bd --- /dev/null +++ b/acs-drv/files/platform/pal_linux/files/common/include/bsa-iommu.h @@ -0,0 +1,114 @@ +/* + * The IOMMU-API to BSA Architecture Compliance Suite glue layer. + * + * Copyright (C) 2021, 2025, ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __BSA_IOMMU_H +#define __BSA_IOMMU_H + +#ifdef CONFIG_IOMMU_DMA +#include + +#include +#include +#include + +#define BSA_IOVA_DMA_ARRAY_LEN 4 + +typedef struct _bsa_iova_array_ { + dma_addr_t dma_addr; + unsigned int dma_len; +}bsa_iova_array; + +void *bsa_iommu_dma_get_iova(struct device *dev, unsigned long long *base, unsigned long int *size, + phys_addr_t *phy_addr, void *in_node); + +void bsa_iommu_dev_start_monitor(struct device *dev); +struct irq_domain* acs_get_irq_domain(void); +void bsa_iommu_dev_stop_monitor(struct device *dev); +int bsa_is_domain_monitored(struct iommu_domain *dom); + +void bsa_iommu_iova_save_addr(dma_addr_t addr, unsigned int length); + +unsigned int bsa_iommu_iova_get_addr(unsigned int index, dma_addr_t *addr); + +enum dev_dma_attr bsa_dev_get_dma_attr(struct device *dev); + + + +struct iova_domain *cookie_iovad_bsa(struct iommu_domain *domain); +//dma_addr_t bsa_dma_addr; +int bsa_scsi_sata_get_dma_addr(struct ata_port *ap, dma_addr_t *dma_addr, unsigned int *dma_len); + +void bsa_scsi_sata_fill_dma_addr(struct scatterlist *sg); + +struct scatterlist *dma_address_store_and_print(uint64_t j, struct ata_port *q); + +pgd_t *read_pgd(uint64_t addr); + +enum iommu_dma_cookie_type { + IOMMU_DMA_IOVA_COOKIE, + IOMMU_DMA_MSI_COOKIE, +}; + + +enum iommu_dma_queue_type { + IOMMU_DMA_OPTS_PER_CPU_QUEUE, + IOMMU_DMA_OPTS_SINGLE_QUEUE, +}; + +struct iommu_dma_options { + enum iommu_dma_queue_type qt; + size_t fq_size; + unsigned int fq_timeout; +}; + + +struct iommu_dma_cookie_copy { + enum iommu_dma_cookie_type type; + union { + /* Full allocator for IOMMU_DMA_IOVA_COOKIE */ + struct { + struct iova_domain iovad; + /* Flush queue */ + union { + struct iova_fq *single_fq; + struct iova_fq __percpu *percpu_fq; + }; + /* Number of TLB flushes that have been started */ + atomic64_t fq_flush_start_cnt; + /* Number of TLB flushes that have been finished */ + atomic64_t fq_flush_finish_cnt; + /* Timer to regularily empty the flush queues */ + struct timer_list fq_timer; + /* 1 when timer is active, 0 when not */ + atomic_t fq_timer_on; + }; + /* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */ + dma_addr_t msi_iova; + }; + struct list_head msi_page_list; + + /* Domain for flush queue callback; NULL if flush queue not in use */ + struct iommu_domain *fq_domain; + /* Options for dma-iommu use */ + struct iommu_dma_options options; + struct mutex mutex; +}; + +#endif +#endif + diff --git a/acs-drv/files/platform/pal_linux/files/common/src/bsa-dma-iommu.c b/acs-drv/files/platform/pal_linux/files/common/src/bsa-dma-iommu.c new file mode 100644 index 0000000000000000000000000000000000000000..62ad9e92d71869566e9cf7fb8314501755203fc5 --- /dev/null +++ b/acs-drv/files/platform/pal_linux/files/common/src/bsa-dma-iommu.c @@ -0,0 +1,248 @@ +/* + * The IOMMU-API to BSA Architecture Compliance Suite glue layer. + * + * Copyright (C) 2021, 2025, ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include "common/include/bsa-iommu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* +struct irq_domain *acs_irq_domain = NULL; + +struct irq_domain* acs_get_irq_domain(void) +{ + if (acs_irq_domain) + return acs_irq_domain; + return 0; +} +EXPORT_SYMBOL(acs_get_irq_domain); +*/ + +struct iova_domain *cookie_iovad_bsa(struct iommu_domain *domain) +{ + if (domain) + return &((struct iommu_dma_cookie_copy *)domain->iova_cookie)->iovad; + return NULL; +} + +bsa_iova_array g_bsa_iova_array[BSA_IOVA_DMA_ARRAY_LEN]; + +struct iommu_domain *g_bsa_iommu_domain = NULL; + + +/** + @brief This API returns the DMA address for a given index from the active IOVA table. + 1. Caller - Platform Abstraction Layer. + 2. Prerequisite - None + @param dev - device whose domain IOVA table is checked + @param base - return DMA address base + @param in_node - return first entry if NULL, else continue from in_node + @return length of the DMA range +**/ +void * +bsa_iommu_dma_get_iova(struct device *dev, unsigned long long *base, unsigned long int *size, + phys_addr_t *phy_addr, void *in_node) +{ + struct iova_domain *iovad = cookie_iovad_bsa(iommu_get_domain_for_dev(dev)); + unsigned long shift; + struct rb_node *node; + + if(!iovad) + return NULL; + + shift = iova_shift(iovad); + + if (in_node == NULL) + node = rb_last(&iovad->rbroot); + else + node = rb_prev((struct rb_node *)in_node); + + while (node) + { + struct iova *iova = container_of(node, struct iova, node); + unsigned long pfn_lo = iova->pfn_lo; + unsigned long pfn_hi = iova->pfn_hi + 1; + phys_addr_t phys = iommu_iova_to_phys(iommu_get_domain_for_dev(dev), + pfn_lo << shift); + + if (phys) { + *base = pfn_lo << shift; + *size = (pfn_hi - pfn_lo) << shift; + *phy_addr = phys; + break; + } + node = rb_prev(node); + } + return (void *)node; +} + +static int bsa_get_sata_dev(void) +{ + int ret = -1; + struct Scsi_Host *shost; + struct ata_port *ap; + struct scsi_device *sdev = NULL; + unsigned int i = 0; + + do { + shost = scsi_host_lookup(i++); + if (shost) { + sdev = NULL; + ap = ata_shost_to_port(shost); + if ((ap == NULL) || (ap->dev == NULL)) + continue; //Not a ATA port + if ((ap->scsi_host == NULL) || (ap->scsi_host != shost)) + continue; //Not a valid ATA Port + do { + /* get the device connected to this host */ + sdev = __scsi_iterate_devices(shost, sdev); + if (sdev) { + g_bsa_iommu_domain = iommu_get_domain_for_dev(ap->dev); + ret = 0; + } + } while(sdev); + scsi_host_put(shost); + } + } while(shost); + + return ret; +} + +/** + @brief This API is used to indicate which device IOMMU transactions are to be monitored. + 1. Caller - Platform Abstraction Layer. + 2. Prerequisite - None + @param dev - device whose domain IOVA table is checked + @return None +**/ +void +bsa_iommu_dev_start_monitor(struct device *dev) +{ + /* We only monitor 1 domain for now */ + + g_bsa_iommu_domain = iommu_get_domain_for_dev(dev); +} + +void +bsa_iommu_dev_stop_monitor(struct device *dev) +{ + g_bsa_iommu_domain = NULL; +} + +int +bsa_is_domain_monitored(struct iommu_domain *dom) +{ + int ret = 0; + + if (g_bsa_iommu_domain == NULL ) + ret = bsa_get_sata_dev(); + + if (g_bsa_iommu_domain == dom) + return 1; + + return 0; +} + +void +bsa_iommu_iova_save_addr(dma_addr_t addr, unsigned int length) +{ + + static unsigned int index = 0; + + g_bsa_iova_array[index].dma_addr = addr; + g_bsa_iova_array[index].dma_len = length; + + index++; + + if (index >= BSA_IOVA_DMA_ARRAY_LEN) + index = 0; +} + +/** + @brief This API returns the DMA address for a given index from the saved IOVA addresses. + 1. Caller - Platform Abstraction Layer. + 2. Prerequisite - bsa_iommu_iova_save_addr. + @param index - index of the entry + @param addr - dma address returned + @return length of the DMA range +**/ +unsigned int +bsa_iommu_iova_get_addr(unsigned int index, dma_addr_t *addr) +{ + + if (index >= BSA_IOVA_DMA_ARRAY_LEN) + return 0; + + *addr = g_bsa_iova_array[index].dma_addr; + + return g_bsa_iova_array[index].dma_len; + +} + +/** + @brief This API gets the DMA attributes of a device. + 1. Caller - Platform Abstraction Layer. + 2. Prerequisite - None. + @param dev - device structure. + @return Coherent or Not. +**/ +enum +dev_dma_attr bsa_dev_get_dma_attr(struct device *dev) +{ + + return device_get_dma_attr(dev); +} + +/* BSA ACS Hook functions to export the DMA address used by the controller */ +dma_addr_t bsa_dma_addr; +unsigned int bsa_dma_len; + +int +bsa_scsi_sata_get_dma_addr(struct ata_port *ap, dma_addr_t *dma_addr, unsigned int *dma_len) +{ + //Not used struct sil24_port_priv *pp = ap->private_data; + + *dma_addr = bsa_dma_addr; + *dma_len = bsa_dma_len; + + return 0; +} + +void +bsa_scsi_sata_fill_dma_addr(struct scatterlist *sg) +{ + + bsa_dma_addr = cpu_to_le64(sg_dma_address(sg)); + bsa_dma_len = cpu_to_le32(sg_dma_len(sg)); +} + diff --git a/acs-drv/files/platform/pal_linux/files/common/src/pal_dma.c b/acs-drv/files/platform/pal_linux/files/common/src/pal_dma.c index 737cdbfd7bc8178bede1cff18d6d32de64774328..c37c78e4d8f9064d70b3e8f245765dd071a8eeb9 100644 --- a/acs-drv/files/platform/pal_linux/files/common/src/pal_dma.c +++ b/acs-drv/files/platform/pal_linux/files/common/src/pal_dma.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Copyright (C) 2016-2018,2021,2023-2024, Arm Limited + * Copyright (C) 2016-2018,2021,2023-2025, Arm Limited * * Author: Prasanth Pulla * @@ -48,7 +48,7 @@ #include "common/include/pal_linux.h" #include "common/include/pal_pcie_enum.h" #include -#include +#include "common/include/bsa-iommu.h" int bsa_scsi_sata_get_dma_addr(struct ata_port *ap, dma_addr_t *dma_addr, unsigned int *dma_len); @@ -57,7 +57,7 @@ bsa_scsi_sata_get_dma_addr(struct ata_port *ap, dma_addr_t *dma_addr, unsigned i unsigned long long int pal_dma_mem_alloc(void **buffer, unsigned int length, void *port, unsigned int flags) { - dma_addr_t mem_dma; + dma_addr_t mem_dma = 0; if (flags == DMA_COHERENT) { *buffer = dmam_alloc_coherent(((struct ata_port *)port)->dev, length, &mem_dma, GFP_KERNEL); @@ -74,6 +74,7 @@ pal_dma_mem_alloc(void **buffer, unsigned int length, void *port, unsigned int f return -1; } } + memset(*buffer, 0, length); return mem_dma; @@ -99,54 +100,139 @@ pal_dma_mem_free(void *buffer, addr_t mem_dma, unsigned int length, void *port, } +struct scatterlist *dma_address_store_and_print(uint64_t j, struct ata_port *q) +{ + struct ata_queued_cmd *cmd; + struct scatterlist *sg = NULL; + int i; + + if (q == NULL) { + printk(KERN_INFO "\n Ata Port address Invalid : %d", (int) j); + return sg; + } + + printk(KERN_INFO "\n DMA index: %d ", (int) j); + printk(KERN_INFO "\n ATA_MAX_QUEUE : %d ", ATA_MAX_QUEUE); + + for (i = 0; i <= ATA_MAX_QUEUE; i++) { + + printk(KERN_INFO "\n Ata command queue index: %d ", i); + cmd = &q->qcmd[i]; + + if (cmd == NULL) { + printk(KERN_INFO "\n Ata command address Invalid "); + continue; + } + + if (cmd->ap != q) { + printk(KERN_INFO "\n Ata_port in command is not as same as the given ata port "); + continue; + } + + // Check if the command is active or in a valid state + if (cmd->flags & ATA_QCFLAG_ACTIVE) + printk(KERN_INFO "\n Command %d is active", i); + + // Optionally, check additional flags here + if (cmd->flags & ATA_QCFLAG_DMAMAP) + printk(KERN_INFO "\n Command %d is DMA mapped", i); + + if (cmd->flags & ATA_QCFLAG_IO) + printk(KERN_INFO "\n Command %d is an I/O command", i); + + sg = cmd->sg; + + if (sg == NULL) { + printk(KERN_INFO "\n Ata command scatterlist address NULL"); + continue; + } + + // Validate each entry + if (sg->length == 0 || sg_page(sg) == NULL || sg_dma_address(sg) == 0) { + printk(KERN_INFO "\n Invalid scatterlist entry at index %d", i); + continue; // Skip invalid entry + } + else { + // If valid, print the DMA address + pr_info("\n Valid scatterlist entry at index %d: DMA address = %llx, ", + i, (unsigned long long)cpu_to_le64(sg_dma_address(sg))); + pr_info("\n DMA size = %llx",(unsigned long long)cpu_to_le32(sg_dma_len(sg))); + return sg; + } + } + return NULL; +} void pal_dma_create_info_table(DMA_INFO_TABLE *dma_info_table) { - struct Scsi_Host *shost; - struct ata_port *ap; - struct scsi_device *sdev = NULL; - unsigned int i = 0, j = 0; - - dma_info_table->num_dma_ctrls = 0; - - do { - shost = scsi_host_lookup(i++); - if (shost) { - sdev = NULL; - ap = ata_shost_to_port(shost); - if ((ap == NULL) || (ap->dev == NULL)) - continue; //Not a ATA port - if ((ap->scsi_host == NULL) || (ap->scsi_host != shost)) - continue; //Not a valid ATA Port - do { - /* get the device connected to this host */ - sdev = __scsi_iterate_devices(shost, sdev); - if (sdev) { - dma_info_table->info[j].host = shost; - dma_info_table->info[j].port = ap; - dma_info_table->info[j].target = sdev; - dma_info_table->info[j].flags = bsa_dev_get_dma_attr(shost->dma_dev); - - /* if we did not get coherence attribute from ACPI/PCI, get it from FDT */ - if (dma_info_table->info[j].flags == 0) { + struct Scsi_Host *shost; + struct ata_port *ap; + struct scsi_device *sdev = NULL; + struct scatterlist *sg; + unsigned int i = 0, j = 0; + + dma_info_table->num_dma_ctrls = 0; + + do { + printk(KERN_INFO "\n SCSI host lookup index: %d ",(int) i); + shost = scsi_host_lookup(i++); + if (shost) { + sdev = NULL; + ap = ata_shost_to_port(shost); + if ((ap == NULL) || (ap->dev == NULL)) { + printk(KERN_INFO "\n Invalid Ataport for scsi index: %d ",(int) i); + continue; //Not a ATA port + } + + if ((ap->scsi_host == NULL) || (ap->scsi_host != shost)) { + printk(KERN_INFO "\n Invalid Ataport for scsi index: %d ",(int) i); + continue; //Not a valid ATA Port + } + + do { + /* get the device connected to this host */ + sdev = __scsi_iterate_devices(shost, sdev); + if (sdev) { + dma_info_table->info[j].host = shost; + dma_info_table->info[j].port = ap; + dma_info_table->info[j].target = sdev; + dma_info_table->info[j].flags = bsa_dev_get_dma_attr(shost->dma_dev); + + /* if we did not get coherence attribute from ACPI/PCI, get it from FDT */ + if (dma_info_table->info[j].flags == 0) { #if LINUX_VERSION_CODE > KERNEL_VERSION(4,19,0) - dma_info_table->info[j].flags = shost->dma_dev->dma_coherent; + dma_info_table->info[j].flags = shost->dma_dev->dma_coherent; #else - dma_info_table->info[j].flags = shost->dma_dev->archdata.dma_coherent; + dma_info_table->info[j].flags = shost->dma_dev->archdata.dma_coherent; #endif - } - if (pal_smmu_check_dev_attach(ap->dev)) - dma_info_table->info[j].flags |= IOMMU_ATTACHED; - dma_info_table->info[j++].type = sdev->type; - dma_info_table->num_dma_ctrls++; - } - } while(sdev); - scsi_host_put(shost); - } - } while(shost); - + } + if (pal_smmu_check_dev_attach(ap->dev)) { + printk(KERN_INFO "\n Iommu Attached for dma index %d ", (int) j); + dma_info_table->info[j].flags |= IOMMU_ATTACHED; + } + else + printk(KERN_INFO "\n Iommu Not Attached for dma index %d \n", (int) j); + + sg = dma_address_store_and_print(j, ap); + if (sg != NULL) { + dma_info_table->info[j].dma_sg_address = cpu_to_le64(sg_dma_address(sg)); + dma_info_table->info[j].dma_sg_length = cpu_to_le32(sg_dma_len(sg)); + } + else { + dma_info_table->info[j].dma_sg_address = 0; + dma_info_table->info[j].dma_sg_length = 0; + } + printk(KERN_INFO "\n Saved DMA sg address %llx ", (uint64_t) dma_info_table->info[j].dma_sg_address); + printk(KERN_INFO "\n Saved DMA sg length %llx ", (uint64_t) dma_info_table->info[j].dma_sg_length); + dma_info_table->info[j++].type = sdev->type; + dma_info_table->num_dma_ctrls++; + } + } while(sdev); + scsi_host_put(shost); + } + } while(shost); } unsigned int @@ -204,6 +290,28 @@ decode_mem_attr_sh(uint64_t val, uint32_t *attr, uint32_t *sh) pr_info("In decode_mem_attr_sh with attr=%x, sh=%x\n", *attr, *sh); } + +pgd_t *read_pgd(uint64_t addr) +{ + uint64_t ttbr1_value = read_sysreg(ttbr1_el1); //Arm Register TTBR1_EL1 + uint64_t baddr = (uint64_t) (ttbr1_value & ~0xFFFF000000000001); + uint64_t virt_addr = addr; + + pgd_t *pgd_entry = (pgd_t *)phys_to_virt(baddr) + pgd_index(addr); + + + printk(KERN_INFO " virt_addr : %016llx \n", virt_addr); + printk(KERN_INFO " BADDR : %016llx \n", baddr); + printk(KERN_INFO " Align_baddr phys_to_virt : %016llx \n", (uint64_t)phys_to_virt(baddr)); + printk(KERN_INFO " pgd_entry : %016llx \n", (uint64_t)pgd_entry); + printk(KERN_INFO " pgd_entry->pgd : %016llx \n", (uint64_t)pgd_entry->pgd); + printk(KERN_INFO " pgd_index : %016llx\n", (uint64_t)pgd_index(addr)); + + return pgd_entry; +} + + + int pal_dma_mem_get_attrs(void *buf, uint32_t *attr, uint32_t *sh) { @@ -212,8 +320,11 @@ pal_dma_mem_get_attrs(void *buf, uint32_t *attr, uint32_t *sh) pte_t *pte; pgd_t *pgd; - pgd = pgd_offset_k((uint64_t)buf); - if(!pgd) + acs_print(ACS_PRINT_DEBUG, "DEBUG : Adddress of the DMA attribute sent : 0x%llx\n", (uint64_t)buf); + + pgd = read_pgd((uint64_t)buf); + + if(!pgd || pgd->pgd == 0) //Found this issue in some platforms return -1; acs_print(ACS_PRINT_DEBUG, "DEBUG : pgd from pgd_offset_k: 0x%llx\n", (uint64_t)(pgd->pgd)); diff --git a/acs-drv/files/platform/pal_linux/files/common/src/pal_gic.c b/acs-drv/files/platform/pal_linux/files/common/src/pal_gic.c index 1a02d4d3a2a277cc5d380e9b17d2dc1100ad8515..25ad719e351ab194866720113e78e3fbb81497d2 100644 --- a/acs-drv/files/platform/pal_linux/files/common/src/pal_gic.c +++ b/acs-drv/files/platform/pal_linux/files/common/src/pal_gic.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Copyright (C) 2016-2021 Arm Limited + * Copyright (C) 2016-2021, 2025, Arm Limited * */ @@ -22,63 +22,11 @@ #include #include #include +#include "common/include/bsa-iommu.h" unsigned int pal_gic_install_isr(unsigned int int_id, void (*isr)(void)) { - unsigned long long flags = 0; - unsigned int ret = 0; - unsigned int virq; - struct irq_domain *domain = NULL; - struct irq_fwspec *fwspec; - - domain = acs_get_irq_domain(); - if (!domain) { - acs_print(ACS_PRINT_ERR, "\n Domain is null", 0); - return 1; - } - - fwspec = kmalloc(sizeof(struct irq_fwspec), GFP_KERNEL); - if (!fwspec) { - acs_print(ACS_PRINT_ERR, "\n Kmalloc failed", 0); - return 1; - } - - fwspec->param_count = 2; - fwspec->param[0] = int_id; - /* Interrupt type LEVEL 0 EDGE RISING 1 */ - fwspec->param[1] = 0; - fwspec->param[2] = 0; - fwspec->fwnode = domain->fwnode; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) - fwspec->fwnode->type = FWNODE_IRQCHIP; -#endif - - if (irq_domain_is_hierarchy(domain)) { - virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec); - if (virq <= 0) { - ret = 1; - goto error; - } - } else { - /* Create mapping */ - virq = irq_create_mapping(domain, int_id); - if (!virq) { - ret = 1; - goto error; - } - } - - ret = request_irq(virq, (irq_handler_t)isr, flags, "ACS", NULL); - if (ret != 0) { - acs_print(ACS_PRINT_ERR, "\n IRQ registration failure %x", int_id); - acs_print(ACS_PRINT_ERR, " \n err %d", ret); - ret = 1; - goto error; - } - -error: - kfree(fwspec); - return ret; + return 0; } void pal_gic_end_of_interrupt(unsigned int int_id) diff --git a/acs-drv/files/platform/pal_linux/files/common/src/pal_pcie.c b/acs-drv/files/platform/pal_linux/files/common/src/pal_pcie.c index 6dfa174ecf3cfa388d7ed5f092fc9859e4343a44..cc74bdc9b1e4b25f506c0d16241a39f918edfa61 100644 --- a/acs-drv/files/platform/pal_linux/files/common/src/pal_pcie.c +++ b/acs-drv/files/platform/pal_linux/files/common/src/pal_pcie.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Copyright (C) 2016-2024, Arm Limited + * Copyright (C) 2016-2025, Arm Limited * * Author: Prasanth Pulla * Daniil Egranov diff --git a/acs-drv/files/platform/pal_linux/files/common/src/pal_smmu.c b/acs-drv/files/platform/pal_linux/files/common/src/pal_smmu.c index ec441944f2f3d22869f98a0437c8751922f5376f..09776ed0311de2ae4762d682a8a06d5ce8489229 100644 --- a/acs-drv/files/platform/pal_linux/files/common/src/pal_smmu.c +++ b/acs-drv/files/platform/pal_linux/files/common/src/pal_smmu.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Copyright (C) 2016-2019, 2021, 2023-2024, Arm Limited + * Copyright (C) 2016-2019, 2021, 2023-2025, Arm Limited * * Author: Prasanth Pulla * @@ -22,10 +22,10 @@ #include #include #include -#include #include #include "common/include/pal_linux.h" +#include "common/include/bsa-iommu.h" #define SMMU_V3_IDR1 0x4 #define SMMU_V3_IDR1_PASID_SHIFT 6 @@ -72,41 +72,32 @@ pal_smmu_device_stop_monitor_iova(void *port) unsigned int pal_smmu_check_device_iova(void *port, unsigned long long dma_addr) { - void *curr_node = NULL; - unsigned int index = 0; - unsigned long long base; - unsigned long int size; - phys_addr_t phys; + struct device *device = ((struct ata_port *)port)->dev; + struct iommu_domain *domain; + phys_addr_t phy_addr; if (!pal_smmu_check_dev_attach(((struct ata_port *)port)->dev)) { acs_print(ACS_PRINT_WARN, "\n This device is not behind an SMMU ", 0); return PAL_LINUX_SKIP; } - /* Check if this address was used in the last few transactions of the IOMMU layer */ - - do { - size = bsa_iommu_iova_get_addr(index, &base); - if (size) { - if ((dma_addr >= base) && (dma_addr < (base + size))) { - return PAL_LINUX_SUCCESS; - } - - index++; - } - - } while(size); - - /* Did not find it above - Check the active IOVA table entries now */ - do { - curr_node = bsa_iommu_dma_get_iova(((struct ata_port *)port)->dev, &base, &size, &phys, curr_node); - if (curr_node) { - pr_info("Device IOVA entry is %llx size = %lx phys = %llx \n", base, size, phys); - if ((dma_addr >= base) && (dma_addr < (base + size))) { - return PAL_LINUX_SUCCESS; - } - } - } while(curr_node); + /* Get the IOMMU domain for the device */ + domain = iommu_get_domain_for_dev(device); + if (!domain) { + pr_err("Failed to get IOMMU domain for device\n"); + return PAL_LINUX_SKIP; + } + + /* Use iommu_iova_to_phys to map IOMMU address to physical address */ + phy_addr = iommu_iova_to_phys(domain, dma_addr); + if (!phy_addr) { + pr_err("Failed to translate IOMMU address to physical address\n"); + return PAL_LINUX_ERR; + } + else { + pr_info("DMA Address: %llx converted to phy addess: %llx", dma_addr, phy_addr); + return PAL_LINUX_SUCCESS; + } return PAL_LINUX_ERR; }