Started implementing an utility to manage PCI passthrough
This commit is contained in:
parent
a36277c002
commit
29a129f2b8
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
119
src/pci.c
Normal file
119
src/pci.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "pci.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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;
|
||||
}
|
30
src/pci.h
Normal file
30
src/pci.h
Normal file
@ -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);
|
@ -9,6 +9,8 @@
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pci.h"
|
||||
|
||||
#define ALIAS(...) \
|
||||
(const char*[]) { __VA_ARGS__, NULL }
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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, "<disk type='file' device='cdrom'>"
|
||||
return format(_xml, "<disk type='file' device='cdrom'>"
|
||||
" <driver name='qemu' type='raw'/>"
|
||||
" <source file='%s'/>"
|
||||
" <target dev='sd%c' bus='sata'/>"
|
||||
|
Loading…
Reference in New Issue
Block a user