├── .gitignore ├── README.md ├── build ├── bulletin.xml ├── component │ └── VMW-esx-7.0.1-thpimon-1.0-0.00001.zip ├── sverify │ └── thpimon.sva ├── thpimon ├── thpimon-nostrip ├── thpimon.map └── vib │ └── thpimon-0.1.0-1OEM.701.1.0.40650718.aarch64.vib ├── pimon.h ├── pimon_charDev.c ├── pimon_charDev.h ├── pimon_os.c ├── pimon_types.h ├── pyUtil ├── pimonLib │ └── __init__.py └── pimon_util.py ├── rpiq_drv.c └── rpiq_drv.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | *.sc 3 | pimon_acpi_device.py 4 | bump* 5 | #build 6 | .build 7 | .vscode 8 | Makefile 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: all the source code contained in this repository is for educational use only, and is provided with no support, endorsement, warranty, guarantee or implied fitness for a particular purpose. It does not constitute sample driver code. Do not reach out to VMware to support this code. Do not copy and reuse in any commercial or product setting. 2 | 3 | # thpimon 4 | Native ESXi on Arm hardware status driver for the Raspberry Pi. 5 | 6 | Using the Python library, you can currently get the board revision and temperature. 7 | 8 | Currently work-in-progress, the driver is capable of communicating with the VideoCore firmware using fixed-size buffers (so no console). 9 | 10 | ### Sample Output 11 | 12 | Taken from a Raspberry Pi 4B: 13 | 14 | ``` 15 | $ /scratch/downloads/pyUtil/pimon_util.py 16 | Firmware Revision: 0x5f440c10 17 | Board Model: 0 18 | Board Revision: 0xc03111 19 | Board MAC Address: d6:51:4:32:a6:dc 20 | Board Serial: 0x000000664b0067 21 | Temp: 60.0 (deg. C) 22 | ``` 23 | 24 | ### Installation 25 | 26 | To install the driver, download the VIB file in the build directory and copy it to your RPi. 27 | 28 | Install it as described here: https://kb.vmware.com/s/article/2008939 29 | 30 | Note: you will need to reboot the RPi after installing the VIB. 31 | 32 | I would recommend downloading the Python library in ./pyUtil/pimonLib/ to interact with the thpimon character device. 33 | 34 | Note: You may need to manually change the `PIMON_DEVICE_PATH` variable in `./pyUtil/gpioLib/__init__.py` to reflect the correct name for the character device. I'm digging through the VMKAPI to find a way to name the character device upon creation, so stay tuned. 35 | 36 | ### Other Branches 37 | 38 | If you want to try auto-configuring the name of the `PIMON_DEVICE_PATH`, check out: https://github.com/thebel1/thpimon/tree/autoconf 39 | 40 | If you want to take screenshots of your Pi, check out: https://github.com/thebel1/thpimon/tree/fbuf 41 | -------------------------------------------------------------------------------- /build/bulletin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | thpimon_1.0-0.00001 6 | 7 | 8 | 9 | Tom Hebel 10 | 11 | 12 | 13 | thpimon: Native RPi hardware monitoring driver 14 | 15 | 16 | general 17 | 18 | 19 | enhancement 20 | 21 | 22 | important 23 | 24 | 25 | extension 26 | 27 | 28 | 29 | Native RPi hardware monitoring driver module for vmkernel 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 2020-11-06T22:17:09+00:00 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | THX_bootbank_thpimon_0.1.0-1OEM.701.1.0.40650718 -------------------------------------------------------------------------------- /build/component/VMW-esx-7.0.1-thpimon-1.0-0.00001.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebel1/thpimon/42cc75e3c0b089e1f4822a19fbd0ae01b620c0e9/build/component/VMW-esx-7.0.1-thpimon-1.0-0.00001.zip -------------------------------------------------------------------------------- /build/sverify/thpimon.sva: -------------------------------------------------------------------------------- 1 | vmware-esx-nativeddk-devtools-7.0.0-1.0.15843807.x86_64 2 | vmware-esx-nativeddkarm64-devtools-7.0.1-1.0.40650718.x86_64 3 | vmware-esx-nativeddk-devtools-7.0.0-1.0.16966451.x86_64 4 | vmware-esx-nativeddkarm64-toolchain-2.0.0-1.x86_64 5 | vmware-esx-nativeddkarm64-devtools-sockets-7.0.1-1.0.40650718.x86_64 6 | aarch64-vmk-linux-gnu-gcc (GCC) 6.4.0 7 | Copyright (C) 2017 Free Software Foundation, Inc. 8 | This is free software; see the source for copying conditions. There is NO 9 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | 11 | aarch64-vmk-linux-gnu-g++ (GCC) 6.4.0 12 | Copyright (C) 2017 Free Software Foundation, Inc. 13 | This is free software; see the source for copying conditions. There is NO 14 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | LLD 8.0.1 (compatible with GNU linkers) 17 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -D__MODULE_LICENSE__="ThirdParty:MIT" -D__MODULE_VERSION__="0.1.0-1OEM.701.1.0.40650718" -Ipartners/samples/public/thrpi/thpimon -Ipartners/samples/public/thrpi/thpimon/.build/build/version -Ipartners/samples/public/thrpi/thpimon/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E bora/modules/vmkapi/module_info/module_info.c -U__TIME__ -U__DATE__ 18 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src 19 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/.build 20 | ff5edde8b8fc17827f6a83cb952b9832 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/SUBDIRS/bora/modules/vmkapi/module_info/module_info.i 21 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thrpi/thpimon -Ipartners/samples/public/thrpi/thpimon/.build/build/version -Ipartners/samples/public/thrpi/thpimon/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thrpi/thpimon/pimon_os.c -U__TIME__ -U__DATE__ 22 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src 23 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/.build 24 | 63cecb2bec063de6295b8f43472abf57 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/pimon_os.i 25 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thrpi/thpimon -Ipartners/samples/public/thrpi/thpimon/.build/build/version -Ipartners/samples/public/thrpi/thpimon/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thrpi/thpimon/pimon_charDev.c -U__TIME__ -U__DATE__ 26 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src 27 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/.build 28 | a9ebbadbb495a5bbbed41cd345679f79 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/pimon_charDev.i 29 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thrpi/thpimon -Ipartners/samples/public/thrpi/thpimon/.build/build/version -Ipartners/samples/public/thrpi/thpimon/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thrpi/thpimon/rpiq_drv.c -U__TIME__ -U__DATE__ 30 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src 31 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/.build 32 | 53da446020842a550d509b004a003f00 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/rpiq_drv.i 33 | /opt/vmware/toolchain/cayman_llvm-e51314a6e421afc0fc8856057af8b195bb9805f3/lin64/usr/bin+glibc212/ld.lld -r -o partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/thpimon.sv --whole-archive partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/thpimon 34 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src 35 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thrpi/thpimon/.build 36 | c17016a5bf11bcb43c3809b4b8393dd6 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/thpimon 37 | 6d53071299e7516bad552dd7fdbaa957 partners/samples/public/thrpi/thpimon/.build/build/vmkdriver-thpimon/release/vmkernel64-arm64/signed/thpimon 38 | -------------------------------------------------------------------------------- /build/thpimon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebel1/thpimon/42cc75e3c0b089e1f4822a19fbd0ae01b620c0e9/build/thpimon -------------------------------------------------------------------------------- /build/thpimon-nostrip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebel1/thpimon/42cc75e3c0b089e1f4822a19fbd0ae01b620c0e9/build/thpimon-nostrip -------------------------------------------------------------------------------- /build/thpimon.map: -------------------------------------------------------------------------------- 1 | regtype=native,bus=acpi,id=RPIQ,driver=thpimon 2 | -------------------------------------------------------------------------------- /build/vib/thpimon-0.1.0-1OEM.701.1.0.40650718.aarch64.vib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thebel1/thpimon/42cc75e3c0b089e1f4822a19fbd0ae01b620c0e9/build/vib/thpimon-0.1.0-1OEM.701.1.0.40650718.aarch64.vib -------------------------------------------------------------------------------- /pimon.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | #ifndef PIMON_H 25 | #define PIMON_H 26 | 27 | #define VMKAPI_ONLY 28 | #include "vmkapi.h" 29 | 30 | #define PIMON_DEBUG 31 | 32 | #define PIMON_DRIVER_NAME "thpimon" 33 | 34 | // Probably overkill 35 | #define PIMON_HEAP_INITIAL_SIZE (1024 * 1024) 36 | #define PIMON_HEAP_MAX_SIZE (2 * 1024 * 1024) 37 | 38 | #define PIMON_INT_MAX ((vmk_uint32)~0) 39 | 40 | #define PIMON_LOG_ERR 1 41 | #define PIMON_LOG_WARN 2 42 | #define PIMON_LOG_NOTE 3 43 | #define PIMON_LOG_INIT 4 44 | #define PIMON_LOG_DISC 5 45 | #define PIMON_LOG_INFO 6 46 | #define PIMON_LOG_FUNC 7 47 | #define PIMON_LOG_TRACEIO 8 48 | 49 | #endif /* PIMON_H */ -------------------------------------------------------------------------------- /pimon_charDev.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | /* 25 | * pimon_charDev.c -- 26 | * 27 | * Generic character device driver implementation. 28 | */ 29 | 30 | #include "pimon_charDev.h" 31 | 32 | /***********************************************************************/ 33 | 34 | static vmk_ModuleID pimon_moduleID; 35 | static vmk_HeapID pimon_heapID; 36 | static vmk_LogComponent pimon_logger; 37 | 38 | static vmk_CharDevOps pimon_charDevFileOps = { 39 | .open = pimon_charDevOpen, 40 | .close = pimon_charDevClose, 41 | .ioctl = pimon_charDevIoctl, 42 | .poll = pimon_charDevPoll, 43 | .read = pimon_charDevRead, 44 | .write = pimon_charDevWrite, 45 | }; 46 | 47 | static vmk_CharDevRegOps pimon_CharDevOps = { 48 | .associate = pimon_charDevAssoc, 49 | .disassociate = pimon_charDevDisassoc, 50 | .fileOps = &pimon_charDevFileOps, 51 | }; 52 | 53 | /* 54 | * Dev ops for the new vmk_Device associated with the char dev 55 | */ 56 | static vmk_DeviceOps pimon_CharVmkDevOps = { 57 | .removeDevice = pimon_charVmkDevRemove, 58 | }; 59 | 60 | /* 61 | * Call backs that glue the char dev to the GPIO driver 62 | */ 63 | static pimon_CharDevCallbacks_t *pimon_CharDevCBs; 64 | 65 | /***********************************************************************/ 66 | 67 | static vmk_TimerQueue pimon_charDevTimerQueue; 68 | 69 | /* 70 | *********************************************************************** 71 | * pimon_charDevInit -- 72 | * 73 | * Initialize the character device driver. Onyl called once, as it sets 74 | * a number of global variables. 75 | * 76 | * Results: 77 | * VMK_OK on success, error code otherwise 78 | * 79 | * Side Effects: 80 | * None. 81 | *********************************************************************** 82 | */ 83 | VMK_ReturnStatus 84 | pimon_charDevInit(vmk_ModuleID moduleID, 85 | vmk_HeapID heapID, 86 | vmk_LogComponent logger) 87 | { 88 | VMK_ReturnStatus status = VMK_OK; 89 | 90 | pimon_moduleID = moduleID; 91 | pimon_heapID = heapID; 92 | pimon_logger = logger; 93 | 94 | return status; 95 | } 96 | 97 | /* 98 | *********************************************************************** 99 | * pimon_charDevRegister -- 100 | * 101 | * Register a character device with the vmkernel chardev layer. 102 | * 103 | * Results: 104 | * VMK_OK on success, error code otherwise 105 | * 106 | * Side Effects: 107 | * None. 108 | *********************************************************************** 109 | */ 110 | VMK_ReturnStatus 111 | pimon_charDevRegister(pimon_CharDevProps_t *props) 112 | { 113 | VMK_ReturnStatus status = VMK_OK; 114 | vmk_Device newDevice; 115 | vmk_DeviceID devID; 116 | vmk_AddrCookie registeringDriverData; 117 | vmk_AddrCookie registrationData; 118 | vmk_DeviceProps deviceProps; 119 | pimon_CharDev_t *charDev; 120 | vmk_AddrCookie charDevPriv; 121 | 122 | devID.busType = props->logicalBusType; 123 | status = vmk_LogicalCreateBusAddress(props->driverHandle, 124 | props->parentDevice, 125 | props->logicalPort, 126 | &devID.busAddress, 127 | &devID.busAddressLen); 128 | if (status != VMK_OK) { 129 | goto logical_bus_failed; 130 | } 131 | 132 | charDev = props->charDev; 133 | 134 | /* 135 | * As per vmkapi_char.h it can only be graphics or a test device 136 | */ 137 | devID.busIdentifier = VMK_CHARDEV_IDENTIFIER_GRAPHICS; 138 | devID.busIdentifierLen = vmk_Strnlen(devID.busIdentifier, VMK_MISC_NAME_MAX); 139 | 140 | charDevPriv.ptr = props->privData; 141 | 142 | /* 143 | * Set up char dev registration 144 | */ 145 | 146 | charDev->regData.moduleID = pimon_moduleID; 147 | charDev->regData.deviceOps = &pimon_CharDevOps; 148 | charDev->regData.devicePrivate = charDevPriv; 149 | 150 | registrationData.ptr = &charDev->regData; 151 | registeringDriverData.ptr = props->charDev; 152 | 153 | deviceProps.registeringDriver = props->driverHandle; 154 | deviceProps.deviceID = &devID; 155 | deviceProps.deviceOps = &pimon_CharVmkDevOps; 156 | deviceProps.registeringDriverData = registeringDriverData; 157 | deviceProps.registrationData = registrationData; 158 | 159 | status = vmk_DeviceRegister(&deviceProps, props->parentDevice, &newDevice); 160 | if (status != VMK_OK) { 161 | vmk_Warning(pimon_logger, 162 | "failed to register device: %s", 163 | vmk_StatusToString(status)); 164 | goto register_device_failed; 165 | } 166 | 167 | status = vmk_LogicalFreeBusAddress(props->driverHandle, 168 | devID.busAddress); 169 | if (status != VMK_OK) { 170 | vmk_Warning(pimon_logger, 171 | "failed to free logical bus: %s", 172 | vmk_StatusToString(status)); 173 | goto free_bus_failed; 174 | } 175 | 176 | /* 177 | * Set CBs 178 | */ 179 | pimon_CharDevCBs = props->callbacks; 180 | 181 | return VMK_OK; 182 | 183 | free_bus_failed: 184 | vmk_LogicalFreeBusAddress(props->driverHandle, 185 | devID.busAddress); 186 | register_device_failed: 187 | logical_bus_failed: 188 | return status; 189 | } 190 | 191 | /* 192 | *********************************************************************** 193 | * pimon_charVmkDevRemove -- 194 | * 195 | * Remove character device. 196 | * 197 | * Results: 198 | * VMK_OK on success, error code otherwise 199 | * 200 | * Side Effects: 201 | * None. 202 | *********************************************************************** 203 | */ 204 | VMK_ReturnStatus 205 | pimon_charVmkDevRemove(vmk_Device logicalDev) 206 | { 207 | VMK_ReturnStatus status = VMK_OK; 208 | 209 | status = vmk_DeviceUnregister(logicalDev); 210 | if (status != VMK_OK) 211 | { 212 | vmk_Warning(pimon_logger, 213 | "failed to unregister device: %s", 214 | vmk_StatusToString(status)); 215 | } 216 | 217 | return status; 218 | } 219 | 220 | /* 221 | *********************************************************************** 222 | * pimon_charDevAssoc -- 223 | * 224 | * Associate a char device with a device. 225 | * 226 | * Results: 227 | * VMK_OK on success, error code otherwise 228 | * 229 | * Side Effects: 230 | * None. 231 | *********************************************************************** 232 | */ 233 | VMK_ReturnStatus 234 | pimon_charDevAssoc(vmk_AddrCookie charDevPriv, 235 | vmk_CharDevHandle charDevHandle) 236 | { 237 | VMK_ReturnStatus status = VMK_OK; 238 | vmk_Name charDevAlias; 239 | 240 | status = vmk_CharDeviceGetAlias(charDevHandle, &charDevAlias); 241 | if (status != VMK_OK) { 242 | vmk_Warning(pimon_logger, 243 | "failed to obtain logical device alias: %s", 244 | vmk_StatusToString(status)); 245 | goto get_alias_failed; 246 | } 247 | 248 | #ifdef PIMON_DEBUG 249 | { 250 | vmk_Log(pimon_logger, 251 | "obtained logical device alias %s", 252 | charDevAlias.string); 253 | } 254 | #endif /* PIMON_DEBUG */ 255 | 256 | return VMK_OK; 257 | 258 | get_alias_failed: 259 | return status; 260 | } 261 | 262 | /* 263 | *********************************************************************** 264 | * pimon_charDevDisassoc -- 265 | * 266 | * Disassociate a char device with a device. 267 | * 268 | * Results: 269 | * VMK_OK on success, error code otherwise 270 | * 271 | * Side Effects: 272 | * None. 273 | *********************************************************************** 274 | */ 275 | VMK_ReturnStatus 276 | pimon_charDevDisassoc(vmk_AddrCookie charDevPriv) 277 | { 278 | VMK_ReturnStatus status = VMK_OK; 279 | 280 | return status; 281 | } 282 | 283 | /* 284 | *********************************************************************** 285 | * pimon_charDevOpen -- 286 | * 287 | * Opens a file. 288 | * 289 | * Results: 290 | * VMK_OK on success, error code otherwise 291 | * 292 | * Side Effects: 293 | * None. 294 | *********************************************************************** 295 | */ 296 | VMK_ReturnStatus 297 | pimon_charDevOpen(vmk_CharDevFdAttr *attr) 298 | { 299 | VMK_ReturnStatus status = VMK_OK; 300 | pimon_CharFileData_t *fileData; 301 | vmk_SpinlockCreateProps lockProps; 302 | 303 | /* 304 | * Init private file data 305 | */ 306 | 307 | fileData = vmk_HeapAlloc(pimon_heapID, sizeof(*fileData)); 308 | if (fileData == NULL) { 309 | status = VMK_NO_MEMORY; 310 | vmk_Warning(pimon_logger, 311 | "failed to create file private data: %s", 312 | vmk_StatusToString(status)); 313 | goto file_priv_alloc_failed; 314 | } 315 | vmk_Memset(fileData, 0, sizeof(*fileData)); 316 | 317 | /* 318 | * Init lock 319 | */ 320 | 321 | lockProps.moduleID = pimon_moduleID; 322 | lockProps.heapID = pimon_heapID; 323 | status = vmk_NameInitialize(&lockProps.name, PIMON_DRIVER_NAME); 324 | if (status != VMK_OK) { 325 | vmk_Warning(pimon_logger, 326 | "failed to init lock name: %s", 327 | vmk_StatusToString(status)); 328 | goto lock_init_failed; 329 | } 330 | 331 | lockProps.type = VMK_SPINLOCK; 332 | lockProps.domain = VMK_LOCKDOMAIN_INVALID; 333 | lockProps.rank = VMK_SPINLOCK_UNRANKED; 334 | status = vmk_SpinlockCreate(&lockProps, &fileData->lock); 335 | if (status != VMK_OK) { 336 | vmk_Warning(pimon_logger, 337 | "failed to create spinlock: %s", 338 | vmk_StatusToString(status)); 339 | goto lock_init_failed; 340 | } 341 | 342 | /* 343 | * We don't use this buffer, so set it to NULL 344 | */ 345 | fileData->data = NULL; 346 | 347 | /* 348 | * Prep file for I/O 349 | */ 350 | fileData->timerPending = VMK_FALSE; 351 | fileData->deathPending = VMK_FALSE; 352 | fileData->pollMask = VMKAPI_POLL_WRITE; 353 | fileData->timeoutUS = PIMON_CHARDEV_POLL_TIMEOUT_US; 354 | attr->clientInstanceData.ptr = fileData; 355 | 356 | #ifdef PIMON_DEBUG 357 | { 358 | vmk_Log(pimon_logger, 359 | "opened file; priv %p lock %p", 360 | fileData, 361 | fileData->lock); 362 | } 363 | #endif /* PIMON_DEBUG */ 364 | 365 | /* Call CB */ 366 | status = pimon_CharDevCBs->open(attr); 367 | 368 | return status; 369 | 370 | lock_init_failed: 371 | vmk_HeapFree(pimon_heapID, fileData); 372 | 373 | file_priv_alloc_failed: 374 | return status; 375 | } 376 | 377 | /* 378 | *********************************************************************** 379 | * pimon_charDevClose -- 380 | * 381 | * Closes a file. 382 | * 383 | * Results: 384 | * VMK_OK on success, error code otherwise 385 | * 386 | * Side Effects: 387 | * None. 388 | *********************************************************************** 389 | */ 390 | VMK_ReturnStatus 391 | pimon_charDevClose(vmk_CharDevFdAttr *attr) 392 | { 393 | VMK_ReturnStatus status = VMK_OK; 394 | pimon_CharFileData_t *fileData = attr->clientInstanceData.ptr; 395 | 396 | if (fileData == NULL) { 397 | status = VMK_BAD_PARAM; 398 | vmk_Warning(pimon_logger, "file data null"); 399 | goto file_data_null; 400 | } 401 | 402 | vmk_SpinlockLock(fileData->lock); 403 | 404 | fileData->deathPending = VMK_TRUE; 405 | 406 | if (fileData->timerPending == VMK_FALSE) { 407 | vmk_SpinlockUnlock(fileData->lock); 408 | pimon_charDevFileDestroy(fileData); 409 | } 410 | 411 | /* Call CB */ 412 | status = pimon_CharDevCBs->close(attr); 413 | 414 | file_data_null: 415 | return status; 416 | } 417 | 418 | /* 419 | *********************************************************************** 420 | * pimon_charDevIoctl -- 421 | * 422 | * GPIO chardev-specific I/O ops. Used for programming reads to input 423 | * pins. 424 | * 425 | * Results: 426 | * VMK_OK on success, error code otherwise 427 | * 428 | * Side Effects: 429 | * None. 430 | *********************************************************************** 431 | */ 432 | VMK_ReturnStatus 433 | pimon_charDevIoctl(vmk_CharDevFdAttr *attr, 434 | unsigned int cmd, 435 | vmk_uintptr_t userData, 436 | vmk_IoctlCallerSize callerSize, 437 | vmk_int32 *result) 438 | { 439 | VMK_ReturnStatus status = VMK_OK; 440 | void *ioctlData; 441 | vmk_ByteCount ioctlDataLen; 442 | pimon_CharFileData_t *fileData = attr->clientInstanceData.ptr; 443 | 444 | if (fileData == NULL) { 445 | status = VMK_BAD_PARAM; 446 | vmk_Warning(pimon_logger, "file data null"); 447 | goto file_data_null; 448 | } 449 | 450 | /* 451 | * Allocate ioctl data 452 | */ 453 | ioctlDataLen = ((pimon_CharDevPriv_t *)attr->clientDeviceData.ptr)->ioctlDataLen; 454 | ioctlData = vmk_HeapAlloc(pimon_heapID, ioctlDataLen); 455 | if (ioctlData == NULL) { 456 | status = VMK_NO_MEMORY; 457 | vmk_Warning(pimon_logger, 458 | "failed to allocate memory for ioctl data: %s", 459 | vmk_StatusToString(status)); 460 | goto ioctl_alloc_failed; 461 | } 462 | 463 | #ifdef PIMON_DEBUG 464 | { 465 | vmk_Log(pimon_logger, 466 | "copying ioctl data from %p (UW) to %p (vmk)", 467 | (vmk_VA)userData, 468 | (vmk_VA)ioctlData); 469 | } 470 | #endif /* PIMON_DEBUG */ 471 | 472 | /* 473 | * Copy ioctl data from UW 474 | */ 475 | status = vmk_CopyFromUser((vmk_VA)ioctlData, 476 | (vmk_VA)userData, 477 | ioctlDataLen); 478 | if (status != VMK_OK) { 479 | vmk_Warning(pimon_logger, 480 | "unable to copy ioctl data from UW ptr %p: %s", 481 | userData, 482 | vmk_StatusToString(status)); 483 | goto ioctl_uw2vmk_failed; 484 | } 485 | 486 | #ifdef PIMON_DEBUG 487 | { 488 | vmk_Log(pimon_logger, 489 | "executing ioctl cmd %d with data %p", 490 | cmd, 491 | userData); 492 | } 493 | #endif 494 | 495 | /* 496 | * Call CB 497 | */ 498 | vmk_SpinlockLock(fileData->lock); 499 | status = pimon_CharDevCBs->ioctl(cmd, ioctlData, ioctlDataLen); 500 | vmk_SpinlockUnlock(fileData->lock); 501 | if (status != VMK_OK) { 502 | vmk_Warning(pimon_logger, 503 | "ioctl cmd %d with data %p failed: %s", 504 | cmd, 505 | userData, 506 | vmk_StatusToString(status)); 507 | goto ioctl_cmd_failed; 508 | } 509 | 510 | /* 511 | * Copy iotl data back to UW 512 | */ 513 | status = vmk_CopyToUser((vmk_VA)userData, 514 | (vmk_VA)ioctlData, 515 | ioctlDataLen); 516 | if (status != VMK_OK) { 517 | vmk_Warning(pimon_logger, 518 | "unable to copy ioctl data back to UW: %s", 519 | vmk_StatusToString(status)); 520 | goto ioctl_vmk2uw_failed; 521 | } 522 | 523 | vmk_HeapFree(pimon_heapID, ioctlData); 524 | 525 | return VMK_OK; 526 | 527 | ioctl_vmk2uw_failed: 528 | ioctl_cmd_failed: 529 | ioctl_uw2vmk_failed: 530 | vmk_HeapFree(pimon_heapID, ioctlData); 531 | 532 | ioctl_alloc_failed: 533 | file_data_null: 534 | return status; 535 | } 536 | 537 | /* 538 | *********************************************************************** 539 | * pimon_charDevRead -- 540 | * 541 | * Reads from a file. Not supported for now, as we only allow writing 542 | * commands of sorts to the chardev file. 543 | * 544 | * Results: 545 | * VMK_OK on success, error code otherwise 546 | * 547 | * Side Effects: 548 | * None. 549 | *********************************************************************** 550 | */ 551 | VMK_ReturnStatus 552 | pimon_charDevRead(vmk_CharDevFdAttr *attr, 553 | char *buffer, 554 | vmk_ByteCount nbytes, 555 | vmk_loff_t *ppos, 556 | vmk_ByteCountSigned *nread) 557 | { 558 | return pimon_charDevIO(attr, buffer, nbytes, ppos, nread, VMK_FALSE); 559 | } 560 | 561 | /* 562 | *********************************************************************** 563 | * pimon_charDevWrite -- 564 | * 565 | * Writes to a file. 566 | * 567 | * Results: 568 | * VMK_OK on success, error code otherwise 569 | * 570 | * Side Effects: 571 | * None. 572 | *********************************************************************** 573 | */ 574 | VMK_ReturnStatus 575 | pimon_charDevWrite(vmk_CharDevFdAttr *attr, 576 | char *buffer, 577 | vmk_ByteCount nbytes, 578 | vmk_loff_t *ppos, 579 | vmk_ByteCountSigned *nwritten) 580 | { 581 | return pimon_charDevIO(attr, buffer, nbytes, ppos, nwritten, VMK_TRUE); 582 | } 583 | 584 | /* 585 | *********************************************************************** 586 | * pimon_charDevIO -- 587 | * 588 | * Read/write to file. 589 | * 590 | * Results: 591 | * VMK_OK on success, error code otherwise 592 | * 593 | * Side Effects: 594 | * None. 595 | *********************************************************************** 596 | */ 597 | VMK_ReturnStatus 598 | pimon_charDevIO(vmk_CharDevFdAttr *attr, 599 | char *buffer, 600 | vmk_ByteCount nbytes, 601 | vmk_loff_t *ppos, 602 | vmk_ByteCountSigned *ndone, 603 | vmk_Bool isWrite) 604 | { 605 | return VMK_NOT_SUPPORTED; 606 | } 607 | 608 | /* 609 | *********************************************************************** 610 | * pimon_charDevPoll -- 611 | * 612 | * Polls a file. 613 | * 614 | * Results: 615 | * VMK_OK on success, error code otherwise 616 | * 617 | * Side Effects: 618 | * None. 619 | *********************************************************************** 620 | */ 621 | VMK_ReturnStatus 622 | pimon_charDevPoll(vmk_CharDevFdAttr *attr, 623 | vmk_PollContext pollCtx, 624 | vmk_uint32 *pollMask) 625 | { 626 | VMK_ReturnStatus status = VMK_OK; 627 | pimon_CharFileData_t *fileData = attr->clientInstanceData.ptr; 628 | vmk_TimerCookie tmrCookie; 629 | 630 | if (fileData == NULL) { 631 | status = VMK_BAD_PARAM; 632 | vmk_Warning(pimon_logger, "file data null"); 633 | goto file_data_null; 634 | } 635 | 636 | vmk_SpinlockLock(fileData->lock); 637 | 638 | if (fileData->pollMask == VMK_FALSE 639 | && fileData->timerPending == VMK_FALSE 640 | && fileData->deathPending == VMK_FALSE) { 641 | tmrCookie.ptr = fileData; 642 | status = vmk_TimerSchedule(pimon_charDevTimerQueue, 643 | pimon_charDevTimerCB, 644 | tmrCookie, 645 | PIMON_CHARDEV_POLL_TIMEOUT_US, 646 | VMK_TIMER_DEFAULT_TOLERANCE, 647 | VMK_TIMER_ATTR_NONE, 648 | VMK_LOCKDOMAIN_INVALID, 649 | VMK_SPINLOCK_UNRANKED, 650 | &fileData->timer); 651 | if (status == VMK_OK) { 652 | fileData->timerPending = VMK_TRUE; 653 | } 654 | else { 655 | vmk_Warning(pimon_logger, 656 | "failed to create poll timer: %s", 657 | vmk_StatusToString(status)); 658 | goto create_poll_timer_failed; 659 | } 660 | } 661 | 662 | vmk_CharDevSetPollContext(pollCtx, (void *)fileData); 663 | *pollMask = fileData->pollMask; 664 | 665 | vmk_SpinlockUnlock(fileData->lock); 666 | 667 | return VMK_OK; 668 | 669 | create_poll_timer_failed: 670 | vmk_SpinlockUnlock(fileData->lock); 671 | 672 | file_data_null: 673 | return status; 674 | } 675 | 676 | /* 677 | *********************************************************************** 678 | * pimon_charDevTimerCB -- 679 | * 680 | * Callback for char device poll timer. 681 | * 682 | * Results: 683 | * VMK_OK on success, error code otherwise 684 | * 685 | * Side Effects: 686 | * None. 687 | *********************************************************************** 688 | */ 689 | void 690 | pimon_charDevTimerCB(vmk_TimerCookie data) 691 | { 692 | pimon_CharFileData_t *fileData = (pimon_CharFileData_t *)data.ptr; 693 | 694 | if (fileData == NULL) { 695 | vmk_Warning(pimon_logger, "file data null"); 696 | goto file_data_null; 697 | } 698 | 699 | vmk_SpinlockLock(fileData->lock); 700 | 701 | fileData->timerPending = VMK_FALSE; 702 | fileData->pollMask = VMKAPI_POLL_WRITE; 703 | 704 | if (fileData->deathPending == VMK_FALSE) { 705 | /* Wake up pollers in UW */ 706 | vmk_CharDevWakePollers(fileData); 707 | } 708 | else { 709 | vmk_SpinlockUnlock(fileData->lock); 710 | pimon_charDevFileDestroy(fileData); 711 | goto death_pending; 712 | } 713 | 714 | vmk_SpinlockUnlock(fileData->lock); 715 | 716 | death_pending: 717 | file_data_null: 718 | return; 719 | } 720 | 721 | /* 722 | *********************************************************************** 723 | * pimon_charDevFileDestroy -- 724 | * 725 | * Destroy a file. 726 | * 727 | * Results: 728 | * VMK_OK on success, error code otherwise 729 | * 730 | * Side Effects: 731 | * None. 732 | *********************************************************************** 733 | */ 734 | void 735 | pimon_charDevFileDestroy(pimon_CharFileData_t *fileData) 736 | { 737 | #ifdef PIMON_DEBUG 738 | { 739 | vmk_Log(pimon_logger, 740 | "destroying file %p", 741 | fileData); 742 | } 743 | #endif /* PIMON_DEBUG */ 744 | 745 | vmk_SpinlockDestroy(fileData->lock); 746 | vmk_HeapFree(pimon_heapID, fileData); 747 | } -------------------------------------------------------------------------------- /pimon_charDev.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | /* 25 | * pimon_charDev.h -- 26 | */ 27 | 28 | #ifndef PIMON_CHARDEV_H 29 | #define PIMON_CHARDEV_H 30 | 31 | /***********************************************************************/ 32 | 33 | #define VMKAPI_ONLY 34 | #include "vmkapi.h" 35 | 36 | #include "pimon.h" 37 | 38 | /***********************************************************************/ 39 | 40 | #define PIMON_CHARDEV_BUFFER_SIZE 4096 /* Probably overkill */ 41 | #define PIMON_CHARDEV_POLL_TIMEOUT_US 1000000 42 | 43 | /***********************************************************************/ 44 | 45 | /* 46 | * Callbacks for gluing the char dev driver to the THX driver 47 | */ 48 | typedef VMK_ReturnStatus (*pimon_CharDevOpenCB_t)(vmk_CharDevFdAttr *attr); 49 | typedef VMK_ReturnStatus (*pimon_CharDevCloseCB_t)(vmk_CharDevFdAttr *attr); 50 | 51 | typedef VMK_ReturnStatus (*pimon_CharDevIoctlCB_t)(unsigned int cmd, 52 | void *ioctlData, 53 | vmk_ByteCount dataLen); 54 | typedef VMK_ReturnStatus (*pimon_CharDevReadCB_t)(char *buffer, 55 | vmk_ByteCount nbytes, 56 | vmk_loff_t *ppos, 57 | vmk_ByteCountSigned *nread); 58 | typedef VMK_ReturnStatus (*pimon_CharDevWriteCB_t)(char *buffer, 59 | vmk_ByteCount nbytes, 60 | vmk_loff_t *ppos, 61 | vmk_ByteCountSigned *nread); 62 | typedef struct pimon_CharDevCallbacks_t { 63 | pimon_CharDevOpenCB_t open; 64 | pimon_CharDevCloseCB_t close; 65 | pimon_CharDevIoctlCB_t ioctl; 66 | pimon_CharDevReadCB_t read; 67 | pimon_CharDevWriteCB_t write; 68 | } pimon_CharDevCallbacks_t; 69 | 70 | typedef struct pimon_CharDevPriv_t { 71 | /* The size of the data being passed via ioctl */ 72 | vmk_ByteCount ioctlDataLen; 73 | } pimon_CharDevPriv_t; 74 | 75 | typedef struct pimon_CharDev_t { 76 | vmk_Device vmkDevice; 77 | vmk_CharDevRegData regData; 78 | pimon_CharDevCallbacks_t devCBs; 79 | } pimon_CharDev_t; 80 | 81 | typedef struct pimon_CharFileData_t { 82 | vmk_Lock lock; 83 | char *data; 84 | vmk_Bool timerPending; 85 | vmk_Bool deathPending; 86 | vmk_Timer timer; 87 | vmk_int32 timeoutUS; 88 | vmk_uint32 pollMask; 89 | } pimon_CharFileData_t; 90 | 91 | typedef struct pimon_CharDevProps_t { 92 | vmk_Driver driverHandle; 93 | vmk_BusType logicalBusType; 94 | vmk_Device parentDevice; 95 | pimon_CharDev_t *charDev; 96 | pimon_CharDevPriv_t *privData; 97 | vmk_uint32 logicalPort; 98 | pimon_CharDevCallbacks_t *callbacks; 99 | } pimon_CharDevProps_t; 100 | 101 | /***********************************************************************/ 102 | 103 | VMK_ReturnStatus pimon_charDevInit(vmk_ModuleID moduleID, 104 | vmk_HeapID heapID, 105 | vmk_LogComponent logger); 106 | 107 | VMK_ReturnStatus pimon_charDevRegister(pimon_CharDevProps_t *props); 108 | 109 | VMK_ReturnStatus pimon_charVmkDevRemove(vmk_Device logicalDev); 110 | 111 | VMK_ReturnStatus pimon_charDevAssoc(vmk_AddrCookie charDevPriv, 112 | vmk_CharDevHandle charDevHandle); 113 | 114 | VMK_ReturnStatus pimon_charDevDisassoc(vmk_AddrCookie charDevPriv); 115 | 116 | VMK_ReturnStatus pimon_charDevOpen(vmk_CharDevFdAttr *attr); 117 | 118 | VMK_ReturnStatus pimon_charDevClose(vmk_CharDevFdAttr *attr); 119 | 120 | VMK_ReturnStatus pimon_charDevIoctl(vmk_CharDevFdAttr *attr, 121 | vmk_uint32 cmd, 122 | vmk_uintptr_t userData, 123 | vmk_IoctlCallerSize callerSize, 124 | vmk_int32 *result); 125 | 126 | VMK_ReturnStatus pimon_charDevRead(vmk_CharDevFdAttr *attr, 127 | char *buffer, 128 | vmk_ByteCount nbytes, 129 | vmk_loff_t *ppos, 130 | vmk_ByteCountSigned *nread); 131 | 132 | VMK_ReturnStatus pimon_charDevWrite(vmk_CharDevFdAttr *attr, 133 | char *buffer, 134 | vmk_ByteCount nbytes, 135 | vmk_loff_t *ppos, 136 | vmk_ByteCountSigned *nwritten); 137 | 138 | VMK_ReturnStatus pimon_charDevIO(vmk_CharDevFdAttr *attr, 139 | char *buffer, 140 | vmk_ByteCount nbytes, 141 | vmk_loff_t *ppos, 142 | vmk_ByteCountSigned *ndone, 143 | vmk_Bool isWrite); 144 | 145 | VMK_ReturnStatus pimon_charDevPoll(vmk_CharDevFdAttr *attr, 146 | vmk_PollContext pollCtx, 147 | vmk_uint32 *pollMask); 148 | 149 | void pimon_charDevTimerCB(vmk_TimerCookie data); 150 | 151 | void pimon_charDevFileDestroy(pimon_CharFileData_t *fileData); 152 | 153 | /***********************************************************************/ 154 | 155 | #endif /* PIMON_CHARDEV_H */ -------------------------------------------------------------------------------- /pimon_os.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | // TODO: 25 | // - Implement a fail-safe for when the requested DMA heap is above 1GB, and 26 | // program the DMA controller to move the DMA aperture upwards to match the 27 | // heap start MA. 28 | 29 | /* 30 | * pimon_os.c -- 31 | * 32 | * Module initialization and other OS stuff. 33 | */ 34 | 35 | #include "pimon.h" 36 | #include "pimon_types.h" 37 | #include "rpiq_drv.h" 38 | #include "pimon_charDev.h" 39 | 40 | /***********************************************************************/ 41 | 42 | static pimon_Driver_t pimon_Driver; 43 | static rpiq_Device_t rpiq_Device; 44 | 45 | /* For RPIQ char dev */ 46 | static pimon_CharDev_t rpiq_CharDev; 47 | static vmk_BusType rpiq_logicalBusType; 48 | 49 | VMK_ReturnStatus pimon_attachDevice(vmk_Device device); 50 | VMK_ReturnStatus pimon_scanDevice(vmk_Device device); 51 | VMK_ReturnStatus pimon_detachDevice(vmk_Device device); 52 | VMK_ReturnStatus pimon_quiesceDevice(vmk_Device device); 53 | VMK_ReturnStatus pimon_startDevice(vmk_Device device); 54 | void pimon_forgetDevice(vmk_Device device); 55 | 56 | static vmk_DriverOps pimon_DriverOps = { 57 | .attachDevice = pimon_attachDevice, 58 | .scanDevice = pimon_scanDevice, 59 | .detachDevice = pimon_detachDevice, 60 | .quiesceDevice = pimon_quiesceDevice, 61 | .startDevice = pimon_startDevice, 62 | .forgetDevice = pimon_forgetDevice, 63 | }; 64 | 65 | /* 66 | * Callbacks gluing the chardev driver to the RPIQ driver. 67 | */ 68 | static pimon_CharDevCallbacks_t rpiq_CharDevCBs = { 69 | .open = rpiq_mmioOpenCB, 70 | .close = rpiq_mmioCloseCB, 71 | .ioctl = rpiq_mmioIoctlCB, 72 | .read = rpiq_mmioReadCB, 73 | .write = rpiq_mmioWriteCB, 74 | }; 75 | 76 | /* 77 | *********************************************************************** 78 | * init_module -- 79 | * 80 | * Entry point for vmkernel module. 81 | * 82 | * Results: 83 | * VMK_OK on success, error code otherwise 84 | * 85 | * Side Effects: 86 | * None 87 | *********************************************************************** 88 | */ 89 | int 90 | init_module(void) 91 | { 92 | VMK_ReturnStatus status = VMK_OK; 93 | vmk_HeapID heapID; 94 | vmk_HeapCreateProps heapProps; 95 | vmk_DriverProps driverProps; 96 | vmk_LogProperties logProps; 97 | vmk_Name charDevBusName; 98 | 99 | status = vmk_NameInitialize(&pimon_Driver.driverName, PIMON_DRIVER_NAME); 100 | VMK_ASSERT(status == VMK_OK); 101 | 102 | pimon_Driver.moduleID = vmk_ModuleCurrentID; 103 | rpiq_Device.initialized = VMK_FALSE; 104 | 105 | /* 106 | * Create module heap 107 | */ 108 | 109 | heapProps.type = VMK_HEAP_TYPE_SIMPLE; 110 | status = vmk_NameInitialize(&heapProps.name, PIMON_DRIVER_NAME); 111 | VMK_ASSERT(status == VMK_OK); 112 | heapProps.module = pimon_Driver.moduleID; 113 | heapProps.initial = PIMON_HEAP_INITIAL_SIZE; 114 | heapProps.creationTimeoutMS = VMK_TIMEOUT_NONBLOCKING; 115 | heapProps.max = PIMON_HEAP_MAX_SIZE; 116 | 117 | status = vmk_HeapCreate(&heapProps, &heapID); 118 | if (status != VMK_OK) { 119 | vmk_WarningMessage("%s: %s: failed to create heap: %s", 120 | PIMON_DRIVER_NAME, 121 | __FUNCTION__, 122 | vmk_StatusToString(status)); 123 | goto heap_create_failed; 124 | } 125 | 126 | vmk_ModuleSetHeapID(pimon_Driver.moduleID, heapID); 127 | VMK_ASSERT(vmk_ModuleGetHeapID(pimon_Driver.moduleID) == heapID); 128 | pimon_Driver.heapID = heapID; 129 | 130 | /* 131 | * Set up logger 132 | */ 133 | 134 | status = vmk_NameInitialize(&logProps.name, PIMON_DRIVER_NAME); 135 | VMK_ASSERT(status == VMK_OK); 136 | logProps.module = pimon_Driver.moduleID; 137 | logProps.heap = pimon_Driver.heapID; 138 | logProps.defaultLevel = 0; 139 | logProps.throttle = NULL; 140 | 141 | status = vmk_LogRegister(&logProps, &pimon_Driver.logger); 142 | if (status != VMK_OK) { 143 | vmk_WarningMessage("%s: %s: failed to register logger: %s", 144 | PIMON_DRIVER_NAME, 145 | __FUNCTION__, 146 | vmk_StatusToString(status)); 147 | goto logger_reg_failed; 148 | } 149 | 150 | /* 151 | * Init logical bus for char dev 152 | */ 153 | 154 | status = vmk_NameInitialize(&charDevBusName, VMK_LOGICAL_BUS_NAME); 155 | status = vmk_BusTypeFind(&charDevBusName, &rpiq_logicalBusType); 156 | if (status != VMK_OK) { 157 | vmk_Warning(pimon_Driver.logger, 158 | "failed to find logical bus: %s", 159 | vmk_StatusToString(status)); 160 | goto chardev_bus_failed; 161 | } 162 | 163 | /* 164 | * Register driver 165 | */ 166 | 167 | vmk_Memset(&driverProps, 0, sizeof(driverProps)); 168 | status = vmk_NameInitialize(&driverProps.name, PIMON_DRIVER_NAME); 169 | VMK_ASSERT(status == VMK_OK); 170 | driverProps.moduleID = pimon_Driver.moduleID; 171 | driverProps.ops = &pimon_DriverOps; 172 | 173 | status = vmk_DriverRegister(&driverProps, 174 | &(pimon_Driver.driverHandle)); 175 | if (status == VMK_OK) { 176 | vmk_Log(pimon_Driver.logger, 177 | "driver registration successful", 178 | PIMON_DRIVER_NAME, 179 | __FUNCTION__); 180 | } 181 | else { 182 | vmk_Warning(pimon_Driver.logger, 183 | "driver registration failed: %s", 184 | vmk_StatusToString(status)); 185 | goto driver_register_failed; 186 | } 187 | 188 | /* 189 | * Init char dev driver 190 | */ 191 | pimon_charDevInit(pimon_Driver.moduleID, 192 | pimon_Driver.heapID, 193 | pimon_Driver.logger); 194 | 195 | return VMK_OK; 196 | 197 | driver_register_failed: 198 | chardev_bus_failed: 199 | vmk_LogUnregister(pimon_Driver.logger); 200 | 201 | logger_reg_failed: 202 | vmk_HeapDestroy(pimon_Driver.heapID); 203 | 204 | heap_create_failed: 205 | return 0; 206 | } 207 | 208 | /* 209 | *********************************************************************** 210 | * cleanup_module -- 211 | * 212 | * Cleanup code for when module is unloaded. 213 | * 214 | * Results: 215 | * VMK_OK on success, error code otherwise 216 | * 217 | * Side Effects: 218 | * None 219 | *********************************************************************** 220 | */ 221 | void 222 | cleanup_module(void) 223 | { 224 | rpiq_drvCleanUp(); 225 | vmk_HeapDestroy(rpiq_Device.dmaHeapID); 226 | vmk_ACPIUnmapIOResource(pimon_Driver.moduleID, rpiq_Device.acpiDevice, 0); 227 | vmk_LogUnregister(pimon_Driver.logger); 228 | vmk_BusTypeRelease(rpiq_logicalBusType); 229 | vmk_HeapDestroy(pimon_Driver.heapID); 230 | vmk_DriverUnregister(pimon_Driver.driverHandle); 231 | } 232 | 233 | /* 234 | *********************************************************************** 235 | * pimon_attachDevice -- 236 | * 237 | * Driver callback that attaches RPIQ logical device to the driver. 238 | * 239 | * Results: 240 | * VMK_OK on success, error code otherwise 241 | * 242 | * Side Effects: 243 | * None 244 | *********************************************************************** 245 | */ 246 | VMK_ReturnStatus 247 | pimon_attachDevice(vmk_Device device) 248 | { 249 | VMK_ReturnStatus status = VMK_OK; 250 | vmk_ACPIDevice acpiDev; 251 | vmk_ACPIInfo acpiInfo; 252 | vmk_MappedResourceAddress mappedAddr; 253 | vmk_HeapAllocationDescriptor heapAllocDesc; 254 | vmk_ByteCount dmaHeapSize; 255 | vmk_HeapCreateProps heapProps; 256 | vmk_HeapID dmaHeapID; 257 | rpiq_Device_t *adapter = &rpiq_Device; 258 | 259 | /* 260 | * Since there is only one RPIQ device on the system board, we're making 261 | * sure we're not initializing more. 262 | */ 263 | if (adapter->initialized != VMK_FALSE) { 264 | vmk_Warning(pimon_Driver.logger, 265 | "RPIQ device %p already initialized as %p", 266 | device, 267 | adapter->vmkDevice); 268 | status = VMK_EXISTS; 269 | goto device_already_exists; 270 | } 271 | 272 | /* 273 | * Get vmk ACPI dev 274 | */ 275 | status = vmk_DeviceGetRegistrationData(device, (vmk_AddrCookie *)&acpiDev); 276 | if (status != VMK_OK) { 277 | vmk_Warning(pimon_Driver.logger, 278 | "failed to get ACPI dev for vmk dev %p: %s", 279 | device, 280 | vmk_StatusToString(status)); 281 | goto get_acpi_dev_failed; 282 | } 283 | 284 | /* 285 | * Find out if this is an ACPI dev 286 | */ 287 | status = vmk_ACPIQueryInfo(acpiDev, &acpiInfo); 288 | if (status != VMK_OK) { 289 | goto not_an_acpi_dev; 290 | } 291 | 292 | vmk_Log(pimon_Driver.logger, 293 | "retrieved ACPI dev %p for vmk dev %p", 294 | acpiDev, 295 | device); 296 | 297 | /* 298 | * Map io resources 299 | */ 300 | 301 | status = vmk_ACPIMapIOResource(pimon_Driver.moduleID, 302 | acpiDev, 303 | 0, 304 | &mappedAddr); 305 | if (status != VMK_OK) { 306 | vmk_Warning(pimon_Driver.logger, 307 | "unable to acquire io resources" 308 | " for device %p: %s", 309 | device, 310 | vmk_StatusToString(status)); 311 | goto iores_map_failed; 312 | } 313 | else if (mappedAddr.type != VMK_IORESOURCE_MEM) { 314 | vmk_Warning(pimon_Driver.logger, 315 | "mapped io space %p of type %d, expecting %d", 316 | mappedAddr, 317 | mappedAddr.type, 318 | VMK_IORESOURCE_MEM); 319 | goto iores_map_failed; 320 | } 321 | 322 | vmk_Log(pimon_Driver.logger, 323 | "mapped io space at %p of length %d for device %p", 324 | (void*)mappedAddr.address.vaddr, 325 | mappedAddr.len, 326 | device); 327 | 328 | /* 329 | * Create DMA heap for device 330 | */ 331 | 332 | heapProps.type = VMK_HEAP_TYPE_CUSTOM; 333 | heapProps.module = pimon_Driver.moduleID; 334 | heapProps.creationTimeoutMS = VMK_TIMEOUT_NONBLOCKING; 335 | heapProps.typeSpecific.custom.physContiguity = VMK_MEM_PHYS_CONTIGUOUS; 336 | 337 | /* 338 | * The DMA addresses accessible by the DMA engine are below 1GB by default, 339 | * so the below may not be sufficient. 340 | */ 341 | // TODO: Make more robust by programming DMA controller to move aperture. 342 | heapProps.typeSpecific.custom.physRange = VMK_PHYS_ADDR_BELOW_2GB; 343 | 344 | status = vmk_NameInitialize(&heapProps.name, RPIQ_DMA_HEAP_NAME); 345 | VMK_ASSERT(status == VMK_OK); 346 | 347 | heapAllocDesc.size = sizeof(rpiq_MboxBuffer_t); 348 | heapAllocDesc.alignment = RPIQ_DMA_MBOX_ALIGNMENT; 349 | heapAllocDesc.count = RPIQ_DMA_MBOX_OBJ_MAX; 350 | 351 | status = vmk_HeapDetermineMaxSize(&heapAllocDesc, 1, &dmaHeapSize); 352 | if (status != VMK_OK) { 353 | vmk_Warning(pimon_Driver.logger, 354 | "failed to determine DMA max heap size: %s", 355 | vmk_StatusToString(status)); 356 | goto dma_heap_size_failed; 357 | } 358 | 359 | heapProps.initial = dmaHeapSize; 360 | heapProps.max = dmaHeapSize; 361 | 362 | status = vmk_HeapCreate(&heapProps, &dmaHeapID); 363 | if (status != VMK_OK) { 364 | vmk_Warning(pimon_Driver.logger, 365 | "failed to create DMA heap: %s", 366 | vmk_StatusToString(status)); 367 | goto dma_heap_create_failed; 368 | } 369 | 370 | /* 371 | * Init adapter 372 | */ 373 | 374 | vmk_Memset(adapter, 0, sizeof(*adapter)); 375 | adapter->vmkDevice = device; 376 | adapter->acpiDevice = acpiDev; 377 | adapter->acpiInfo = acpiInfo; 378 | vmk_AtomicWrite64(&adapter->refCount, 0); 379 | 380 | vmk_Memcpy(&adapter->mmioMappedAddr, 381 | &mappedAddr, 382 | sizeof(adapter->mmioMappedAddr)); 383 | adapter->mmioBase = (void *)mappedAddr.address.vaddr; 384 | adapter->mmioLen = mappedAddr.len; 385 | adapter->dmaHeapID = dmaHeapID; 386 | adapter->initialized = VMK_TRUE; 387 | 388 | /* 389 | * Init RPIQ driver 390 | */ 391 | status = rpiq_drvInit(&pimon_Driver, &rpiq_Device); 392 | if (status != VMK_OK) { 393 | vmk_Warning(pimon_Driver.logger, 394 | "failed to initialize RPIQ driver: %s", 395 | vmk_StatusToString(status)); 396 | goto rpiq_drv_init_failed; 397 | } 398 | 399 | /* 400 | * Attach device 401 | */ 402 | status = vmk_DeviceSetAttachedDriverData(adapter->vmkDevice, adapter); 403 | if (status != VMK_OK) { 404 | vmk_Warning(pimon_Driver.logger, 405 | "failed to attach device %p: %s", 406 | adapter->vmkDevice, 407 | vmk_StatusToString(status)); 408 | goto device_attach_failed; 409 | } 410 | 411 | #ifdef PIMON_DEBUG 412 | { 413 | vmk_Log(pimon_Driver.logger, 414 | "DMA heap %p", 415 | adapter->dmaHeapID); 416 | } 417 | #endif /* PIMON_DEBUG */ 418 | 419 | return VMK_OK; 420 | 421 | device_attach_failed: 422 | rpiq_drv_init_failed: 423 | vmk_HeapDestroy(rpiq_Device.dmaHeapID); 424 | 425 | dma_heap_create_failed: 426 | dma_heap_size_failed: 427 | vmk_ACPIUnmapIOResource(pimon_Driver.moduleID, acpiDev, 0); 428 | 429 | iores_map_failed: 430 | not_an_acpi_dev: 431 | get_acpi_dev_failed: 432 | device_already_exists: 433 | vmk_Warning(pimon_Driver.logger, 434 | "no device attached", 435 | PIMON_DRIVER_NAME, 436 | __FUNCTION__); 437 | return status; 438 | } 439 | 440 | /* 441 | *********************************************************************** 442 | * pimon_scanDevice -- 443 | * 444 | * Register char dev. 445 | * 446 | * Results: 447 | * VMK_OK on success, error code otherwise 448 | * 449 | * Side Effects: 450 | * None 451 | *********************************************************************** 452 | */ 453 | VMK_ReturnStatus 454 | pimon_scanDevice(vmk_Device device) 455 | { 456 | VMK_ReturnStatus status = VMK_OK; 457 | pimon_CharDevPriv_t *privData; 458 | pimon_CharDevProps_t charDevProps; 459 | 460 | /* We only allow data to be transmitted in a particular size */ 461 | privData = vmk_HeapAlloc(pimon_Driver.heapID, sizeof(*privData)); 462 | if (privData == NULL) { 463 | status = VMK_NO_MEMORY; 464 | vmk_Warning(pimon_Driver.logger, "unable to allocate char dev priv data"); 465 | goto priv_alloc_failed; 466 | } 467 | privData->ioctlDataLen = sizeof(rpiq_MboxBuffer_t); 468 | 469 | charDevProps.driverHandle = pimon_Driver.driverHandle; 470 | charDevProps.logicalBusType = rpiq_logicalBusType; 471 | charDevProps.parentDevice = device; 472 | charDevProps.charDev = &rpiq_CharDev; 473 | charDevProps.logicalPort = 0; 474 | charDevProps.privData = privData; 475 | charDevProps.callbacks = &rpiq_CharDevCBs; 476 | 477 | status = pimon_charDevRegister(&charDevProps); 478 | 479 | priv_alloc_failed: 480 | return status; 481 | } 482 | 483 | /* 484 | *********************************************************************** 485 | * pimon_detachDevice -- 486 | * 487 | * Detaches the device from the driver. 488 | * 489 | * Results: 490 | * VMK_OK on success, error code otherwise 491 | * 492 | * Side Effects: 493 | * None 494 | *********************************************************************** 495 | */ 496 | VMK_ReturnStatus 497 | pimon_detachDevice(vmk_Device device) 498 | { 499 | VMK_ReturnStatus status = VMK_OK; 500 | 501 | return status; 502 | } 503 | 504 | /* 505 | *********************************************************************** 506 | * pimon_quiesceDevice -- 507 | * 508 | * Do nothing. 509 | * 510 | * Results: 511 | * VMK_OK on success, error code otherwise 512 | * 513 | * Side Effects: 514 | * None 515 | *********************************************************************** 516 | */ 517 | VMK_ReturnStatus 518 | pimon_quiesceDevice(vmk_Device device) 519 | { 520 | VMK_ReturnStatus status = VMK_OK; 521 | 522 | return status; 523 | } 524 | 525 | /* 526 | *********************************************************************** 527 | * pimon_startDevice -- 528 | * 529 | * Start the GPIO device. 530 | * 531 | * Results: 532 | * VMK_OK on success, error code otherwise 533 | * 534 | * Side Effects: 535 | * None 536 | *********************************************************************** 537 | */ 538 | VMK_ReturnStatus 539 | pimon_startDevice(vmk_Device device) 540 | { 541 | VMK_ReturnStatus status = VMK_OK; 542 | 543 | return status; 544 | } 545 | 546 | /* 547 | *********************************************************************** 548 | * pimon_forgetDevice -- 549 | * 550 | * Do nothing. 551 | * 552 | * Results: 553 | * None. 554 | * 555 | * Side Effects: 556 | * None 557 | *********************************************************************** 558 | */ 559 | void 560 | pimon_forgetDevice(vmk_Device device) 561 | { 562 | return; 563 | } -------------------------------------------------------------------------------- /pimon_types.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | /* 25 | * pimon_types.h -- 26 | * 27 | * Pimon module/driver types. 28 | */ 29 | 30 | #ifndef PIMON_TYPES_H 31 | #define PIMON_TYPES_H 32 | 33 | /***********************************************************************/ 34 | 35 | typedef struct pimon_Driver_t { 36 | vmk_Name driverName; 37 | vmk_ModuleID moduleID; 38 | vmk_HeapID heapID; 39 | vmk_Driver driverHandle; 40 | vmk_IOResource resHandle; 41 | vmk_LogComponent logger; 42 | } pimon_Driver_t; 43 | 44 | /***********************************************************************/ 45 | 46 | #endif /* PIMON_TYPES_H */ -------------------------------------------------------------------------------- /pyUtil/pimonLib/__init__.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # UW library interfacing with the PiMon driver. 3 | # Copyright (c) 2020 Tom Hebel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | ################################################################################ 23 | 24 | import math 25 | import struct 26 | import fcntl 27 | 28 | ######################################################################### 29 | 30 | PIMON_DEVICE_PATH = '/dev/vmgfx33' 31 | 32 | ######################################################################### 33 | 34 | RPIQ_BUFFER_LEN = 32 35 | 36 | RPIQ_PROCESS_REQ = 0 37 | 38 | # 39 | # PiMon ioctl commands 40 | # 41 | RPIQ_CHAN_MBOX_PROP_ARM2VC = 8 42 | 43 | # 44 | # RPIQ tags 45 | # 46 | 47 | RPIQ_MBOX_TAG_FWREV = 0x00000001 48 | RPIQ_MBOX_FWREV_LEN = 0x4 49 | 50 | RPIQ_MBOX_TAG_BOARDMODEL = 0x00010001 51 | RPIQ_MBOX_BOARDMODEL_LEN = 0x4 52 | 53 | RPIQ_MBOX_TAG_BOARDREV = 0x00010002 54 | RPIQ_MBOX_BOARDREV_LEN = 0x4 55 | 56 | RPIQ_MBOX_TAG_BOARDMAC = 0x00010003 57 | RPIQ_MBOX_BOARDMAC_LEN = 0x6 58 | 59 | RPIQ_MBOX_TAG_BOARDSERIAL = 0x00010004 60 | RPIQ_MBOX_BOARDSERIAL_LEN = 0x8 61 | 62 | RPIQ_MBOX_TAG_GET_TEMP = 0x00030006 63 | RPIQ_MBOX_GET_TEMP_LEN = 0x8 64 | 65 | RPIQ_MBOX_TAG_ARMMEM = 0x00010005 66 | RPIQ_MBOX_ARMMEM_LEN = 0x8 67 | 68 | ######################################################################### 69 | # PiMon -- 70 | # 71 | # PiMon char dev interface class. 72 | ######################################################################### 73 | class PiMon: 74 | pimonDev = open(PIMON_DEVICE_PATH, 'r+b') 75 | def __del__(self): 76 | self.pimonDev.close() 77 | 78 | ######################################################################### 79 | # getFWFev -- 80 | # 81 | ######################################################################### 82 | def getFWRev(self): 83 | try: 84 | ioctlData = bytearray(struct.pack('Q', unpacked) 159 | masked = int(struct.unpack('> macShift 160 | out = masked 161 | except Exception as e: 162 | print(e) 163 | return 0 164 | return out 165 | 166 | ######################################################################### 167 | # getBoardSerial -- 168 | # 169 | ######################################################################### 170 | def getBoardSerial(self): 171 | try: 172 | ioctlData = bytearray(struct.pack('dmaHeapID, 77 | sizeof(*rpiq_MboxDMABuffer.ptr), 78 | RPIQ_DMA_MBOX_ALIGNMENT); 79 | 80 | /* Verify that mbox buffer is below 2GB */ 81 | vmk_VA2MA((vmk_VA)rpiq_MboxDMABuffer.ptr, 0, &dmaBufPtrMA); 82 | if (dmaBufPtrMA > RPIQ_DMA_MAX_ADDR) { 83 | status = VMK_NO_MEMORY; 84 | vmk_Warning(pimon_Driver->logger, 85 | "unable to allocate below 2GB"); 86 | goto dma_addr_invalid; 87 | } 88 | 89 | /* 90 | * Init lock 91 | */ 92 | 93 | lockProps.moduleID = pimon_Driver->moduleID; 94 | lockProps.heapID = pimon_Driver->heapID; 95 | status = vmk_NameInitialize(&lockProps.name, RPIQ_DMA_LOCK_NAME); 96 | if (status != VMK_OK) { 97 | vmk_Warning(pimon_Driver->logger, 98 | "failed to init DMA buffer lock name: %s", 99 | vmk_StatusToString(status)); 100 | goto lock_init_failed; 101 | } 102 | 103 | lockProps.type = VMK_SPINLOCK; 104 | lockProps.domain = VMK_LOCKDOMAIN_INVALID; 105 | lockProps.rank = VMK_SPINLOCK_UNRANKED; 106 | status = vmk_SpinlockCreate(&lockProps, &rpiq_MboxDMABuffer.lock); 107 | if (status != VMK_OK) { 108 | vmk_Warning(pimon_Driver->logger, 109 | "failed to create DMA buffer spinlock: %s", 110 | vmk_StatusToString(status)); 111 | goto lock_init_failed; 112 | } 113 | 114 | lock_init_failed: 115 | dma_addr_invalid: 116 | vmk_HeapFree(rpiq_Device->dmaHeapID, rpiq_MboxDMABuffer.ptr); 117 | return status; 118 | } 119 | 120 | /* 121 | *********************************************************************** 122 | * rpiq_drvCleanUp -- 123 | * 124 | * Clean up the RPIQ driver. 125 | * 126 | * Results: 127 | * VMK_OK on success, error code otherwise 128 | * 129 | * Side Effects: 130 | * None 131 | *********************************************************************** 132 | */ 133 | void 134 | rpiq_drvCleanUp() 135 | { 136 | vmk_SpinlockDestroy(rpiq_MboxDMABuffer.lock); 137 | vmk_HeapFree(rpiq_Device->dmaHeapID, rpiq_MboxDMABuffer.ptr); 138 | } 139 | 140 | /* 141 | *********************************************************************** 142 | * rpiq_mboxDrain -- 143 | * 144 | * Drain the VideoCore mailbox. 145 | * 146 | * Results: 147 | * VMK_OK on success 148 | * VMK_TIMEOUT otherwise 149 | * 150 | * Side Effects: 151 | * None. 152 | *********************************************************************** 153 | */ 154 | VMK_ReturnStatus 155 | rpiq_mboxDrain() 156 | { 157 | int retries = 0; 158 | vmk_uint32 mboxStatus, mboxVal; 159 | 160 | do { 161 | vmk_MappedResourceRead32(&rpiq_Device->mmioMappedAddr, 162 | RPIQ_MBOX_STATUS, 163 | &mboxStatus); 164 | if (mboxStatus == RPIQ_MBOX_EMPTY) { 165 | return VMK_OK; 166 | } 167 | RPIQ_MEM_BARRIER(); 168 | vmk_MappedResourceRead32(&rpiq_Device->mmioMappedAddr, 169 | RPIQ_MBOX_READ, 170 | &mboxVal); 171 | } while (++retries < RPIQ_MBOX_MAX_RETRIES); 172 | 173 | return VMK_TIMEOUT; 174 | } 175 | 176 | /* 177 | *********************************************************************** 178 | * rpiq_mboxStatusCleared -- 179 | * 180 | * Spin until VC mailbox status flag is clear. 181 | * 182 | * Results: 183 | * VMK_OK on success 184 | * VMK_TIMEOUT otherwise 185 | * 186 | * Side Effects: 187 | * None. 188 | *********************************************************************** 189 | */ 190 | VMK_ReturnStatus 191 | rpiq_mboxStatusCleared(vmk_uint32 status) 192 | { 193 | int retries = 0; 194 | vmk_uint32 mboxStatus; 195 | 196 | do { 197 | vmk_MappedResourceRead32(&rpiq_Device->mmioMappedAddr, 198 | RPIQ_MBOX_STATUS, 199 | &mboxStatus); 200 | if ((mboxStatus & status) == 0) { 201 | return VMK_OK; 202 | } 203 | RPIQ_MEM_BARRIER(); 204 | } while (++retries < RPIQ_MBOX_MAX_RETRIES); 205 | 206 | return VMK_TIMEOUT; 207 | } 208 | 209 | /* 210 | *********************************************************************** 211 | * rpiq_mboxSend -- 212 | * 213 | * Send a request to a VideoCore mailbox and receive its response. 214 | * 215 | * Results: 216 | * VMK_OK on success, error code otherwise 217 | * 218 | * Side Effects: 219 | * None. 220 | *********************************************************************** 221 | */ 222 | VMK_ReturnStatus 223 | rpiq_mboxSend(rpiq_MboxChannel_t channel, // IN 224 | rpiq_MboxBuffer_t *buffer) // IN/OUT 225 | { 226 | VMK_ReturnStatus status = VMK_OK; 227 | rpiq_MboxDMABuffer_t *dmaBuf = &rpiq_MboxDMABuffer; 228 | rpiq_MboxBuffer_t *dmaBufPtr = dmaBuf->ptr; 229 | vmk_MA dmaBufPtrMA; 230 | vmk_uint32 mboxIn; 231 | vmk_uint32 mboxOut; 232 | vmk_uint32 mboxReadRetries = 0; 233 | 234 | /* 235 | * Drain mbox 236 | */ 237 | if (rpiq_mboxDrain() != VMK_OK) { 238 | status = VMK_TIMEOUT; 239 | vmk_Warning(pimon_Driver->logger, "failed to drain mailbox"); 240 | goto mbox_drain_timeout; 241 | } 242 | 243 | /* Lock the DMA buffer */ 244 | vmk_SpinlockLock(dmaBuf->lock); 245 | 246 | /* 247 | *------------------------------------------------------------------- 248 | * MBOX WRITE 249 | *------------------------------------------------------------------- 250 | */ 251 | 252 | /* 253 | * Wait for mbox to become empty 254 | */ 255 | if (rpiq_mboxStatusCleared(RPIQ_MBOX_FULL) != VMK_OK) { 256 | status = VMK_TIMEOUT; 257 | vmk_Warning(pimon_Driver->logger, 258 | "timeout waiting for mbox to become empty"); 259 | goto mbox_empty_timeout; 260 | } 261 | 262 | /* 263 | * Copy buffer to location accessible by VC DMA 264 | */ 265 | vmk_Memcpy(dmaBufPtr, buffer, sizeof(*dmaBufPtr)); 266 | 267 | /* 268 | * Prepare mbox input data 269 | */ 270 | vmk_VA2MA((vmk_VA)dmaBufPtr, 0, &dmaBufPtrMA); 271 | mboxIn = ((vmk_uint32)dmaBufPtrMA 272 | | RPIQ_DMA_COHERENT_ADDR 273 | | (vmk_uint32)channel); 274 | 275 | RPIQ_MEM_BARRIER(); 276 | 277 | vmk_MappedResourceWrite32(&rpiq_Device->mmioMappedAddr, 278 | RPIQ_MBOX_WRITE, 279 | mboxIn); 280 | 281 | RPIQ_MEM_BARRIER(); 282 | 283 | /* 284 | * This needs to be here. 285 | */ 286 | RPIQ_INVAL_DCACHE((void *)dmaBufPtr); 287 | 288 | /* 289 | *------------------------------------------------------------------- 290 | * MBOX READ 291 | *------------------------------------------------------------------- 292 | */ 293 | 294 | /* 295 | * Wait for mbox to fill up 296 | */ 297 | if (rpiq_mboxStatusCleared(RPIQ_MBOX_EMPTY) != VMK_OK) { 298 | status = VMK_TIMEOUT; 299 | vmk_Warning(pimon_Driver->logger, 300 | "timeout waiting for mbox to become full"); 301 | goto mbox_full_timeout; 302 | } 303 | 304 | RPIQ_MEM_BARRIER(); 305 | 306 | /* 307 | * Perform read 308 | */ 309 | do { 310 | vmk_MappedResourceRead32(&rpiq_Device->mmioMappedAddr, 311 | RPIQ_MBOX_READ, 312 | &mboxOut); 313 | 314 | RPIQ_MEM_BARRIER(); 315 | 316 | ++mboxReadRetries; 317 | if (mboxReadRetries >= RPIQ_MBOX_MAX_RETRIES) { 318 | status = VMK_TIMEOUT; 319 | vmk_Warning(pimon_Driver->logger, 320 | "RPIQ mailbox read failed after %d retries", 321 | mboxReadRetries); 322 | goto mbox_read_attempts; 323 | } 324 | } while ((mboxOut & RPIQ_MBOX_CHAN_MASK) != channel); 325 | 326 | RPIQ_MEM_BARRIER(); 327 | 328 | /* 329 | * Something went wrong, in & out buffers don't match 330 | */ 331 | if (mboxOut != mboxIn) { 332 | status = VMK_FAILURE; 333 | vmk_Warning(pimon_Driver->logger, 334 | "mismatch in mbox input %p and output %p", 335 | mboxIn, 336 | mboxOut); 337 | goto mbox_response_mismatch; 338 | } 339 | 340 | /* 341 | * Invalidate cache and copy data back to in/out buffer 342 | */ 343 | 344 | RPIQ_INVAL_DCACHE((void *)dmaBufPtr); 345 | vmk_Memcpy(buffer, dmaBufPtr, sizeof(*buffer)); 346 | if (buffer->header.requestResponse != RPIQ_MBOX_SUCCESS) { 347 | status = VMK_FAILURE; 348 | vmk_Warning(pimon_Driver->logger, 349 | "no response data received"); 350 | } 351 | 352 | /* Unlock the DMA buffer */ 353 | vmk_SpinlockUnlock(dmaBuf->lock); 354 | 355 | return VMK_OK; 356 | 357 | mbox_response_mismatch: 358 | mbox_read_attempts: 359 | mbox_full_timeout: 360 | mbox_empty_timeout: 361 | mbox_drain_timeout: 362 | vmk_SpinlockUnlock(dmaBuf->lock); 363 | return status; 364 | } 365 | 366 | /* 367 | *********************************************************************** 368 | * rpiq_mmioOpenCB -- 369 | * 370 | * Callback used by char dev driver when the /dev/vmgfx32 file is 371 | * opened. 372 | * 373 | * Results: 374 | * VMK_OK on success, error code otherwise 375 | * 376 | * Side Effects: 377 | * None. 378 | *********************************************************************** 379 | */ 380 | VMK_ReturnStatus 381 | rpiq_mmioOpenCB(vmk_CharDevFdAttr *attr) 382 | { 383 | VMK_ReturnStatus status = VMK_OK; 384 | 385 | vmk_Log(pimon_Driver->logger, "Opening file."); 386 | 387 | return status; 388 | } 389 | 390 | /* 391 | *********************************************************************** 392 | * rpiq_mmioCloseCB -- 393 | * 394 | * Callback used by char dev driver when the /dev/vmgfx32 file is 395 | * closed. 396 | * 397 | * Results: 398 | * VMK_OK on success, error code otherwise 399 | * 400 | * Side Effects: 401 | * None. 402 | *********************************************************************** 403 | */ 404 | VMK_ReturnStatus 405 | rpiq_mmioCloseCB(vmk_CharDevFdAttr *attr) 406 | { 407 | VMK_ReturnStatus status = VMK_OK; 408 | 409 | vmk_Log(pimon_Driver->logger, "Closing file."); 410 | 411 | return status; 412 | } 413 | 414 | /* 415 | *********************************************************************** 416 | * rpiq_mmioIoctlCB -- 417 | * 418 | * Callback used by char dev driver for I/O control. The RPIQ char dev 419 | * file only supports access via ioctl, since it allows for sending 420 | * and receiving data structures easily. 421 | * 422 | * Results: 423 | * VMK_OK on success, error code otherwise 424 | * 425 | * Side Effects: 426 | * None. 427 | *********************************************************************** 428 | */ 429 | VMK_ReturnStatus 430 | rpiq_mmioIoctlCB(unsigned int channel, // IN 431 | void *data, // IN/OUT 432 | vmk_ByteCount ioctlDataLen) // IN 433 | { 434 | VMK_ReturnStatus status = VMK_OK; 435 | rpiq_MboxBuffer_t *buffer; 436 | 437 | // TODO: add support for arbitrary length mailbox data, to allow for things 438 | // like requesting console data. 439 | 440 | /* 441 | * Sanity checks 442 | */ 443 | 444 | if (data == NULL || ioctlDataLen != sizeof(*buffer)) { 445 | status = VMK_BAD_PARAM; 446 | vmk_Warning(pimon_Driver->logger, "invalid ioctl data"); 447 | goto invalid_ioctl_data; 448 | } 449 | 450 | if (channel < RPIQ_CHAN_MBOX_MIN 451 | || channel > RPIQ_CHAN_MBOX_MAX) { 452 | status = VMK_BAD_PARAM; 453 | vmk_Warning(pimon_Driver->logger, "ioctl command %d invalid", channel); 454 | goto invalid_ioct_channel; 455 | } 456 | 457 | // TODO: Should we check the tag validity too? 458 | buffer = (rpiq_MboxBuffer_t *)data; 459 | if (buffer->endTag != 0 460 | || buffer->padding[0] != 0 461 | || buffer->padding[1] != 0 462 | || buffer->header.bufLen != sizeof(*buffer)) { 463 | status = VMK_BAD_PARAM; 464 | vmk_Warning(pimon_Driver->logger, "invalid mailbox buffer"); 465 | 466 | goto invalid_mbox_buffer; 467 | } 468 | 469 | #ifdef RPIQ_DEBUG 470 | { 471 | vmk_Log(pimon_Driver->logger, 472 | "bufLen=%d rqRp=%d tag=0x%x rpLen=%d rqLen=%d endTag=%d" 473 | " padding[0]=%d padding[1]=%d", 474 | buffer->header.bufLen, 475 | buffer->header.requestResponse, 476 | buffer->header.tag, 477 | buffer->header.responseLen, 478 | buffer->header.requestLen, 479 | buffer->endTag, 480 | buffer->padding[0], 481 | buffer->padding[1]); 482 | } 483 | #endif /* RPIQ_DEBUG */ 484 | 485 | /* 486 | * Mbox I/O 487 | */ 488 | 489 | status = rpiq_mboxSend(channel, buffer); 490 | if (status != VMK_OK) { 491 | vmk_Warning(pimon_Driver->logger, 492 | "unable to send to mailbox: %s", 493 | vmk_StatusToString(status)); 494 | goto mbox_send_failed; 495 | } 496 | 497 | return VMK_OK; 498 | 499 | mbox_send_failed: 500 | invalid_mbox_buffer: 501 | invalid_ioct_channel: 502 | invalid_ioctl_data: 503 | return status; 504 | } 505 | 506 | /* 507 | *********************************************************************** 508 | * rpiq_mmioReadCB -- 509 | * 510 | * Callback used by char dev driver when the file is read. Currently 511 | * not supported, as all I/O is done via ioctl. 512 | * 513 | * Results: 514 | * VMK_OK on success, error code otherwise 515 | * 516 | * Side Effects: 517 | * None. 518 | *********************************************************************** 519 | */ 520 | VMK_ReturnStatus 521 | rpiq_mmioReadCB(char *buffer, 522 | vmk_ByteCount nbytes, 523 | vmk_loff_t *ppos, 524 | vmk_ByteCountSigned *nread) 525 | { 526 | return VMK_NOT_SUPPORTED; 527 | } 528 | 529 | /* 530 | *********************************************************************** 531 | * rpiq_mmioWrite -- 532 | * 533 | * Callback used by char dev driver when the file is written to. 534 | * Currently not supported, as all I/O is done via ioctl. 535 | * 536 | * Results: 537 | * VMK_OK on success, error code otherwise 538 | * 539 | * Side Effects: 540 | * None. 541 | *********************************************************************** 542 | */ 543 | VMK_ReturnStatus 544 | rpiq_mmioWriteCB(char *buffer, 545 | vmk_ByteCount nbytes, 546 | vmk_loff_t *ppos, 547 | vmk_ByteCountSigned *nwritten) 548 | { 549 | return VMK_NOT_SUPPORTED; 550 | } -------------------------------------------------------------------------------- /rpiq_drv.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************\ 2 | * Native ESXi on Arm driver for hardware monitoring on the Raspberry Pi. 3 | * Copyright (c) 2020 Tom Hebel 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | \******************************************************************************/ 23 | 24 | /* 25 | * rpiq_drv.h -- 26 | * 27 | * Definition for RPIQ device layer interface. 28 | */ 29 | 30 | #ifndef RPIQ_DRV_H 31 | #define RPIQ_DRV_H 32 | 33 | #include "pimon.h" 34 | #include "pimon_types.h" 35 | 36 | #define RPIQ_DEBUG 37 | 38 | /***********************************************************************/ 39 | 40 | #define RPIQ_DMA_HEAP_NAME "rpiqDmaHeap" 41 | 42 | #define RPIQ_DMA_COHERENT_ADDR 0xC0000000 43 | #define RPIQ_DMA_MAX_ADDR ((vmk_VA)1<<32) 44 | #define RPIQ_DMA_MBOX_ALIGNMENT 16 45 | 46 | /* Maximum mbox objects to allocate on DMA heap */ 47 | #define RPIQ_DMA_MBOX_OBJ_MAX 256 48 | 49 | #define RPIQ_MBOX_CHAN_MASK 15 50 | 51 | /* Very generous to avoid sporadic timeouts */ 52 | #define RPIQ_MBOX_MAX_RETRIES 0x4000 53 | 54 | /***********************************************************************/ 55 | 56 | /* 57 | * Offsets from RPIQ MMIO base 58 | */ 59 | #define RPIQ_MBOX_READ 0x0 60 | #define RPIQ_MBOX_STATUS 0x18 61 | #define RPIQ_MBOX_WRITE 0x20 62 | 63 | #define RPIQ_PROCESS_REQ 0x0 64 | #define RPIQ_MBOX_FULL (1 << 31) 65 | #define RPIQ_MBOX_EMPTY (1 << 30) 66 | #define RPIQ_MBOX_SUCCESS 0x80000000 67 | 68 | /* 69 | * Mbox tags 70 | */ 71 | 72 | /* Firmware */ 73 | #define RPIQ_MBOX_TAG_FWREV 0x00000001 74 | 75 | /* Hardware */ 76 | #define RPIQ_MBOX_TAG_BOARDMODEL 0x00010001 77 | #define RPIQ_MBOX_TAG_BOARDREV 0x00010002 78 | #define RPIQ_MBOX_TAG_BOARDMAC 0x00010003 79 | #define RPIQ_MBOX_TAG_BOARDSERIAL 0x00010004 80 | #define RPIQ_MBOX_TAG_ARMMEM 0x00010005 81 | #define RPIQ_MBOX_TAG_VCMEM 0x00010006 82 | #define RPIQ_MBOX_TAG_CLKS 0x00010007 83 | 84 | /* Config */ 85 | #define RPIQ_MBOX_TAG_CMDLINE 0x00050001 86 | 87 | /* Shared resources */ 88 | #define RPIQ_MBOX_TAG_DMACHAN 0x00060001 89 | 90 | /* Power */ 91 | #define RPIQ_MBOX_TAG_GETPWR 0x00020001 92 | #define RPIQ_MBOX_TAG_TIMING 0x00020002 93 | #define RPIQ_MBOX_TAG_SETPWR 0x00028001 94 | 95 | /* Clocks */ 96 | #define RPIQ_MBOX_TAG_GETCLK_STATE 0x00030001 97 | #define RPIQ_MBOX_TAG_SETCLK_STATE 0x00038001 98 | #define RPIQ_MBOX_TAG_GETCLK_RATE 0x00030002 99 | #define RPIQ_MBOX_TAG_SETCLK_RATE 0x00038002 100 | #define RPIQ_MBOX_TAG_GETCLK_MAX 0x00030004 101 | #define RPIQ_MBOX_TAG_GETCLK_MIN 0x00038007 102 | #define RPIQ_MBOX_TAG_GETCLK_TURBO 0x00030009 103 | #define RPIQ_MBOX_TAG_SETCLK_TURBO 0x00038009 104 | 105 | // TODO: the resto fot he mbox tags... 106 | 107 | #define RPIQ_MBOX_TAG_GET_TEMP 0x00030006 108 | 109 | #define RPIQ_INVALID_RESPONSE (~((vmk_uint32)0)) 110 | 111 | /***********************************************************************/ 112 | 113 | #define RPIQ_DMA_LOCK_NAME "dmaBufLock" 114 | 115 | #define RPIQ_MEM_BARRIER() asm volatile ("dsb sy" ::: "memory") 116 | 117 | /* 118 | * Should be sufficient as per p. 33 in: 119 | * https://web.wpi.edu/Pubs/ETD/Available/etd-012017-170924/unrestricted/green_archive.pdf 120 | */ 121 | #define RPIQ_INVAL_DCACHE(_va) \ 122 | asm volatile ("dc ivac, %0" :: "r" (_va)) 123 | 124 | #define RPIQ_DMA_CLEAN_DCACHE(_va) \ 125 | asm volatile ("dc civac, %0" :: "r" (_va)) 126 | 127 | /***********************************************************************/ 128 | 129 | typedef struct rpiq_Device_t { 130 | /* Object */ 131 | vmk_Bool initialized; 132 | vmk_atomic64 refCount; 133 | /* Device */ 134 | vmk_Device vmkDevice; 135 | vmk_ACPIDevice acpiDevice; 136 | vmk_ACPIInfo acpiInfo; 137 | /* MMIO */ 138 | vmk_MappedResourceAddress mmioMappedAddr; 139 | char *mmioBase; 140 | vmk_ByteCount mmioLen; 141 | /* DMA */ 142 | vmk_HeapID dmaHeapID; 143 | } rpiq_Device_t; 144 | 145 | /* 146 | * Data structures for passing data between UW and RPIQ interface. 147 | */ 148 | 149 | typedef struct rpiq_MboxHeader_t { 150 | vmk_uint32 bufLen; 151 | vmk_uint32 requestResponse; 152 | vmk_uint32 tag; 153 | vmk_uint32 responseLen; 154 | vmk_uint32 requestLen; 155 | } rpiq_MboxHeader_t; 156 | 157 | typedef struct rpiq_MboxBuffer_t { 158 | rpiq_MboxHeader_t header; 159 | /* Everything below be set to zero! */ 160 | vmk_uint32 endTag; 161 | vmk_uint32 padding[2]; 162 | } rpiq_MboxBuffer_t, rpiq_IoctlData_t; 163 | 164 | /* 165 | * RPIQ mailbox channels which selected using the ioctl cmd. 166 | */ 167 | typedef enum rpiq_MboxChannel_t { 168 | /* Update the min value when adding elements */ 169 | RPIQ_CHAN_MBOX_MIN = 8, 170 | RPIQ_CHAN_MBOX_PROP_ARM2VC = 8, 171 | RPIQ_CHAN_MBOX_MAX = RPIQ_CHAN_MBOX_PROP_ARM2VC, 172 | } rpiq_MboxChannel_t, rpiq_IoctlCommand_t; 173 | 174 | typedef struct rpiq_MboxDMABuffer_t { 175 | rpiq_MboxBuffer_t *ptr; 176 | vmk_Lock lock; 177 | } rpiq_MboxDMABuffer_t; 178 | 179 | /***********************************************************************/ 180 | 181 | VMK_ReturnStatus rpiq_drvInit(pimon_Driver_t *driver, 182 | rpiq_Device_t *adapter); 183 | 184 | void rpiq_drvCleanUp(); 185 | 186 | VMK_ReturnStatus rpiq_mboxRead(rpiq_MboxChannel_t channel, 187 | vmk_uint32 *response); 188 | 189 | VMK_ReturnStatus rpiq_mboxWrite(rpiq_MboxChannel_t channel, 190 | rpiq_MboxBuffer_t *buffer); 191 | 192 | VMK_ReturnStatus rpiq_mmioOpenCB(vmk_CharDevFdAttr *attr); 193 | 194 | VMK_ReturnStatus rpiq_mmioCloseCB(vmk_CharDevFdAttr *attr); 195 | 196 | VMK_ReturnStatus rpiq_mmioIoctlCB(unsigned int cmd, 197 | void *data, 198 | vmk_ByteCount ioctlDataLen); 199 | 200 | VMK_ReturnStatus rpiq_mmioReadCB(char *buffer, 201 | vmk_ByteCount nbytes, 202 | vmk_loff_t *ppos, 203 | vmk_ByteCountSigned *nread); 204 | 205 | VMK_ReturnStatus rpiq_mmioWriteCB(char *buffer, 206 | vmk_ByteCount nbytes, 207 | vmk_loff_t *ppos, 208 | vmk_ByteCountSigned *nwritten); 209 | 210 | /***********************************************************************/ 211 | 212 | #endif /* RPIQ_DRV_H */ --------------------------------------------------------------------------------