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);
|
bool backing_filter(const char* file);
|
||||||
|
|
||||||
/// @brief Lists the backings in the pool.
|
/// @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.
|
/// @return The result of the operation.
|
||||||
result_t list_backings(char*** _backings);
|
result_t list_backings(char*** _backings);
|
||||||
|
|
||||||
|
@ -236,6 +236,7 @@ result_t list_containers(char*** _containers) {
|
|||||||
// List the files in the container pool
|
// List the files in the container pool
|
||||||
result = list_files(_containers, pool_path, container_filter);
|
result = list_files(_containers, pool_path, container_filter);
|
||||||
|
|
||||||
|
// Free the path
|
||||||
free(pool_path);
|
free(pool_path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ result_t reset_container(const char* container);
|
|||||||
bool container_filter(const char* file);
|
bool container_filter(const char* file);
|
||||||
|
|
||||||
/// @brief Lists the containers in the pool.
|
/// @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.
|
/// @return The result of the operation.
|
||||||
result_t list_containers(char*** _containers);
|
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 <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "pci.h"
|
||||||
|
|
||||||
#define ALIAS(...) \
|
#define ALIAS(...) \
|
||||||
(const char*[]) { __VA_ARGS__, NULL }
|
(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.
|
/// @return The result of the operation.
|
||||||
result_t copy_file(const char* src, const char* dst);
|
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.
|
/// @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 pointer array to store the resulting array in. The caller is responsible for freeing the strings and the array.
|
/// @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 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.
|
/// @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.
|
/// @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");
|
return failure("Too many ISO images");
|
||||||
|
|
||||||
// Generate the XML
|
// Generate the XML
|
||||||
return foramt(_xml, "<disk type='file' device='cdrom'>"
|
return format(_xml, "<disk type='file' device='cdrom'>"
|
||||||
" <driver name='qemu' type='raw'/>"
|
" <driver name='qemu' type='raw'/>"
|
||||||
" <source file='%s'/>"
|
" <source file='%s'/>"
|
||||||
" <target dev='sd%c' bus='sata'/>"
|
" <target dev='sd%c' bus='sata'/>"
|
||||||
|
Loading…
Reference in New Issue
Block a user