mirror of
https://github.com/mbilker/vgpu_unlock-rs.git
synced 2026-06-07 23:57:45 +02:00
lib: derive many field names from Arc's mdev GPU project
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
pub struct CStrFormat<'a>(pub &'a [u8]);
|
pub struct CStrFormat<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
@@ -34,6 +34,54 @@ impl<T: fmt::LowerHex> fmt::Display for HexFormat<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct HexFormatSlice<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for HexFormatSlice<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for HexFormatSlice<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
if self.0.is_empty() {
|
||||||
|
f.write_str("[]")
|
||||||
|
} else {
|
||||||
|
f.write_str("0x")?;
|
||||||
|
|
||||||
|
for v in self.0.iter() {
|
||||||
|
write!(f, "{:02x}", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WideCharFormat<'a>(pub &'a [u16]);
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for WideCharFormat<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_char('"')?;
|
||||||
|
|
||||||
|
fmt::Display::fmt(self, f)?;
|
||||||
|
|
||||||
|
f.write_char('"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for WideCharFormat<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
for item in char::decode_utf16(self.0.iter().copied().take_while(|&ch| ch != 0)) {
|
||||||
|
f.write_char(item.unwrap_or(char::REPLACEMENT_CHARACTER))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct StraightFormat<T>(pub T);
|
pub struct StraightFormat<T>(pub T);
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for StraightFormat<T> {
|
impl<T: fmt::Debug> fmt::Debug for StraightFormat<T> {
|
||||||
|
|||||||
220
src/lib.rs
220
src/lib.rs
@@ -5,6 +5,8 @@
|
|||||||
//! - DualCoder for the original [`vgpu_unlock`](https://github.com/DualCoder/vgpu_unlock)
|
//! - DualCoder for the original [`vgpu_unlock`](https://github.com/DualCoder/vgpu_unlock)
|
||||||
//! - DualCoder, snowman, Felix, Elec for vGPU profile modification at runtime
|
//! - DualCoder, snowman, Felix, Elec for vGPU profile modification at runtime
|
||||||
//! - NVIDIA for their open-source driver [sources](https://github.com/NVIDIA/open-gpu-kernel-modules)
|
//! - NVIDIA for their open-source driver [sources](https://github.com/NVIDIA/open-gpu-kernel-modules)
|
||||||
|
//! - Arc Compute for their work on Mdev-GPU and GVM documenting more field names in the vGPU
|
||||||
|
//! configuration structure
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -32,7 +34,7 @@ mod ioctl;
|
|||||||
mod log;
|
mod log;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::format::{CStrFormat, HexFormat, StraightFormat};
|
use crate::format::{CStrFormat, HexFormat, HexFormatSlice, StraightFormat, WideCharFormat};
|
||||||
use crate::ioctl::_IOCWR;
|
use crate::ioctl::_IOCWR;
|
||||||
use crate::log::{error, info};
|
use crate::log::{error, info};
|
||||||
|
|
||||||
@@ -164,30 +166,34 @@ struct VgpuStart {
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct VgpuConfig {
|
struct VgpuConfig {
|
||||||
gpu_type: u32,
|
vgpu_type: u32,
|
||||||
card_name: [u8; 32],
|
vgpu_name: [u8; 32],
|
||||||
vgpu_type: [u8; 160],
|
vgpu_class: [u8; 32],
|
||||||
|
vgpu_signature: [u8; 128],
|
||||||
features: [u8; 128],
|
features: [u8; 128],
|
||||||
max_instances: u32,
|
max_instances: u32,
|
||||||
num_displays: u32,
|
num_heads: u32,
|
||||||
display_width: u32,
|
max_resolution_x: u32,
|
||||||
display_height: u32,
|
max_resolution_y: u32,
|
||||||
max_pixels: u32,
|
max_pixels: u32,
|
||||||
frl_config: u32,
|
frl_config: u32,
|
||||||
cuda_enabled: u32,
|
cuda_enabled: u32,
|
||||||
ecc_supported: u32,
|
ecc_supported: u32,
|
||||||
mig_instance_size: u32,
|
mig_instance_size: u32,
|
||||||
multi_vgpu_supported: u32,
|
multi_vgpu_supported: u32,
|
||||||
pci_id: u64,
|
vdev_id: u64,
|
||||||
pci_device_id: u64,
|
pdev_id: u64,
|
||||||
framebuffer: u64,
|
fb_length: u64,
|
||||||
mappable_video_size: u64,
|
mappable_video_size: u64,
|
||||||
framebuffer_reservation: u64,
|
fb_reservation: u64,
|
||||||
encoder_capacity: u64,
|
encoder_capacity: u32,
|
||||||
bar1_length: u64,
|
bar1_length: u64,
|
||||||
frl_enabled: u32,
|
frl_enable: u32,
|
||||||
blob: [u8; 256],
|
adapter_name: [u8; 64],
|
||||||
license_type: [u8; 1156],
|
adapter_name_unicode: [u16; 64],
|
||||||
|
short_gpu_name_string: [u8; 64],
|
||||||
|
licensed_product_name: [u8; 128],
|
||||||
|
vgpu_extra_params: [u8; 1024],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -222,9 +228,11 @@ struct VgpuProfileOverride<'a> {
|
|||||||
mappable_video_size: Option<u64>,
|
mappable_video_size: Option<u64>,
|
||||||
#[serde(default, with = "human_number")]
|
#[serde(default, with = "human_number")]
|
||||||
framebuffer_reservation: Option<u64>,
|
framebuffer_reservation: Option<u64>,
|
||||||
encoder_capacity: Option<u64>,
|
encoder_capacity: Option<u32>,
|
||||||
bar1_length: Option<u64>,
|
bar1_length: Option<u64>,
|
||||||
frl_enabled: Option<u32>,
|
frl_enabled: Option<u32>,
|
||||||
|
adapter_name: Option<&'a str>,
|
||||||
|
short_gpu_name: Option<&'a str>,
|
||||||
license_type: Option<&'a str>,
|
license_type: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,32 +249,46 @@ impl fmt::Debug for VgpuStart {
|
|||||||
impl fmt::Debug for VgpuConfig {
|
impl fmt::Debug for VgpuConfig {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_struct("VgpuConfig")
|
f.debug_struct("VgpuConfig")
|
||||||
.field("gpu_type", &self.gpu_type)
|
.field("vgpu_type", &self.vgpu_type)
|
||||||
.field("card_name", &CStrFormat(&self.card_name))
|
.field("vgpu_name", &CStrFormat(&self.vgpu_name))
|
||||||
.field("vgpu_type", &CStrFormat(&self.vgpu_type))
|
.field("vgpu_class", &CStrFormat(&self.vgpu_class))
|
||||||
|
.field("vgpu_signature", &HexFormatSlice(&self.vgpu_signature))
|
||||||
.field("features", &CStrFormat(&self.features))
|
.field("features", &CStrFormat(&self.features))
|
||||||
.field("max_instances", &self.max_instances)
|
.field("max_instances", &self.max_instances)
|
||||||
.field("num_displays", &self.num_displays)
|
.field("num_heads", &self.num_heads)
|
||||||
.field("display_width", &self.display_width)
|
.field("max_resolution_x", &self.max_resolution_x)
|
||||||
.field("display_height", &self.display_height)
|
.field("max_resolution_y", &self.max_resolution_y)
|
||||||
.field("max_pixels", &self.max_pixels)
|
.field("max_pixels", &self.max_pixels)
|
||||||
.field("frl_config", &self.frl_config)
|
.field("frl_config", &self.frl_config)
|
||||||
.field("cuda_enabled", &self.cuda_enabled)
|
.field("cuda_enabled", &self.cuda_enabled)
|
||||||
.field("ecc_supported", &self.ecc_supported)
|
.field("ecc_supported", &self.ecc_supported)
|
||||||
.field("mig_instance_size", &self.mig_instance_size)
|
.field("mig_instance_size", &self.mig_instance_size)
|
||||||
.field("multi_vgpu_supported", &self.multi_vgpu_supported)
|
.field("multi_vgpu_supported", &self.multi_vgpu_supported)
|
||||||
.field("pci_id", &HexFormat(self.pci_id))
|
.field("vdev_id", &HexFormat(self.vdev_id))
|
||||||
.field("pci_device_id", &HexFormat(self.pci_device_id))
|
.field("pdev_id", &HexFormat(self.pdev_id))
|
||||||
.field("framebuffer", &HexFormat(self.framebuffer))
|
.field("fb_length", &HexFormat(self.fb_length))
|
||||||
.field("mappable_video_size", &HexFormat(self.mappable_video_size))
|
.field("mappable_video_size", &HexFormat(self.mappable_video_size))
|
||||||
.field(
|
.field("fb_reservation", &HexFormat(self.fb_reservation))
|
||||||
"framebuffer_reservation",
|
|
||||||
&HexFormat(self.framebuffer_reservation),
|
|
||||||
)
|
|
||||||
.field("encoder_capacity", &HexFormat(self.encoder_capacity))
|
.field("encoder_capacity", &HexFormat(self.encoder_capacity))
|
||||||
.field("bar1_length", &HexFormat(self.bar1_length))
|
.field("bar1_length", &HexFormat(self.bar1_length))
|
||||||
.field("blob", &StraightFormat(&self.blob[..]))
|
.field("frl_enable", &self.frl_enable)
|
||||||
.field("license_type", &CStrFormat(&self.license_type))
|
.field("adapter_name", &CStrFormat(&self.adapter_name))
|
||||||
|
.field(
|
||||||
|
"adapter_name_unicode",
|
||||||
|
&WideCharFormat(&self.adapter_name_unicode),
|
||||||
|
)
|
||||||
|
.field(
|
||||||
|
"short_gpu_name_string",
|
||||||
|
&CStrFormat(&self.short_gpu_name_string),
|
||||||
|
)
|
||||||
|
.field(
|
||||||
|
"licensed_product_name",
|
||||||
|
&CStrFormat(&self.licensed_product_name),
|
||||||
|
)
|
||||||
|
.field(
|
||||||
|
"vgpu_extra_params",
|
||||||
|
&HexFormatSlice(&self.vgpu_extra_params[..]),
|
||||||
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,13 +473,13 @@ fn handle_profile_override(config: &mut VgpuConfig) -> bool {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let gpu_type = format!("nvidia-{}", config.gpu_type);
|
let vgpu_type = format!("nvidia-{}", config.vgpu_type);
|
||||||
let mdev_uuid = LAST_MDEV_UUID.lock().take();
|
let mdev_uuid = LAST_MDEV_UUID.lock().take();
|
||||||
|
|
||||||
if let Some(config_override) = config_overrides.profile.get(gpu_type.as_str()) {
|
if let Some(config_override) = config_overrides.profile.get(vgpu_type.as_str()) {
|
||||||
info!("Applying profile {} overrides", gpu_type);
|
info!("Applying profile {} overrides", vgpu_type);
|
||||||
|
|
||||||
if !apply_profile_override(config, &gpu_type, config_override) {
|
if !apply_profile_override(config, &vgpu_type, config_override) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,7 +487,7 @@ fn handle_profile_override(config: &mut VgpuConfig) -> bool {
|
|||||||
if let Some(config_override) = config_overrides.mdev.get(mdev_uuid.as_str()) {
|
if let Some(config_override) = config_overrides.mdev.get(mdev_uuid.as_str()) {
|
||||||
info!("Applying mdev UUID {} profile overrides", mdev_uuid);
|
info!("Applying mdev UUID {} profile overrides", mdev_uuid);
|
||||||
|
|
||||||
if !apply_profile_override(config, &gpu_type, config_override) {
|
if !apply_profile_override(config, &vgpu_type, config_override) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,40 +498,43 @@ fn handle_profile_override(config: &mut VgpuConfig) -> bool {
|
|||||||
|
|
||||||
fn apply_profile_override(
|
fn apply_profile_override(
|
||||||
config: &mut VgpuConfig,
|
config: &mut VgpuConfig,
|
||||||
gpu_type: &str,
|
vgpu_type: &str,
|
||||||
config_override: &VgpuProfileOverride,
|
config_override: &VgpuProfileOverride,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
macro_rules! handle_copy_overrides {
|
macro_rules! handle_copy_overrides {
|
||||||
($field:ident) => {
|
($source_field:ident => $target_field:ident) => {
|
||||||
if let Some(value) = config_override.$field {
|
if let Some(value) = config_override.$source_field {
|
||||||
info!(
|
info!(
|
||||||
"Patching {}/{}: {} -> {}",
|
"Patching {}/{}: {} -> {}",
|
||||||
gpu_type,
|
vgpu_type,
|
||||||
stringify!($field),
|
stringify!($target_field),
|
||||||
config.$field,
|
config.$target_field,
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
|
|
||||||
config.$field = value;
|
config.$target_field = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($($field:ident),*$(,)?) => {
|
($field:ident) => {
|
||||||
|
handle_copy_overrides!($field => $field);
|
||||||
|
};
|
||||||
|
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => {
|
||||||
$(
|
$(
|
||||||
handle_copy_overrides!($field);
|
handle_copy_overrides!($source_field $(=> $target_field)?);
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
macro_rules! handle_str_overrides {
|
macro_rules! handle_str_overrides {
|
||||||
($field:ident) => {
|
($source_field:ident => $target_field:ident) => {
|
||||||
if let Some(value) = config_override.$field {
|
if let Some(value) = config_override.$source_field {
|
||||||
let value_bytes = value.as_bytes();
|
let value_bytes = value.as_bytes();
|
||||||
|
|
||||||
// Use `len - 1` to account for the required NULL terminator.
|
// Use `len - 1` to account for the required NULL terminator.
|
||||||
if value_bytes.len() > config.$field.len() - 1 {
|
if value_bytes.len() > config.$target_field.len() - 1 {
|
||||||
error!(
|
error!(
|
||||||
"Patching {}/{}: value '{}' is too long",
|
"Patching {}/{}: value '{}' is too long",
|
||||||
gpu_type,
|
vgpu_type,
|
||||||
stringify!($field),
|
stringify!($target_field),
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -517,26 +542,76 @@ fn apply_profile_override(
|
|||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
"Patching {}/{}: '{}' -> '{}'",
|
"Patching {}/{}: '{}' -> '{}'",
|
||||||
gpu_type,
|
vgpu_type,
|
||||||
stringify!($field),
|
stringify!($target_field),
|
||||||
from_c_str(&config.$field),
|
from_c_str(&config.$target_field),
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
|
|
||||||
// Zero out the field first.
|
// Zero out the field first.
|
||||||
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
|
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
|
||||||
for v in config.$field.iter_mut() {
|
for v in config.$target_field.iter_mut() {
|
||||||
*v = 0;
|
*v = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the string bytes.
|
// Write the string bytes.
|
||||||
let _ = config.$field[..].as_mut().write_all(value_bytes);
|
let _ = config.$target_field[..].as_mut().write_all(value_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($($field:ident),*$(,)?) => {
|
($field:ident) => {
|
||||||
|
handle_str_overrides!($field => $field);
|
||||||
|
};
|
||||||
|
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => {
|
||||||
$(
|
$(
|
||||||
handle_str_overrides!($field);
|
handle_str_overrides!($source_field $(=> $target_field)?);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! handle_wide_str_overrides {
|
||||||
|
($source_field:ident => $target_field:ident) => {
|
||||||
|
if let Some(value) = config_override.$source_field {
|
||||||
|
// Use `len - 1` to account for the required NULL terminator.
|
||||||
|
if value.encode_utf16().count() > config.$target_field.len() - 1 {
|
||||||
|
error!(
|
||||||
|
"Patching {}/{}: value '{}' is too long",
|
||||||
|
vgpu_type,
|
||||||
|
stringify!($target_field),
|
||||||
|
value
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Patching {}/{}: '{}' -> '{}'",
|
||||||
|
vgpu_type,
|
||||||
|
stringify!($target_field),
|
||||||
|
WideCharFormat(&config.$target_field),
|
||||||
|
value
|
||||||
|
);
|
||||||
|
|
||||||
|
// Zero out the field first.
|
||||||
|
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
|
||||||
|
for v in config.$target_field.iter_mut() {
|
||||||
|
*v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the string bytes.
|
||||||
|
for (v, ch) in config.$target_field[..]
|
||||||
|
.iter_mut()
|
||||||
|
.zip(value.encode_utf16().chain(Some(0)))
|
||||||
|
{
|
||||||
|
*v = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($field:ident) => {
|
||||||
|
handle_wide_str_overrides!($field => $field);
|
||||||
|
};
|
||||||
|
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => {
|
||||||
|
$(
|
||||||
|
handle_wide_str_overrides!($source_field $(=> $target_field)?);
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -545,35 +620,42 @@ fn apply_profile_override(
|
|||||||
// field order.
|
// field order.
|
||||||
|
|
||||||
handle_copy_overrides! {
|
handle_copy_overrides! {
|
||||||
gpu_type,
|
gpu_type => vgpu_type,
|
||||||
}
|
}
|
||||||
handle_str_overrides! {
|
handle_str_overrides! {
|
||||||
card_name,
|
card_name => vgpu_name,
|
||||||
vgpu_type,
|
vgpu_type => vgpu_class,
|
||||||
features,
|
features,
|
||||||
}
|
}
|
||||||
handle_copy_overrides! {
|
handle_copy_overrides! {
|
||||||
max_instances,
|
max_instances,
|
||||||
num_displays,
|
num_displays => num_heads,
|
||||||
display_width,
|
display_width => max_resolution_x,
|
||||||
display_height,
|
display_height => max_resolution_y,
|
||||||
max_pixels,
|
max_pixels,
|
||||||
frl_config,
|
frl_config,
|
||||||
cuda_enabled,
|
cuda_enabled,
|
||||||
ecc_supported,
|
ecc_supported,
|
||||||
mig_instance_size,
|
mig_instance_size,
|
||||||
multi_vgpu_supported,
|
multi_vgpu_supported,
|
||||||
pci_id,
|
pci_id => vdev_id,
|
||||||
pci_device_id,
|
pci_device_id => pdev_id,
|
||||||
framebuffer,
|
framebuffer => fb_length,
|
||||||
mappable_video_size,
|
mappable_video_size,
|
||||||
framebuffer_reservation,
|
framebuffer_reservation => fb_reservation,
|
||||||
encoder_capacity,
|
encoder_capacity,
|
||||||
bar1_length,
|
bar1_length,
|
||||||
frl_enabled,
|
frl_enabled => frl_enable,
|
||||||
}
|
}
|
||||||
handle_str_overrides! {
|
handle_str_overrides! {
|
||||||
license_type,
|
adapter_name,
|
||||||
|
}
|
||||||
|
handle_wide_str_overrides! {
|
||||||
|
adapter_name => adapter_name_unicode,
|
||||||
|
}
|
||||||
|
handle_str_overrides! {
|
||||||
|
short_gpu_name => short_gpu_name_string,
|
||||||
|
license_type => licensed_product_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
Reference in New Issue
Block a user