diff --git a/src/backing.h b/src/backing.h index 6139fcf..5d64ca1 100755 --- a/src/backing.h +++ b/src/backing.h @@ -57,7 +57,7 @@ result_t remove_backing(const char* backing); bool backing_filter(const char* file); /// @brief Lists the backings in the pool. -/// @param _backings The string pointer array to store the resulting array in. The caller is responsible for freeing the strings and the array. +/// @param _backings The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array. /// @return The result of the operation. result_t list_backings(char*** _backings); diff --git a/src/container.c b/src/container.c index 4d156d6..4d983fb 100755 --- a/src/container.c +++ b/src/container.c @@ -236,6 +236,7 @@ result_t list_containers(char*** _containers) { // List the files in the container pool result = list_files(_containers, pool_path, container_filter); + // Free the path free(pool_path); return result; } diff --git a/src/container.h b/src/container.h index 24c9ecc..f091a0e 100755 --- a/src/container.h +++ b/src/container.h @@ -59,7 +59,7 @@ result_t reset_container(const char* container); bool container_filter(const char* file); /// @brief Lists the containers in the pool. -/// @param _containers The string pointer array to store the resulting array in. The caller is responsible for freeing the strings and the array. +/// @param _containers The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array. /// @return The result of the operation. result_t list_containers(char*** _containers); diff --git a/src/pci.c b/src/pci.c new file mode 100644 index 0000000..1868610 --- /dev/null +++ b/src/pci.c @@ -0,0 +1,119 @@ +#include "pci.h" + +#include +#include +#include +#include +#include +#include +#include + +result_t check_pci_address(const char* pci) { + // Check the length of the string (should be 12 characters, eg. 0000:00:00.0) + if (strlen(pci) != 12) + return failure("Invalid PCI address '%s'. Valid PCI addresses are in the format '0000:00:00.0'.", pci); + + // Check the format of the string + for (int i = 0; i < 12; i++) + if (i == 4 || i == 7) { + if (pci[i] != ':') + return failure("Invalid PCI address '%s'. Missing colon at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci, i); + } else if (i == 10) { + if (pci[i] != '.') + return failure("Invalid PCI address '%s'. Missing dot at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci, i); + } else { + if (!isxdigit(pci[i])) + return failure("Invalid PCI address '%s'. Non-hexadecimal character '%c' at position %d. Valid PCI addresses are in the format '0000:00:00.0'.", pci, pci[i], i); + } + + return success(); +} + +result_t check_pci_exists(const char* pci) { + // Check the PCI address format + result_t result = check_pci_address(pci); + if (result != success()) + return result; + + // Check if the PCI address exists by checking if the sysfs directory exists + char* path; + result = format(&path, "/sys/bus/pci/devices/%s", pci); + if (result != success()) + return result; + + // Check that the directory exists + struct stat st; + if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) { + free(path); + return failure("PCI address '%s' does not exist.", pci); + } + + // Free the path + free(path); + + return success(); +} + +result_t get_iommu_group(int* _group, const char* pci) { + // Initialize the output parameter + *_group = -1; + + // Check that the PCI address exists + result_t result = check_pci_exists(pci); + if (result != success()) + return result; + + // Get the IOMMU group if the PCI device has one + char* path; + result = format(&path, "/sys/bus/pci/devices/%s/iommu_group", pci); + if (result != success()) + return result; + + // Check that the file exists and is a symlink + struct stat st; + if (lstat(path, &st) != 0 || !S_ISLNK(st.st_mode)) { + free(path); + return failure("PCI address '%s' does not have an IOMMU group.", pci); + } + + // Read the IOMMU group by getting the path of the symlink, and getting the basename of the path + char iommu_group_path[256]; // 256 should be enough for the path + ssize_t len = readlink(path, iommu_group_path, sizeof(iommu_group_path) - 1); + + // Check for errors during the readlink call + if (len == -1) { + free(path); + return failure("Failed to read IOMMU group of PCI address '%s'.", pci); + } + + // Null-terminate the path + iommu_group_path[len] = '\0'; + + // Get the basename of the path, and try to parse it as an integer + *_group = atoi(basename(iommu_group_path)); + + return success(); +} + +bool pci_filter(const char* file) { + // Check that the PCI device exists + return check_pci_exists(file) == success(); +} + +result_t get_iommu_group_devices(char*** _devices, int group) { + // Initialize the output parameters + *_devices = NULL; + + // Get the path of the IOMMU group + char* path; + result_t result = format(&path, "/sys/kernel/iommu_groups/%d/devices", group); + if (result != success()) + return result; + + // List the devices in the IOMMU group + result = list_files(_devices, path, pci_filter); + + // Free the path + free(path); + return result; +} diff --git a/src/pci.h b/src/pci.h new file mode 100644 index 0000000..019b668 --- /dev/null +++ b/src/pci.h @@ -0,0 +1,30 @@ +#pragma once + +#include "utils.h" + +/// @brief Checks whether the given string is a valid PCI address. If the string is not a valid PCI address, the call will return a failure result with an error message. +/// @param pci The string to check. +/// @return The result of the operation. +result_t check_pci_address(const char* pci); + +/// @brief Checks whether the given PCI address exists. If the PCI address does not exist, the call will return a failure result with an error message. +/// @param pci The PCI address to check. +/// @return The result of the operation. +result_t check_pci_exists(const char* pci); + +/// @brief Gets the IOMMU group of the given PCI address. +/// @param _group The integer pointer to store the resulting IOMMU group in. +/// @param pci The PCI address to get the IOMMU group of. +/// @return The result of the operation. +result_t get_iommu_group(int* _group, const char* pci); + +/// @brief Checks that the given file is a PCI device. This function is used as a filter for listing PCI devices. +/// @param file The file to check. +/// @return Whether the file is a PCI device. +bool pci_filter(const char* file); + +/// @brief Get the PCI devices in the given IOMMU group. +/// @param _devices The string array pointer to store the resulting devices in. The caller is responsible for freeing the strings and the array. +/// @param group The IOMMU group to get the devices of. +/// @return The result of the operation. +result_t get_iommu_group_devices(char*** _devices, int group); \ No newline at end of file diff --git a/src/sandbox.c b/src/sandbox.c index 582fe7f..509983e 100755 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -9,6 +9,8 @@ #include #include +#include "pci.h" + #define ALIAS(...) \ (const char*[]) { __VA_ARGS__, NULL } diff --git a/src/utils.h b/src/utils.h index 0aeb173..be0a28a 100755 --- a/src/utils.h +++ b/src/utils.h @@ -120,8 +120,8 @@ result_t write_file(const char* path, const char* str); /// @return The result of the operation. result_t copy_file(const char* src, const char* dst); -/// @brief Lists the files in the given directory, and stores the resulting array in the given string pointer array. A filter can be provided to only include files that match the filter. -/// @param _files The string pointer array to store the resulting array in. The caller is responsible for freeing the strings and the array. +/// @brief Lists the files in the given directory, and stores the resulting array in the given string array pointer. A filter can be provided to only include files that match the filter. +/// @param _files The string array pointer to store the resulting array in. The caller is responsible for freeing the strings and the array. /// @param path The path to the directory to list the files in. /// @param filter The filter to use to only include files that match the filter. This parameter can be NULL if no filter is needed. /// @return The result of the operation. diff --git a/src/xml.c b/src/xml.c index 730db67..6795ac2 100644 --- a/src/xml.c +++ b/src/xml.c @@ -8,7 +8,7 @@ result_t generate_iso_xml(char** _xml, char* iso_path, int index) { return failure("Too many ISO images"); // Generate the XML - return foramt(_xml, "" + return format(_xml, "" " " " " " "