mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-20 05:10:11 +01:00
Merge branch 'magma-in-mesa' into 'main'
[RFC][WiP] Magma: Cross-platform system call interface [RFC][WiP] See merge request mesa/mesa!33190
This commit is contained in:
commit
ab4bb3a3ba
40 changed files with 4646 additions and 1 deletions
|
|
@ -102,3 +102,12 @@ opencl_headers = files(
|
||||||
'CL/cl_version.h',
|
'CL/cl_version.h',
|
||||||
'CL/opencl.h',
|
'CL/opencl.h',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if with_magma and host_machine.system() == 'linux'
|
||||||
|
drm_h = files('drm-uapi/drm.h')
|
||||||
|
amdgpu_drm_h = files('drm-uapi/amdgpu_drm.h')
|
||||||
|
msm_drm_h = files('drm-uapi/msm_drm.h')
|
||||||
|
virtgpu_drm_h = files('drm-uapi/virtgpu_drm.h')
|
||||||
|
xe_drm_h = files('drm-uapi/xe_drm.h')
|
||||||
|
i915_drm_h = files('drm-uapi/i915_drm.h')
|
||||||
|
endif
|
||||||
|
|
|
||||||
|
|
@ -777,8 +777,10 @@ if with_gallium_rusticl
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
with_magma = get_option('magma')
|
||||||
with_virtgpu_kumquat = get_option('virtgpu_kumquat') and with_gfxstream_vk
|
with_virtgpu_kumquat = get_option('virtgpu_kumquat') and with_gfxstream_vk
|
||||||
if with_gallium_rusticl or with_nouveau_vk or with_tools.contains('etnaviv') or with_virtgpu_kumquat
|
pre_args += '-DHAVE_MAGMA=@0@'.format(with_magma.to_int())
|
||||||
|
if with_gallium_rusticl or with_nouveau_vk or with_tools.contains('etnaviv') or with_virtgpu_kumquat or with_magma
|
||||||
# rust.bindgen() does not pass `--rust-target` to bindgen until 1.7.0.
|
# rust.bindgen() does not pass `--rust-target` to bindgen until 1.7.0.
|
||||||
if meson.version().version_compare('< 1.7.0')
|
if meson.version().version_compare('< 1.7.0')
|
||||||
error('Mesa Rust support requires Meson 1.7.0 or newer')
|
error('Mesa Rust support requires Meson 1.7.0 or newer')
|
||||||
|
|
|
||||||
|
|
@ -863,3 +863,10 @@ option(
|
||||||
type : 'feature',
|
type : 'feature',
|
||||||
description : 'Use SPIRV-Tools for dumping SPIR-V for debugging purposes (required by CLC)'
|
description : 'Use SPIRV-Tools for dumping SPIR-V for debugging purposes (required by CLC)'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
option(
|
||||||
|
'magma',
|
||||||
|
type : 'boolean',
|
||||||
|
value : false,
|
||||||
|
description : 'Build Magma backends of Mesa3D'
|
||||||
|
)
|
||||||
|
|
|
||||||
15
src/magma/ffi/include/.clang-format
Normal file
15
src/magma/ffi/include/.clang-format
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright 2021 The ChromiumOS Authors
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
ColumnLimit: 100
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
IndentCaseLabels: false
|
||||||
63
src/magma/ffi/include/magma.h
Normal file
63
src/magma/ffi/include/magma.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Mesa3D authors
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef MAGMA_H
|
||||||
|
#define MAGMA_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Versioning
|
||||||
|
*/
|
||||||
|
#define MAGMA_VERSION_MAJOR 0
|
||||||
|
#define MAGMA_VERSION_MINOR 1
|
||||||
|
#define MAGMA_VERSION_PATCH 3
|
||||||
|
|
||||||
|
#define MAGMA_MAX_PHYSICAL_DEVICES 8
|
||||||
|
|
||||||
|
struct magma_physical_device;
|
||||||
|
struct magma_device;
|
||||||
|
struct magma_buffer;
|
||||||
|
|
||||||
|
typedef struct *magma_physical_device magma_physical_device_t;
|
||||||
|
typedef struct *magma_device magma_device_t;
|
||||||
|
typedef struct *magma_buffer magma_buffer_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates physical devices on the system.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
* - `physical_devices` must be an array of type magma_physical_device_t with size `MAGMA_MAX_PHYSICAL_DEVICES`.
|
||||||
|
*/
|
||||||
|
int32_t
|
||||||
|
magma_enumerate_physical_devices(magma_physical_device_t physical_devices[MAGMA_MAX_PHYSICAL_DEVICES],
|
||||||
|
uint32_t *num_devices);
|
||||||
|
int32_t magma_create_device(const magma_physical_device_t physical_device, magma_device_t *device);
|
||||||
|
int32_t magma_physical_device_close(magma_physical_device_t *physical_devices);
|
||||||
|
|
||||||
|
int32_t magma_device_get_memory_properties(const magma_device_t device,
|
||||||
|
struct magma_memory_properties *mem_props);
|
||||||
|
int32_t magma_device_get_memory_budget(const magma_device_t device, const uint32_t heap_idx,
|
||||||
|
struct magma_heap_budget *budget);
|
||||||
|
int32_t magma_device_create_buffer(const magma_device_t device,
|
||||||
|
const struct magma_create_buffer_info *info,
|
||||||
|
magma_buffer_t *buffer);
|
||||||
|
int32_t magma_device_create_context(const magma_device_t device,
|
||||||
|
const struct magma_create_context_info *info,
|
||||||
|
magma_context_t *context);
|
||||||
|
int32_t magma_device_close(magma_physical_device_t *physical_devices);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
63
src/magma/ffi/include/magma_defines.h
Normal file
63
src/magma/ffi/include/magma_defines.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Mesa3D authors
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAGMA_DEFINES_H
|
||||||
|
#define MAGMA_DEFINES_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAGMA_MAX_MEMORY_HEAPS 8
|
||||||
|
#define MAGMA_MAX_MEMORY_TYPES 16
|
||||||
|
|
||||||
|
struct magma_pci_info {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t device_id;
|
||||||
|
uint16_t subvendor_id;
|
||||||
|
uint16_t subdevice_id;
|
||||||
|
uint16_t revision_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct magma_memory_heap {
|
||||||
|
uint64_t heap_size;
|
||||||
|
uint64_t heap_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct magma_memory_type {
|
||||||
|
uint32_t property_flags;
|
||||||
|
uint32_t heap_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct magma_memory_properties {
|
||||||
|
uint32_t memory_type_count;
|
||||||
|
uint32_t memory_heap_count;
|
||||||
|
struct magma_memory_type memory_types[MAGMA_MAX_MEMORY_TYPES];
|
||||||
|
struct magma_memory_heaps memory_heaps[MAGMA_MAX_MEMORY_HEAPS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct magma_heap_budget {
|
||||||
|
uint64_t budget;
|
||||||
|
uint64_t usage;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t MAGMA_BUFFER_FLAG_AMD_OA = 0x000000001;
|
||||||
|
uint32_t MAGMA_BUFFER_FLAG_AMD_GDS = 0x000000002;
|
||||||
|
struct magma_create_buffer_info {
|
||||||
|
uint32_t memory_type_idx;
|
||||||
|
uint32_t alignment;
|
||||||
|
uint32_t common_flags;
|
||||||
|
uint32_t vendor_flags;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t MAGMA_VENDOR_ID_INTEL = 0x8086;
|
||||||
|
uint16_t MAGMA_VENDOR_ID_AMD = 0x1002;
|
||||||
|
uint16_t MAGMA_VENDOR_ID_MALI = 0x13B5;
|
||||||
|
uint16_t MAGMA_VENDOR_ID_QCOM = 0x5413;
|
||||||
|
|
||||||
|
#endif
|
||||||
91
src/magma/ffi/lib.rs
Normal file
91
src/magma/ffi/lib.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright 2024 The ChromiumOS Authors
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::panic::catch_unwind;
|
||||||
|
use std::panic::AssertUnwindSafe;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::slice::from_raw_parts_mut;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use libc::EINVAL;
|
||||||
|
use libc::ESRCH;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use mesa3d_magma::magma_enumerate_devices as enumerate_devices;
|
||||||
|
use mesa3d_magma::MagmaBuffer;
|
||||||
|
use mesa3d_magma::MagmaDevice;
|
||||||
|
use mesa3d_magma::MagmaPhysicalDevice;
|
||||||
|
|
||||||
|
use mesa3d_util::FromRawDescriptor;
|
||||||
|
use mesa3d_util::IntoRawDescriptor;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use mesa3d_util::OwnedDescriptor;
|
||||||
|
use mesa3d_util::RawDescriptor;
|
||||||
|
use mesa3d_util::DEFAULT_RAW_DESCRIPTOR;
|
||||||
|
|
||||||
|
const NO_ERROR: i32 = 0;
|
||||||
|
const MAGMA_MAX_PHYSICAL_DEVICES: u32 = 8;
|
||||||
|
|
||||||
|
fn return_result<T>(result: MesaResult<T>) -> i32 {
|
||||||
|
if let Err(e) = result {
|
||||||
|
error!("An error occurred: {}", e);
|
||||||
|
-EINVAL
|
||||||
|
} else {
|
||||||
|
NO_ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! return_on_error {
|
||||||
|
($result:expr) => {
|
||||||
|
match $result {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
error!("An error occurred: {}", e);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type magma_physical_device = MagmaPhysicalDevice;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type magma_device = MagmaDevice;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type magma_buffer = MagmaBuffer;
|
||||||
|
|
||||||
|
// The following structs (in define.rs) must be ABI-compatible with FFI header
|
||||||
|
// (magma.h).
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn magma_enumerate_physical_devices(
|
||||||
|
physical_devices: &mut *mut magma_physical_device,
|
||||||
|
num_devices: &mut u32,
|
||||||
|
) -> i32 {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let result = enumerate_devices();
|
||||||
|
let phys_devs = return_on_error!(result);
|
||||||
|
*num_devices = phys_devs.len().try_into().unwrap();
|
||||||
|
if *num_devices > MAGMA_MAX_PHYSICAL_DEVICES {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let physical_devices =
|
||||||
|
from_raw_parts_mut(physical_devices, MAGMA_MAX_PHYSICAL_DEVICES as usize);
|
||||||
|
for (i, phys_dev) in phys_devs.into_iter().enumerate() {
|
||||||
|
physical_devices[i] = Box::into_raw(Box::new(phys_dev)) as _;
|
||||||
|
}
|
||||||
|
|
||||||
|
NO_ERROR
|
||||||
|
}))
|
||||||
|
.unwrap_or(-ESRCH)
|
||||||
|
}
|
||||||
11
src/magma/ffi/meson.build
Normal file
11
src/magma/ffi/meson.build
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright © 2025 Google
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
libmesa_magma = static_library(
|
||||||
|
'mesa3d_magma_ffi',
|
||||||
|
'lib.rs',
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'c',
|
||||||
|
link_with: [libmesa_magma, libmesa_rust_util],
|
||||||
|
dependencies: [dep_mesa3d_util, dep_log]
|
||||||
|
)
|
||||||
16
src/magma/lib.rs
Normal file
16
src/magma/lib.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
mod magma;
|
||||||
|
mod magma_defines;
|
||||||
|
mod magma_kumquat;
|
||||||
|
mod sys;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use magma_defines::*;
|
||||||
|
|
||||||
|
pub use magma::magma_enumerate_devices;
|
||||||
|
pub use magma::MagmaBuffer;
|
||||||
|
pub use magma::MagmaContext;
|
||||||
|
pub use magma::MagmaDevice;
|
||||||
|
pub use magma::MagmaPhysicalDevice;
|
||||||
291
src/magma/magma.rs
Normal file
291
src/magma/magma.rs
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//! Magma: Rust implementation of Fuchsia's driver model.
|
||||||
|
//!
|
||||||
|
//! Design found at <https://fuchsia.dev/fuchsia-third_party/mesa3d/src/development/graphics/magma/concepts/design>.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::OwnedDescriptor;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaError;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MagmaPciBusInfo;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::magma_defines::MagmaResult;
|
||||||
|
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
use crate::magma_kumquat::enumerate_devices as magma_kumquat_enumerate_devices;
|
||||||
|
use crate::sys::platform::enumerate_devices as platform_enumerate_devices;
|
||||||
|
|
||||||
|
const VIRTGPU_KUMQUAT_ENABLED: &str = "VIRTGPU_KUMQUAT";
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MagmaPhysicalDevice {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: MagmaPciInfo,
|
||||||
|
pci_bus_info: MagmaPciBusInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MagmaDevice {
|
||||||
|
device: Arc<dyn Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MagmaContext {
|
||||||
|
_context: Arc<dyn Context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MagmaBuffer {
|
||||||
|
buffer: Arc<dyn Buffer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magma_enumerate_devices() -> MagmaResult<Vec<MagmaPhysicalDevice>> {
|
||||||
|
let devices = match std::env::var(VIRTGPU_KUMQUAT_ENABLED) {
|
||||||
|
Ok(_) => magma_kumquat_enumerate_devices()?,
|
||||||
|
Err(_) => platform_enumerate_devices()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaPhysicalDevice {
|
||||||
|
pub(crate) fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: MagmaPciInfo,
|
||||||
|
pci_bus_info: MagmaPciBusInfo,
|
||||||
|
) -> MagmaPhysicalDevice {
|
||||||
|
MagmaPhysicalDevice {
|
||||||
|
physical_device,
|
||||||
|
pci_info,
|
||||||
|
pci_bus_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_device(&self) -> MagmaResult<MagmaDevice> {
|
||||||
|
let device = self
|
||||||
|
.physical_device
|
||||||
|
.create_device(&self.physical_device, &self.pci_info)?;
|
||||||
|
Ok(MagmaDevice { device })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct MagmaSemaphore {
|
||||||
|
semaphore: OwnedDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MagmaExecResource {
|
||||||
|
buffer: MagmaBuffer,
|
||||||
|
offset: u64,
|
||||||
|
length: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MagmaExecCommandBuffer {
|
||||||
|
resource_idx: u32,
|
||||||
|
unused: u32,
|
||||||
|
start_offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MagmaCommandDescriptor {
|
||||||
|
flags: u64,
|
||||||
|
command_buffers: Vec<MagmaExecCommandBuffer>,
|
||||||
|
resources: Vec<MagmaExecResource>,
|
||||||
|
wait_semaphores: Vec<MagmaSemaphore>,
|
||||||
|
signal_semaphores: Vec<MagmaSemaphore>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MagmaInlineCommandBuffer {
|
||||||
|
data: Vec<u8>,
|
||||||
|
wait_semaphores: Vec<MagmaSemaphore>,
|
||||||
|
signal_semaphores: Vec<MagmaSemaphore>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaDevice {
|
||||||
|
pub fn get_memory_properties(&self) -> MagmaResult<MagmaMemoryProperties> {
|
||||||
|
let mem_props = self.device.get_memory_properties()?;
|
||||||
|
Ok(mem_props)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_memory_budget(&self, heap_idx: u32) -> MagmaResult<MagmaHeapBudget> {
|
||||||
|
let budget = self.device.get_memory_budget(heap_idx)?;
|
||||||
|
Ok(budget)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_context(&self) -> MagmaResult<MagmaContext> {
|
||||||
|
let context = self.device.create_context(&self.device)?;
|
||||||
|
Ok(MagmaContext { _context: context })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_buffer(&self, create_info: &MagmaCreateBufferInfo) -> MagmaResult<MagmaBuffer> {
|
||||||
|
let buffer = self.device.create_buffer(&self.device, create_info)?;
|
||||||
|
Ok(MagmaBuffer { buffer })
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: we probably want to import with a memory type
|
||||||
|
pub fn import(&self, info: MagmaImportHandleInfo) -> MagmaResult<MagmaBuffer> {
|
||||||
|
let buffer = self.device.import(&self.device, info)?;
|
||||||
|
Ok(MagmaBuffer { buffer })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaBuffer {
|
||||||
|
pub fn map(&self) -> MagmaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let region = self.buffer.map(&self.buffer)?;
|
||||||
|
Ok(region)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export(&self) -> MagmaResult<MesaHandle> {
|
||||||
|
let handle = self.buffer.export()?;
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate(
|
||||||
|
&self,
|
||||||
|
sync_flags: u64,
|
||||||
|
ranges: &[MagmaMappedMemoryRange],
|
||||||
|
) -> MagmaResult<()> {
|
||||||
|
self.buffer.invalidate(sync_flags, ranges)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&self, sync_flags: u64, ranges: &[MagmaMappedMemoryRange]) -> MagmaResult<()> {
|
||||||
|
self.buffer.flush(sync_flags, ranges)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaContext {
|
||||||
|
pub fn execute_command(
|
||||||
|
_connection: &MagmaPhysicalDevice,
|
||||||
|
_command_descriptor: u64,
|
||||||
|
) -> MagmaResult<u64> {
|
||||||
|
Err(MagmaError::Unimplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_immediate_commands(
|
||||||
|
_connection: &MagmaPhysicalDevice,
|
||||||
|
_wait_semaphores: Vec<MagmaSemaphore>,
|
||||||
|
_signal_semaphore: Vec<MagmaSemaphore>,
|
||||||
|
) -> MagmaResult<u64> {
|
||||||
|
Err(MagmaError::Unimplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_handle() -> MagmaResult<u64> {
|
||||||
|
Err(MagmaError::Unimplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
fn get_physical_device() -> Option<MagmaPhysicalDevice> {
|
||||||
|
let valid_vendor_ids: [u16; 4] = [
|
||||||
|
MAGMA_VENDOR_ID_INTEL,
|
||||||
|
MAGMA_VENDOR_ID_AMD,
|
||||||
|
MAGMA_VENDOR_ID_MALI,
|
||||||
|
MAGMA_VENDOR_ID_QCOM,
|
||||||
|
];
|
||||||
|
|
||||||
|
let physical_devices = magma_enumerate_devices().unwrap();
|
||||||
|
physical_devices
|
||||||
|
.into_iter()
|
||||||
|
.find(|device| valid_vendor_ids.contains(&device.pci_info.vendor_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_memory_properties() {
|
||||||
|
let physical_device = get_physical_device().unwrap();
|
||||||
|
let device = physical_device.create_device().unwrap();
|
||||||
|
let mem_props = device.get_memory_properties().unwrap();
|
||||||
|
|
||||||
|
assert_ne!(mem_props.memory_type_count, 0);
|
||||||
|
assert_ne!(mem_props.memory_heap_count, 0);
|
||||||
|
|
||||||
|
assert_ne!(mem_props.memory_type_count, 0,);
|
||||||
|
assert_ne!(mem_props.memory_heap_count, 0,);
|
||||||
|
|
||||||
|
println!("--- Retrieved Magma Memory Properties ---");
|
||||||
|
println!(" Total Memory Type Count: {}", mem_props.memory_type_count);
|
||||||
|
println!(" Total Memory Heap Count: {}", mem_props.memory_heap_count);
|
||||||
|
|
||||||
|
println!("--- Validating Memory Heaps ---");
|
||||||
|
for i in 0..mem_props.memory_heap_count as usize {
|
||||||
|
let heap = &mem_props.memory_heaps[i];
|
||||||
|
println!(" Heap {}:", i);
|
||||||
|
println!(" Size: {} bytes", heap.heap_size);
|
||||||
|
println!(" Flags: {:#x}", heap.heap_flags); // Print flags in hex for clarity
|
||||||
|
|
||||||
|
// Assertions for heaps
|
||||||
|
assert!(heap.heap_size > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through and validate Memory Types
|
||||||
|
println!("--- Validating Memory Types ---");
|
||||||
|
for i in 0..mem_props.memory_type_count as usize {
|
||||||
|
let mem_type = &mem_props.memory_types[i];
|
||||||
|
println!(" Memory Type {}:", i);
|
||||||
|
println!(" Heap Index: {}", mem_type.heap_idx);
|
||||||
|
println!(" Property Flags: {:#x}", mem_type.property_flags); // Print flags in hex
|
||||||
|
|
||||||
|
// Assertions for memory types
|
||||||
|
assert!(mem_type.heap_idx < mem_props.memory_heap_count,);
|
||||||
|
|
||||||
|
// Assert that each memory type has at least one property flag set (not NONE)
|
||||||
|
assert_ne!(mem_type.property_flags, 0,);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("--- Magma Memory Properties Test Passed Successfully! ---");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_memory_allocation() {
|
||||||
|
let physical_device = get_physical_device().unwrap();
|
||||||
|
let device = physical_device.create_device().unwrap();
|
||||||
|
|
||||||
|
let mem_props = device.get_memory_properties().unwrap();
|
||||||
|
|
||||||
|
let mut chosen_memory_type_idx: Option<u32> = None;
|
||||||
|
for i in 0..mem_props.memory_type_count as usize {
|
||||||
|
let mem_type = &mem_props.memory_types[i];
|
||||||
|
if (mem_type.property_flags & MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT != 0)
|
||||||
|
&& (mem_type.property_flags & MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT != 0)
|
||||||
|
{
|
||||||
|
chosen_memory_type_idx = Some(i as u32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_type_idx = chosen_memory_type_idx.unwrap();
|
||||||
|
let buffer_size: u64 = 4096;
|
||||||
|
|
||||||
|
let create_info = MagmaCreateBufferInfo {
|
||||||
|
memory_type_idx,
|
||||||
|
alignment: 4096,
|
||||||
|
common_flags: 0,
|
||||||
|
vendor_flags: 0,
|
||||||
|
size: buffer_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
let buffer = device.create_buffer(&create_info).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
206
src/magma/magma_defines.rs
Normal file
206
src/magma/magma_defines.rs
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use remain::sorted;
|
||||||
|
use thiserror::Error;
|
||||||
|
use zerocopy::FromBytes;
|
||||||
|
use zerocopy::IntoBytes;
|
||||||
|
|
||||||
|
/// An error type based on magma_common_defs.h
|
||||||
|
#[sorted]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum MagmaError {
|
||||||
|
#[error("Access Denied")]
|
||||||
|
AccessDenied,
|
||||||
|
#[error("Bad State")]
|
||||||
|
BadState,
|
||||||
|
#[error("Connection Lost")]
|
||||||
|
ConnectionLost,
|
||||||
|
#[error("Context Killed")]
|
||||||
|
ContextKilled,
|
||||||
|
#[error("Internal Error")]
|
||||||
|
InternalError,
|
||||||
|
#[error("Invalid Arguments")]
|
||||||
|
InvalidArgs,
|
||||||
|
#[error("Memory Error")]
|
||||||
|
MemoryError,
|
||||||
|
#[error("A Mesa error was returned {0}")]
|
||||||
|
MesaError(MesaError),
|
||||||
|
#[error("Timed out")]
|
||||||
|
TimedOut,
|
||||||
|
#[error("Unimplemented")]
|
||||||
|
Unimplemented,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MesaError> for MagmaError {
|
||||||
|
fn from(e: MesaError) -> MagmaError {
|
||||||
|
MagmaError::MesaError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type MagmaResult<T> = std::result::Result<T, MagmaError>;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaPciInfo {
|
||||||
|
pub vendor_id: u16,
|
||||||
|
pub device_id: u16,
|
||||||
|
pub subvendor_id: u16,
|
||||||
|
pub subdevice_id: u16,
|
||||||
|
pub revision_id: u8,
|
||||||
|
pub padding: [u8; 7],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaPciBusInfo {
|
||||||
|
pub domain: u16,
|
||||||
|
pub bus: u8,
|
||||||
|
pub device: u8,
|
||||||
|
pub function: u8,
|
||||||
|
pub padding: [u8; 7],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be set in the case of VRAM only
|
||||||
|
pub const MAGMA_HEAP_DEVICE_LOCAL_BIT: u64 = 0x00000001;
|
||||||
|
pub const MAGMA_HEAP_CPU_VISIBLE_BIT: u64 = 0x00000010;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaHeap {
|
||||||
|
pub heap_size: u64,
|
||||||
|
pub heap_flags: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaHeap {
|
||||||
|
pub fn is_device_local(&self) -> bool {
|
||||||
|
self.heap_flags & MAGMA_HEAP_DEVICE_LOCAL_BIT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cpu_visible(&self) -> bool {
|
||||||
|
self.heap_flags & MAGMA_HEAP_CPU_VISIBLE_BIT != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT: u32 = 0x00000001;
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT: u32 = 0x00000002;
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT: u32 = 0x00000004;
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT: u32 = 0x00000008;
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT: u32 = 0x00000010;
|
||||||
|
pub const MAGMA_MEMORY_PROPERTY_PROTECTED_BIT: u32 = 0x00000020;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaMemoryType {
|
||||||
|
pub property_flags: u32,
|
||||||
|
pub heap_idx: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaMemoryType {
|
||||||
|
pub fn is_device_local(&self) -> bool {
|
||||||
|
self.property_flags & MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_coherent(&self) -> bool {
|
||||||
|
self.property_flags & MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cached(&self) -> bool {
|
||||||
|
self.property_flags & MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_protected(&self) -> bool {
|
||||||
|
self.property_flags & MAGMA_MEMORY_PROPERTY_PROTECTED_BIT != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAGMA_MAX_MEMORY_TYPES: usize = 32;
|
||||||
|
pub const MAGMA_MAX_MEMORY_HEAPS: usize = 16;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaMemoryProperties {
|
||||||
|
pub memory_type_count: u32,
|
||||||
|
pub memory_heap_count: u32,
|
||||||
|
pub memory_types: [MagmaMemoryType; MAGMA_MAX_MEMORY_TYPES],
|
||||||
|
pub memory_heaps: [MagmaHeap; MAGMA_MAX_MEMORY_HEAPS],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaMemoryProperties {
|
||||||
|
pub(crate) fn increment_heap_count(&mut self) {
|
||||||
|
self.memory_heap_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_heap(&mut self, heap_size: u64, heap_flags: u64) {
|
||||||
|
self.memory_heaps[self.memory_heap_count as usize].heap_size = heap_size;
|
||||||
|
self.memory_heaps[self.memory_heap_count as usize].heap_flags = heap_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_memory_type(&mut self, property_flags: u32) {
|
||||||
|
self.memory_types[self.memory_type_count as usize].property_flags = property_flags;
|
||||||
|
self.memory_types[self.memory_type_count as usize].heap_idx = self.memory_heap_count;
|
||||||
|
self.memory_type_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_memory_heap(&self, heap_idx: u32) -> &MagmaHeap {
|
||||||
|
&self.memory_heaps[heap_idx as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_memory_type(&self, memory_type_idx: u32) -> &MagmaMemoryType {
|
||||||
|
&self.memory_types[memory_type_idx as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaHeapBudget {
|
||||||
|
pub budget: u64,
|
||||||
|
pub usage: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common allocation flags
|
||||||
|
// - MAGMA_BUFFER_FLAG_EXTERNAL: The buffer *may* be exported as an OS-specific handle
|
||||||
|
// - MAGMA_BUFFER_FLAG_SCANOUT: The buffer *may* be used by the scanout engine directly
|
||||||
|
pub const MAGMA_BUFFER_FLAG_EXTERNAL: u32 = 0x000000001;
|
||||||
|
pub const MAGMA_BUFFER_FLAG_SCANOUT: u32 = 0x000000002;
|
||||||
|
|
||||||
|
// Acceptable buffer vendor flags if the vendor is AMD:
|
||||||
|
// - MAGMA_BUFFER_FLAG_AMD_FLAG_OA: Ordered append, used by 3D/Compute engines
|
||||||
|
// - MAGMA_BUFFER_FLAG_AMD_FLAG_GDS: Global on-chip data storage. Used to share
|
||||||
|
// data across shader threads
|
||||||
|
pub const MAGMA_BUFFER_FLAG_AMD_OA: u32 = 0x000000001;
|
||||||
|
pub const MAGMA_BUFFER_FLAG_AMD_GDS: u32 = 0x000000002;
|
||||||
|
|
||||||
|
pub const MAGMA_SYNC_WHOLE_RANGE: u64 = 1 << 0;
|
||||||
|
pub const MAGMA_SYNC_RANGES: u64 = 1 << 1;
|
||||||
|
pub const MAGMA_SYNC_INVALIDATE_READ: u64 = 1 << 2;
|
||||||
|
pub const MAGMA_SYNC_INVALIDATE_WRITE: u64 = 1 << 3;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaMappedMemoryRange {
|
||||||
|
pub offset: u64,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Default, Debug, IntoBytes, FromBytes)]
|
||||||
|
pub struct MagmaCreateBufferInfo {
|
||||||
|
pub memory_type_idx: u32,
|
||||||
|
pub alignment: u32,
|
||||||
|
pub common_flags: u32,
|
||||||
|
pub vendor_flags: u32,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as PCI id
|
||||||
|
pub const MAGMA_VENDOR_ID_INTEL: u16 = 0x8086;
|
||||||
|
pub const MAGMA_VENDOR_ID_AMD: u16 = 0x1002;
|
||||||
|
pub const MAGMA_VENDOR_ID_MALI: u16 = 0x13B5;
|
||||||
|
pub const MAGMA_VENDOR_ID_QCOM: u16 = 0x5413;
|
||||||
|
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
|
||||||
|
pub struct MagmaImportHandleInfo {
|
||||||
|
pub handle: MesaHandle,
|
||||||
|
pub size: u64,
|
||||||
|
pub memory_type_idx: u32,
|
||||||
|
}
|
||||||
103
src/magma/magma_kumquat.rs
Normal file
103
src/magma/magma_kumquat.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2025 Android Open Source Project
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use virtgpu_kumquat::VirtGpuKumquat;
|
||||||
|
|
||||||
|
use crate::magma::MagmaPhysicalDevice;
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MagmaPciBusInfo;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::sys::platform::PlatformPhysicalDevice;
|
||||||
|
use crate::traits::AsVirtGpu;
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::GenericPhysicalDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
pub struct MagmaKumquat {
|
||||||
|
virtgpu: VirtGpuKumquat,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagmaKumquat {
|
||||||
|
pub fn new() -> MesaResult<MagmaKumquat> {
|
||||||
|
Ok(MagmaKumquat {
|
||||||
|
virtgpu: VirtGpuKumquat::new("/tmp/kumquat-gpu-0")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsVirtGpu for MagmaKumquat {
|
||||||
|
fn as_virtgpu(&self) -> Option<&VirtGpuKumquat> {
|
||||||
|
Some(&self.virtgpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformPhysicalDevice for MagmaKumquat {}
|
||||||
|
impl PhysicalDevice for MagmaKumquat {}
|
||||||
|
|
||||||
|
impl GenericPhysicalDevice for MagmaKumquat {
|
||||||
|
fn create_device(
|
||||||
|
&self,
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
_pci_info: &MagmaPciInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Device>> {
|
||||||
|
let _virtgpu = physical_device.as_virtgpu().unwrap();
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for MagmaKumquat {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, _heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, _device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
_create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
_info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_devices() -> MesaResult<Vec<MagmaPhysicalDevice>> {
|
||||||
|
let pci_info: MagmaPciInfo = Default::default();
|
||||||
|
let pci_bus_info: MagmaPciBusInfo = Default::default();
|
||||||
|
let mut devices: Vec<MagmaPhysicalDevice> = Vec::new();
|
||||||
|
|
||||||
|
let enc = MagmaKumquat::new()?;
|
||||||
|
// TODO): Get data from the server
|
||||||
|
|
||||||
|
devices.push(MagmaPhysicalDevice::new(
|
||||||
|
Arc::new(enc),
|
||||||
|
pci_info,
|
||||||
|
pci_bus_info,
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
169
src/magma/meson.build
Normal file
169
src/magma/meson.build
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
# Copyright © 2024 Google
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
magma_generated_libs = []
|
||||||
|
|
||||||
|
dep_log = dependency('log',
|
||||||
|
version: '>= 0.4.22',
|
||||||
|
fallback: ['log-0.4-rs', 'dep_log'],
|
||||||
|
required: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
dep_windows_sys = null_dep
|
||||||
|
if host_machine.system() == 'linux' or host_machine.system() == 'android'
|
||||||
|
drm_bindgen = rust.bindgen(
|
||||||
|
input : drm_h,
|
||||||
|
output : 'mesa3d_magma_drm_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_.+',
|
||||||
|
'--allowlist-var', 'drm_.+',
|
||||||
|
'--allowlist-type', 'drm_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
amdgpu_bindgen = rust.bindgen(
|
||||||
|
input : amdgpu_drm_h,
|
||||||
|
output : 'mesa3d_magma_amdgpu_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_AMDGPU_.+',
|
||||||
|
'--allowlist-var', 'AMDGPU_.+',
|
||||||
|
'--allowlist-type', 'drm_amdgpu_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
msm_bindgen = rust.bindgen(
|
||||||
|
input : msm_drm_h,
|
||||||
|
output : 'mesa3d_magma_msm_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_MSM_.+',
|
||||||
|
'--allowlist-var', 'MSM_.+',
|
||||||
|
'--allowlist-type', 'drm_msm_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
virtgpu_bindgen = rust.bindgen(
|
||||||
|
input : virtgpu_drm_h,
|
||||||
|
output : 'mesa3d_magma_virtgpu_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_VIRTGPU_.+',
|
||||||
|
'--allowlist-var', 'VIRTGPU_.+',
|
||||||
|
'--allowlist-type', 'drm_virtgpu_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
xe_bindgen = rust.bindgen(
|
||||||
|
input : xe_drm_h,
|
||||||
|
output : 'mesa3d_magma_xe_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_XE.+',
|
||||||
|
'--allowlist-var', 'XE_.+',
|
||||||
|
'--allowlist-type', 'drm_xe_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
i915_bindgen = rust.bindgen(
|
||||||
|
input : i915_drm_h,
|
||||||
|
output : 'mesa3d_magma_i915_bindgen.rs',
|
||||||
|
include_directories : [inc_include],
|
||||||
|
args : [
|
||||||
|
'--with-derive-default',
|
||||||
|
'--allowlist-var', 'DRM_I915.+',
|
||||||
|
'--allowlist-var', 'I915_.+',
|
||||||
|
'--allowlist-type', 'drm_i915_.+',
|
||||||
|
'--no-prepend-enum-name',
|
||||||
|
'--no-doc-comments',
|
||||||
|
'--no-layout-tests'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
drm_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_drm_bindgen',
|
||||||
|
drm_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
amdgpu_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_amdgpu_bindgen',
|
||||||
|
amdgpu_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
msm_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_msm_bindgen',
|
||||||
|
msm_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
virtgpu_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_virtgpu_bindgen',
|
||||||
|
virtgpu_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
i915_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_i915_bindgen',
|
||||||
|
i915_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
xe_bindgen_gen = static_library(
|
||||||
|
'mesa3d_magma_xe_bindgen',
|
||||||
|
xe_bindgen,
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
)
|
||||||
|
|
||||||
|
magma_generated_libs += [drm_bindgen_gen, amdgpu_bindgen_gen,
|
||||||
|
msm_bindgen_gen, virtgpu_bindgen_gen,
|
||||||
|
i915_bindgen_gen, xe_bindgen_gen]
|
||||||
|
elif host_machine.system() == 'windows'
|
||||||
|
dep_windows_sys = dependency('windows-sys-rs',
|
||||||
|
version: '>= 0.61.1',
|
||||||
|
fallback: ['windows-sys-0.6-rs', 'dep_windows_sys'],
|
||||||
|
required: true,
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
magma_args = []
|
||||||
|
magma_args += ['--cfg', 'avoid_cargo']
|
||||||
|
|
||||||
|
libmesa_magma = static_library(
|
||||||
|
'mesa3d_magma',
|
||||||
|
'lib.rs',
|
||||||
|
gnu_symbol_visibility : 'hidden',
|
||||||
|
rust_abi : 'rust',
|
||||||
|
rust_args: magma_args,
|
||||||
|
link_with: [magma_generated_libs, libmesa_rust_util, libmesa_protocols,
|
||||||
|
libvirtgpu_kumquat],
|
||||||
|
dependencies: [dep_mesa3d_util, dep_log, dep_windows_sys]
|
||||||
|
)
|
||||||
438
src/magma/sys/linux/amdgpu.rs
Normal file
438
src/magma/sys/linux/amdgpu.rs
Normal file
|
|
@ -0,0 +1,438 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::os::fd::BorrowedFd;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use mesa3d_util::log_status;
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
|
||||||
|
use crate::ioctl_readwrite;
|
||||||
|
use crate::ioctl_write_ptr;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MAGMA_BUFFER_FLAG_AMD_GDS;
|
||||||
|
use crate::magma_defines::MAGMA_BUFFER_FLAG_AMD_OA;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_CPU_VISIBLE_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||||
|
|
||||||
|
use crate::sys::linux::bindings::amdgpu_bindings::*;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_COMMAND_BASE;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_IOCTL_BASE;
|
||||||
|
use crate::sys::linux::PlatformDevice;
|
||||||
|
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericBuffer;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_amdgpu_ctx,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_AMDGPU_CTX,
|
||||||
|
drm_amdgpu_ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
drm_ioctl_amdgpu_info,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_AMDGPU_INFO,
|
||||||
|
drm_amdgpu_info
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! amdgpu_info_ioctl {
|
||||||
|
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
|
||||||
|
$(#[$attr])*
|
||||||
|
pub unsafe fn $name(fd: BorrowedFd<'_>,
|
||||||
|
data: *mut $ty)
|
||||||
|
-> MesaResult<()> {
|
||||||
|
let mut info: drm_amdgpu_info = Default::default();
|
||||||
|
info.query = $nr;
|
||||||
|
info.return_size = ::std::mem::size_of::<$ty>() as u32;
|
||||||
|
info.return_pointer = data as __u64;
|
||||||
|
drm_ioctl_amdgpu_info(fd, &info)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
amdgpu_info_ioctl!(
|
||||||
|
drm_ioctl_amdgpu_info_memory,
|
||||||
|
AMDGPU_INFO_MEMORY,
|
||||||
|
drm_amdgpu_memory_info
|
||||||
|
);
|
||||||
|
|
||||||
|
amdgpu_info_ioctl!(
|
||||||
|
drm_ioctl_amdgpu_info_vram_gtt,
|
||||||
|
AMDGPU_INFO_VRAM_GTT,
|
||||||
|
drm_amdgpu_info_vram_gtt
|
||||||
|
);
|
||||||
|
|
||||||
|
amdgpu_info_ioctl!(drm_ioctl_amdgpu_info_gtt_usage, AMDGPU_INFO_GTT_USAGE, u64);
|
||||||
|
|
||||||
|
amdgpu_info_ioctl!(
|
||||||
|
drm_ioctl_amdgpu_info_vram_usage,
|
||||||
|
AMDGPU_INFO_VRAM_USAGE,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
|
||||||
|
amdgpu_info_ioctl!(
|
||||||
|
drm_ioctl_amdgpu_info_vis_vram_usage,
|
||||||
|
AMDGPU_INFO_VIS_VRAM_USAGE,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_amdgpu_gem_create,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE,
|
||||||
|
drm_amdgpu_gem_create
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_amdgpu_gem_mmap,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP,
|
||||||
|
drm_amdgpu_gem_mmap
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct AmdGpu {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
mem_props: MagmaMemoryProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AmdGpuContext {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
context_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AmdGpuBuffer {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AmdGpu {
|
||||||
|
pub fn new(physical_device: Arc<dyn PhysicalDevice>) -> MesaResult<AmdGpu> {
|
||||||
|
let mut mem_props: MagmaMemoryProperties = Default::default();
|
||||||
|
let mut memory_info: drm_amdgpu_memory_info = Default::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_memory_info struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_amdgpu_info_memory(physical_device.as_fd().unwrap(), &mut memory_info)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
if memory_info.gtt.total_heap_size > 0 {
|
||||||
|
mem_props.add_heap(memory_info.gtt.total_heap_size, MAGMA_HEAP_CPU_VISIBLE_BIT);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT | MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_info.vram.total_heap_size > 0 {
|
||||||
|
mem_props.add_heap(
|
||||||
|
memory_info.vram.total_heap_size,
|
||||||
|
MAGMA_HEAP_DEVICE_LOCAL_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_info.cpu_accessible_vram.total_heap_size > 0 {
|
||||||
|
mem_props.add_heap(
|
||||||
|
memory_info.cpu_accessible_vram.total_heap_size,
|
||||||
|
MAGMA_HEAP_DEVICE_LOCAL_BIT | MAGMA_HEAP_CPU_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(AmdGpu {
|
||||||
|
physical_device,
|
||||||
|
mem_props,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for AmdGpu {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Ok(self.mem_props.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
if heap_idx >= self.mem_props.memory_heap_count {
|
||||||
|
return Err(MesaError::WithContext("Heap Index out of bounds"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vram_gtt: drm_amdgpu_info_vram_gtt = Default::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_memory_info_vram_gtt struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_amdgpu_info_vram_gtt(self.physical_device.as_fd().unwrap(), &mut vram_gtt)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
let budget: u64;
|
||||||
|
let mut usage: u64 = 0;
|
||||||
|
let heap = &self.mem_props.memory_heaps[heap_idx as usize];
|
||||||
|
|
||||||
|
if heap.is_device_local() && heap.is_cpu_visible() {
|
||||||
|
budget = vram_gtt.vram_cpu_accessible_size;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - usage
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_amdgpu_info_vis_vram_usage(
|
||||||
|
self.physical_device.as_fd().unwrap(),
|
||||||
|
&mut usage,
|
||||||
|
)?;
|
||||||
|
};
|
||||||
|
} else if heap.is_device_local() {
|
||||||
|
budget = vram_gtt.vram_size;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - usage
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_amdgpu_info_vram_usage(
|
||||||
|
self.physical_device.as_fd().unwrap(),
|
||||||
|
&mut usage,
|
||||||
|
)?;
|
||||||
|
};
|
||||||
|
} else if heap.is_cpu_visible() {
|
||||||
|
budget = vram_gtt.gtt_size;
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - usage
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_amdgpu_info_gtt_usage(self.physical_device.as_fd().unwrap(), &mut usage)?;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return Err(MesaError::Unsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MagmaHeapBudget { budget, usage })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, _device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
let ctx = AmdGpuContext::new(self.physical_device.clone(), 0)?;
|
||||||
|
Ok(Arc::new(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let buf = AmdGpuBuffer::new(self.physical_device.clone(), create_info, &self.mem_props)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let gem_handle = self.physical_device.import(info.handle)?;
|
||||||
|
let buf = AmdGpuBuffer::from_existing(
|
||||||
|
self.physical_device.clone(),
|
||||||
|
gem_handle,
|
||||||
|
info.size.try_into()?,
|
||||||
|
)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for AmdGpu {}
|
||||||
|
impl PlatformDevice for AmdGpu {}
|
||||||
|
|
||||||
|
impl AmdGpuContext {
|
||||||
|
fn new(physical_device: Arc<dyn PhysicalDevice>, _priority: i32) -> MesaResult<AmdGpuContext> {
|
||||||
|
let mut ctx_arg = drm_amdgpu_ctx::default();
|
||||||
|
ctx_arg.in_.op = AMDGPU_CTX_OP_ALLOC_CTX;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_ctx struct
|
||||||
|
let context_id: u32 = unsafe {
|
||||||
|
drm_ioctl_amdgpu_ctx(physical_device.as_fd().unwrap(), &mut ctx_arg)?;
|
||||||
|
ctx_arg.out.alloc.ctx_id
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(AmdGpuContext {
|
||||||
|
physical_device,
|
||||||
|
context_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AmdGpuContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut ctx_arg = drm_amdgpu_ctx::default();
|
||||||
|
ctx_arg.in_.op = AMDGPU_CTX_OP_FREE_CTX;
|
||||||
|
ctx_arg.in_.ctx_id = self.context_id;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_ctx struct
|
||||||
|
let result =
|
||||||
|
unsafe { drm_ioctl_amdgpu_ctx(self.physical_device.as_fd().unwrap(), &mut ctx_arg) };
|
||||||
|
log_status!(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for AmdGpuContext {}
|
||||||
|
|
||||||
|
impl AmdGpuBuffer {
|
||||||
|
fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
mem_props: &MagmaMemoryProperties,
|
||||||
|
) -> MesaResult<AmdGpuBuffer> {
|
||||||
|
let mut gem_create_in: drm_amdgpu_gem_create_in = Default::default();
|
||||||
|
let mut gem_create: drm_amdgpu_gem_create = Default::default();
|
||||||
|
|
||||||
|
let memory_type = mem_props.get_memory_type(create_info.memory_type_idx);
|
||||||
|
|
||||||
|
gem_create_in.bo_size = create_info.size;
|
||||||
|
// FIXME: gpu_info.pte_fragment_size, alignment
|
||||||
|
// Need GPU topology crate
|
||||||
|
gem_create_in.alignment = create_info.alignment as u64;
|
||||||
|
|
||||||
|
// Goal: An explicit sync world + discardable world only.
|
||||||
|
gem_create_in.domain_flags |= AMDGPU_GEM_CREATE_EXPLICIT_SYNC as u64;
|
||||||
|
gem_create_in.domain_flags |= AMDGPU_GEM_CREATE_DISCARDABLE as u64;
|
||||||
|
|
||||||
|
if memory_type.is_coherent() {
|
||||||
|
gem_create_in.domain_flags |= AMDGPU_GEM_CREATE_CPU_GTT_USWC as u64;
|
||||||
|
} else {
|
||||||
|
gem_create_in.domain_flags |= AMDGPU_GEM_CREATE_NO_CPU_ACCESS as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_type.is_protected() {
|
||||||
|
gem_create_in.domain_flags |= AMDGPU_GEM_CREATE_ENCRYPTED as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should these be "heaps" of zero size?
|
||||||
|
if create_info.vendor_flags & MAGMA_BUFFER_FLAG_AMD_OA != 0 {
|
||||||
|
gem_create_in.domains |= AMDGPU_GEM_DOMAIN_OA as u64
|
||||||
|
} else if create_info.vendor_flags & MAGMA_BUFFER_FLAG_AMD_GDS != 0 {
|
||||||
|
gem_create_in.domains |= AMDGPU_GEM_DOMAIN_GDS as u64;
|
||||||
|
} else if memory_type.is_device_local() {
|
||||||
|
gem_create_in.domains |= AMDGPU_GEM_DOMAIN_VRAM as u64;
|
||||||
|
} else {
|
||||||
|
gem_create_in.domains |= AMDGPU_GEM_DOMAIN_GTT as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_gem_create_args
|
||||||
|
let gem_handle = unsafe {
|
||||||
|
gem_create.in_ = gem_create_in;
|
||||||
|
drm_ioctl_amdgpu_gem_create(physical_device.as_fd().unwrap(), &mut gem_create)?;
|
||||||
|
gem_create.out.handle
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(AmdGpuBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle,
|
||||||
|
size: create_info.size.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_existing(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
) -> MesaResult<AmdGpuBuffer> {
|
||||||
|
Ok(AmdGpuBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericBuffer for AmdGpuBuffer {
|
||||||
|
fn map(&self, _buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let mut gem_mmap: drm_amdgpu_gem_mmap = Default::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_gem_mmap
|
||||||
|
let offset = unsafe {
|
||||||
|
gem_mmap.in_.handle = self.gem_handle;
|
||||||
|
drm_ioctl_amdgpu_gem_mmap(self.physical_device.as_fd().unwrap(), &mut gem_mmap)?;
|
||||||
|
gem_mmap.out.addr_ptr
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapping = self.physical_device.cpu_map(offset, self.size)?;
|
||||||
|
Ok(Arc::new(mapping))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle> {
|
||||||
|
self.physical_device.export(self.gem_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AmdGpuBuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// GEM close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer for AmdGpuBuffer {}
|
||||||
|
|
||||||
|
unsafe impl Send for AmdGpu {}
|
||||||
|
unsafe impl Sync for AmdGpu {}
|
||||||
|
|
||||||
|
unsafe impl Send for AmdGpuContext {}
|
||||||
|
unsafe impl Sync for AmdGpuContext {}
|
||||||
|
|
||||||
|
unsafe impl Send for AmdGpuBuffer {}
|
||||||
|
unsafe impl Sync for AmdGpuBuffer {}
|
||||||
14
src/magma/sys/linux/bindings/amdgpu_bindings.rs
Normal file
14
src/magma/sys/linux/bindings/amdgpu_bindings.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
#[cfg(avoid_cargo)]
|
||||||
|
pub use mesa3d_magma_amdgpu_bindgen::*;
|
||||||
|
|
||||||
|
#[cfg(not(avoid_cargo))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mesa3d_magma_amdgpu_bindgen.rs"));
|
||||||
13
src/magma/sys/linux/bindings/drm_bindings.rs
Normal file
13
src/magma/sys/linux/bindings/drm_bindings.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
#[cfg(avoid_cargo)]
|
||||||
|
pub use mesa3d_magma_drm_bindgen::*;
|
||||||
|
|
||||||
|
#[cfg(not(avoid_cargo))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mesa3d_magma_drm_bindgen.rs"));
|
||||||
12
src/magma/sys/linux/bindings/i915_binding.rs
Normal file
12
src/magma/sys/linux/bindings/i915_binding.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
#[cfg(not(use_meson))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/i915_bindings.rs"));
|
||||||
|
|
||||||
|
#[cfg(use_meson)]
|
||||||
|
pub use i915_bindings::*;
|
||||||
14
src/magma/sys/linux/bindings/i915_bindings.rs
Normal file
14
src/magma/sys/linux/bindings/i915_bindings.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
#[cfg(avoid_cargo)]
|
||||||
|
pub use mesa3d_magma_i915_bindgen::*;
|
||||||
|
|
||||||
|
#[cfg(not(avoid_cargo))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mesa3d_magma_i915_bindgen.rs"));
|
||||||
8
src/magma/sys/linux/bindings/mod.rs
Normal file
8
src/magma/sys/linux/bindings/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pub mod amdgpu_bindings;
|
||||||
|
pub mod drm_bindings;
|
||||||
|
pub mod i915_bindings;
|
||||||
|
pub mod msm_bindings;
|
||||||
|
pub mod xe_bindings;
|
||||||
12
src/magma/sys/linux/bindings/msm_bindings.rs
Normal file
12
src/magma/sys/linux/bindings/msm_bindings.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
#[cfg(avoid_cargo)]
|
||||||
|
pub use mesa3d_magma_msm_bindgen::*;
|
||||||
|
|
||||||
|
#[cfg(not(avoid_cargo))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mesa3d_magma_msm_bindgen.rs"));
|
||||||
12
src/magma/sys/linux/bindings/xe_bindings.rs
Normal file
12
src/magma/sys/linux/bindings/xe_bindings.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
#[cfg(avoid_cargo)]
|
||||||
|
pub use mesa3d_magma_xe_bindgen::*;
|
||||||
|
|
||||||
|
#[cfg(not(avoid_cargo))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mesa3d_magma_xe_bindgen.rs"));
|
||||||
301
src/magma/sys/linux/common.rs
Normal file
301
src/magma/sys/linux/common.rs
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::os::fd::AsFd;
|
||||||
|
use std::os::fd::BorrowedFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use mesa3d_util::log_status;
|
||||||
|
use mesa3d_util::AsRawDescriptor;
|
||||||
|
use mesa3d_util::FromRawDescriptor;
|
||||||
|
use mesa3d_util::MemoryMapping;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use mesa3d_util::OwnedDescriptor;
|
||||||
|
use mesa3d_util::RawDescriptor;
|
||||||
|
use mesa3d_util::MESA_HANDLE_TYPE_MEM_DMABUF;
|
||||||
|
|
||||||
|
use rustix::fs::major;
|
||||||
|
use rustix::fs::minor;
|
||||||
|
use rustix::fs::open;
|
||||||
|
use rustix::fs::readlink;
|
||||||
|
use rustix::fs::stat;
|
||||||
|
use rustix::fs::Dir;
|
||||||
|
use rustix::fs::Mode;
|
||||||
|
use rustix::fs::OFlags;
|
||||||
|
|
||||||
|
use libc::O_CLOEXEC;
|
||||||
|
use libc::O_RDWR;
|
||||||
|
|
||||||
|
use crate::magma::MagmaPhysicalDevice;
|
||||||
|
use crate::magma_defines::MagmaPciBusInfo;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::magma_defines::MAGMA_VENDOR_ID_AMD;
|
||||||
|
use crate::magma_defines::MAGMA_VENDOR_ID_INTEL;
|
||||||
|
use crate::magma_defines::MAGMA_VENDOR_ID_QCOM;
|
||||||
|
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::drm_gem_close;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::drm_prime_handle;
|
||||||
|
use crate::sys::linux::drm_ioctl_gem_close;
|
||||||
|
use crate::sys::linux::drm_ioctl_prime_fd_to_handle;
|
||||||
|
use crate::sys::linux::drm_ioctl_prime_handle_to_fd;
|
||||||
|
use crate::sys::linux::get_drm_device_name;
|
||||||
|
use crate::sys::linux::AmdGpu;
|
||||||
|
use crate::sys::linux::Msm;
|
||||||
|
use crate::sys::linux::Xe;
|
||||||
|
use crate::sys::linux::DRM_DIR_NAME;
|
||||||
|
use crate::sys::linux::DRM_RENDER_MINOR_NAME;
|
||||||
|
use crate::sys::linux::I915;
|
||||||
|
|
||||||
|
use crate::traits::AsVirtGpu;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericPhysicalDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
const PCI_ATTRS: [&str; 5] = [
|
||||||
|
"revision",
|
||||||
|
"vendor",
|
||||||
|
"device",
|
||||||
|
"subsystem_vendor",
|
||||||
|
"subsystem_device",
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LinuxPhysicalDevice {
|
||||||
|
descriptor: OwnedDescriptor,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub trait PlatformPhysicalDevice {
|
||||||
|
fn as_fd(&self) -> Option<BorrowedFd<'_>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_raw_descriptor(&self) -> RawDescriptor {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_map(&self, _offset: u64, _size: usize) -> MesaResult<MemoryMapping> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self, _gem_handle: u32) -> MesaResult<MesaHandle> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(&self, _handle: MesaHandle) -> MesaResult<u32> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self, _gem_handle: u32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericPhysicalDevice for LinuxPhysicalDevice {
|
||||||
|
fn create_device(
|
||||||
|
&self,
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: &MagmaPciInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Device>> {
|
||||||
|
let device: Arc<dyn Device> = match pci_info.vendor_id {
|
||||||
|
MAGMA_VENDOR_ID_AMD => Arc::new(AmdGpu::new(physical_device.clone())?),
|
||||||
|
MAGMA_VENDOR_ID_QCOM => Arc::new(Msm::new(physical_device.clone())),
|
||||||
|
MAGMA_VENDOR_ID_INTEL => {
|
||||||
|
if self.name == "xe" {
|
||||||
|
Arc::new(Xe::new(physical_device.clone(), pci_info)?)
|
||||||
|
} else {
|
||||||
|
Arc::new(I915::new(physical_device.clone())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PlatformDevice {}
|
||||||
|
|
||||||
|
impl LinuxPhysicalDevice {
|
||||||
|
pub fn new(device_node: PathBuf) -> MesaResult<LinuxPhysicalDevice> {
|
||||||
|
let descriptor: OwnedDescriptor = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(device_node.clone())?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
// TODO: confirm if necessary if everything has PCI-ID
|
||||||
|
let name = get_drm_device_name(&descriptor)?;
|
||||||
|
println!("the name is {}", name);
|
||||||
|
|
||||||
|
Ok(LinuxPhysicalDevice { descriptor, name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformPhysicalDevice for LinuxPhysicalDevice {
|
||||||
|
fn as_fd(&self) -> Option<BorrowedFd<'_>> {
|
||||||
|
Some(self.descriptor.as_fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_raw_descriptor(&self) -> RawDescriptor {
|
||||||
|
self.descriptor.as_raw_descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_map(&self, offset: u64, size: usize) -> MesaResult<MemoryMapping> {
|
||||||
|
MemoryMapping::from_offset(&self.descriptor, offset.try_into()?, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self, gem_handle: u32) -> MesaResult<MesaHandle> {
|
||||||
|
let mut arg: drm_prime_handle = drm_prime_handle {
|
||||||
|
handle: gem_handle,
|
||||||
|
flags: (O_CLOEXEC | O_RDWR) as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_prime_handle
|
||||||
|
let fd = unsafe {
|
||||||
|
drm_ioctl_prime_handle_to_fd(self.descriptor.as_fd(), &mut arg)?;
|
||||||
|
arg.fd
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// `fd` is valid after a successful PRIME_HANDLE_TO_HANDLE syscall.
|
||||||
|
let descriptor = unsafe { OwnedDescriptor::from_raw_descriptor(fd) };
|
||||||
|
|
||||||
|
Ok(MesaHandle {
|
||||||
|
os_handle: descriptor,
|
||||||
|
handle_type: MESA_HANDLE_TYPE_MEM_DMABUF,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(&self, handle: MesaHandle) -> MesaResult<u32> {
|
||||||
|
let mut arg: drm_prime_handle = drm_prime_handle {
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_prime_handle
|
||||||
|
let handle = unsafe {
|
||||||
|
arg.fd = handle.os_handle.as_raw_descriptor();
|
||||||
|
drm_ioctl_prime_fd_to_handle(self.descriptor.as_fd(), &mut arg)?;
|
||||||
|
arg.handle
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&self, gem_handle: u32) {
|
||||||
|
let arg: drm_gem_close = drm_gem_close {
|
||||||
|
handle: gem_handle,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_gem_handle
|
||||||
|
let result = unsafe { drm_ioctl_gem_close(self.descriptor.as_fd(), &arg) };
|
||||||
|
|
||||||
|
log_status!(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsVirtGpu for LinuxPhysicalDevice {}
|
||||||
|
impl PhysicalDevice for LinuxPhysicalDevice {}
|
||||||
|
|
||||||
|
// Helper function to parse hexadecimal string to u16
|
||||||
|
fn parse_hex_u16(s: &str) -> MesaResult<u16> {
|
||||||
|
let valid_str = s.trim().strip_prefix("0x").unwrap_or(s.trim());
|
||||||
|
Ok(u16::from_str_radix(valid_str, 16)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_devices() -> MesaResult<Vec<MagmaPhysicalDevice>> {
|
||||||
|
let mut devices: Vec<MagmaPhysicalDevice> = Vec::new();
|
||||||
|
let dir_fd = open(
|
||||||
|
DRM_DIR_NAME,
|
||||||
|
OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
|
||||||
|
Mode::empty(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let dir = Dir::new(dir_fd)?;
|
||||||
|
for entry in dir.flatten() {
|
||||||
|
let filename = entry.file_name().to_str()?;
|
||||||
|
if filename.contains(DRM_RENDER_MINOR_NAME) {
|
||||||
|
let path = Path::new(DRM_DIR_NAME).join(filename);
|
||||||
|
let statbuf = stat(&path)?;
|
||||||
|
|
||||||
|
let maj = major(statbuf.st_rdev);
|
||||||
|
let min = minor(statbuf.st_rdev);
|
||||||
|
|
||||||
|
let pci_device_dir = format!("/sys/dev/char/{}:{}/device", maj, min);
|
||||||
|
let pci_subsystem_dir = format!("{}/subsystem", pci_device_dir);
|
||||||
|
let subsystem_path = Path::new(&pci_subsystem_dir);
|
||||||
|
let subsystem = readlink(subsystem_path, Vec::new())?;
|
||||||
|
|
||||||
|
// If not valid UTF-8, assume not PCI
|
||||||
|
let is_pci_subsystem = subsystem
|
||||||
|
.to_str()
|
||||||
|
.map(|s| s.contains("/pci"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !is_pci_subsystem {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pci_info: MagmaPciInfo = Default::default();
|
||||||
|
let mut pci_bus_info: MagmaPciBusInfo = Default::default();
|
||||||
|
for attr in PCI_ATTRS {
|
||||||
|
let attr_path = format!("{}/{}", pci_device_dir, attr);
|
||||||
|
let mut file = File::open(attr_path)?;
|
||||||
|
let mut hex_string = String::new();
|
||||||
|
file.read_to_string(&mut hex_string)?;
|
||||||
|
|
||||||
|
match attr {
|
||||||
|
"revision" => pci_info.revision_id = parse_hex_u16(&hex_string)?.try_into()?,
|
||||||
|
"vendor" => pci_info.vendor_id = parse_hex_u16(&hex_string)?,
|
||||||
|
"device" => pci_info.device_id = parse_hex_u16(&hex_string)?,
|
||||||
|
"subsystem_vendor" => pci_info.subvendor_id = parse_hex_u16(&hex_string)?,
|
||||||
|
"subsystem_device" => pci_info.subdevice_id = parse_hex_u16(&hex_string)?,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uevent_path = format!("{}/uevent", pci_device_dir);
|
||||||
|
let text: String = fs::read_to_string(uevent_path)?;
|
||||||
|
for line in text.lines() {
|
||||||
|
if line.contains("PCI_SLOT_NAME") {
|
||||||
|
let v: Vec<&str> = line.split(&['=', ':', '.'][..]).collect();
|
||||||
|
|
||||||
|
pci_bus_info.domain = v[1].parse::<u16>()?;
|
||||||
|
pci_bus_info.bus = v[2].parse::<u8>()?;
|
||||||
|
pci_bus_info.device = v[3].parse::<u8>()?;
|
||||||
|
pci_bus_info.function = v[4].parse::<u8>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.push(MagmaPhysicalDevice::new(
|
||||||
|
Arc::new(LinuxPhysicalDevice::new(path.to_path_buf())?),
|
||||||
|
pci_info,
|
||||||
|
pci_bus_info,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for LinuxPhysicalDevice {}
|
||||||
|
unsafe impl Sync for LinuxPhysicalDevice {}
|
||||||
92
src/magma/sys/linux/drm.rs
Normal file
92
src/magma/sys/linux/drm.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::os::fd::AsFd;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::os::raw::c_uint;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use mesa3d_util::OwnedDescriptor;
|
||||||
|
|
||||||
|
use crate::ioctl_readwrite;
|
||||||
|
use crate::ioctl_write_ptr;
|
||||||
|
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::__kernel_size_t;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::drm_gem_close;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::drm_prime_handle;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::drm_version;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_IOCTL_BASE;
|
||||||
|
|
||||||
|
pub const DRM_DIR_NAME: &str = "/dev/dri";
|
||||||
|
pub const DRM_RENDER_MINOR_NAME: &str = "renderD";
|
||||||
|
const DRM_IOCTL_VERSION: c_uint = 0x00;
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_get_version,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_IOCTL_VERSION,
|
||||||
|
drm_version
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_prime_handle_to_fd,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
0x2d,
|
||||||
|
drm_prime_handle
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_prime_fd_to_handle,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
0x2e,
|
||||||
|
drm_prime_handle
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(drm_ioctl_gem_close, DRM_IOCTL_BASE, 0x09, drm_gem_close);
|
||||||
|
|
||||||
|
pub fn get_drm_device_name(descriptor: &OwnedDescriptor) -> MesaResult<String> {
|
||||||
|
let mut version = drm_version {
|
||||||
|
version_major: 0,
|
||||||
|
version_minor: 0,
|
||||||
|
version_patchlevel: 0,
|
||||||
|
name_len: 0,
|
||||||
|
name: null_mut(),
|
||||||
|
date_len: 0,
|
||||||
|
date: null_mut(),
|
||||||
|
desc_len: 0,
|
||||||
|
desc: null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Descriptor is valid and borrowed properly..
|
||||||
|
unsafe {
|
||||||
|
drm_get_version(descriptor.as_fd(), &mut version)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enough bytes to hold the device name and terminating null character.
|
||||||
|
let mut name_bytes: Vec<u8> = vec![0; (version.name_len + 1) as usize];
|
||||||
|
let mut version = drm_version {
|
||||||
|
version_major: 0,
|
||||||
|
version_minor: 0,
|
||||||
|
version_patchlevel: 0,
|
||||||
|
name_len: name_bytes.len() as __kernel_size_t,
|
||||||
|
name: name_bytes.as_mut_ptr() as *mut c_char,
|
||||||
|
date_len: 0,
|
||||||
|
date: null_mut(),
|
||||||
|
desc_len: 0,
|
||||||
|
desc: null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// No more than name_len + 1 bytes will be written to name.
|
||||||
|
unsafe {
|
||||||
|
drm_get_version(descriptor.as_fd(), &mut version)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString::new(&name_bytes[..(version.name_len as usize)])?
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| MesaError::WithContext("couldn't convert string"))
|
||||||
|
}
|
||||||
162
src/magma/sys/linux/flexible_array.rs
Normal file
162
src/magma/sys/linux/flexible_array.rs
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//! A wrapper for structures that contain flexible arrays.
|
||||||
|
//!
|
||||||
|
//! The following code provides generic helpers for creating and accessing flexible array structs.
|
||||||
|
//! A complete definition of flexible array structs is found in the ISO 9899 specification
|
||||||
|
//! <http://www.iso-9899.info/n1570.html>. A flexible array struct is of the form:
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! #[repr(C)]
|
||||||
|
//! struct T {
|
||||||
|
//! some_data: u32,
|
||||||
|
//! nents: u32,
|
||||||
|
//! entries: __IncompleteArrayField<S>,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! where:
|
||||||
|
//!
|
||||||
|
//! - `T` is the flexible array struct type
|
||||||
|
//! - `S` is the flexible array type
|
||||||
|
//! - `nents` is the flexible array length
|
||||||
|
//! - `entries` is the flexible array member
|
||||||
|
//!
|
||||||
|
//! These structures are used by the kernel API.
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
// Returns a `Vec<T>` with a size in bytes at least as large as `size_in_bytes`.
|
||||||
|
fn vec_with_size_in_bytes<T: Default>(size_in_bytes: usize) -> Vec<T> {
|
||||||
|
let rounded_size = size_in_bytes.div_ceil(size_of::<T>());
|
||||||
|
let mut v = Vec::with_capacity(rounded_size);
|
||||||
|
v.resize_with(rounded_size, T::default);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kernel API has many structs that resemble the following `Foo` structure:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// #[repr(C)]
|
||||||
|
/// struct Foo {
|
||||||
|
/// some_data: u32,
|
||||||
|
/// entries: __IncompleteArrayField<__u32>,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In order to allocate such a structure, `size_of::<Foo>()` would be too small because it would
|
||||||
|
/// not include any space for `entries`. To make the allocation large enough while still being
|
||||||
|
/// aligned for `Foo`, a `Vec<Foo>` is created. Only the first element of `Vec<Foo>` would actually
|
||||||
|
/// be used as a `Foo`. The remaining memory in the `Vec<Foo>` is for `entries`, which must be
|
||||||
|
/// contiguous with `Foo`. This function is used to make the `Vec<Foo>` with enough space for
|
||||||
|
/// `count` entries.
|
||||||
|
fn vec_with_array_field<T: Default, F>(count: usize) -> Vec<T> {
|
||||||
|
let element_space = count * size_of::<F>();
|
||||||
|
let vec_size_bytes = size_of::<T>() + element_space;
|
||||||
|
vec_with_size_in_bytes(vec_size_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A collection of methods that are required by the FlexibleArrayWrapper type.
|
||||||
|
///
|
||||||
|
/// When implemented for `T`, this trait allows the caller to set number of `S` entries and
|
||||||
|
/// retrieve a slice of `S` entries. Trait methods must only be called by the FlexibleArrayWrapper
|
||||||
|
/// type. Don't implement this trait directly, use the flexible_array! macro to avoid duplication.
|
||||||
|
pub trait FlexibleArray<S> {
|
||||||
|
/// Implementations must set flexible array length in the flexible array struct to the value
|
||||||
|
/// specified by `len`. Appropriate conversions (i.e, usize to u32) are allowed so long as
|
||||||
|
/// they don't overflow or underflow.
|
||||||
|
fn set_len(&mut self, len: usize);
|
||||||
|
/// Implementations must return the length of the flexible array member. Appropriate
|
||||||
|
/// conversions (i.e, usize to u32) are allowed so long as they don't overflow or underflow.
|
||||||
|
fn get_len(&self) -> usize;
|
||||||
|
/// Implementations must return a slice of flexible array member of length `len`.
|
||||||
|
/// # Safety
|
||||||
|
/// Do not use this function directly, as the FlexibleArrayWrapper will guarantee safety.
|
||||||
|
unsafe fn get_slice(&self, len: usize) -> &[S];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Always use this macro for implementing the FlexibleArray<`S`> trait for a given `T`. There
|
||||||
|
/// exists an 1:1 mapping of macro identifiers to the definitions in the FlexibleArray<`S`>
|
||||||
|
/// documentation, so refer to that for more information.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! flexible_array_impl {
|
||||||
|
($T:ident, $S:ident, $nents:ident, $entries:ident) => {
|
||||||
|
impl FlexibleArray<$S> for $T {
|
||||||
|
fn set_len(&mut self, len: usize) {
|
||||||
|
self.$nents = ::std::convert::TryInto::try_into(len).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_len(&self) -> usize {
|
||||||
|
self.$nents as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_slice(&self, len: usize) -> &[$S] {
|
||||||
|
self.$entries.as_slice(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FlexibleArrayWrapper<T, S> {
|
||||||
|
entries: Vec<T>,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
allocated_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience wrapper for flexible array structs.
|
||||||
|
///
|
||||||
|
/// The FlexibleArray trait must be implemented for the flexible array struct before using this
|
||||||
|
/// wrapper.
|
||||||
|
impl<T, S> FlexibleArrayWrapper<T, S>
|
||||||
|
where
|
||||||
|
T: FlexibleArray<S> + Default,
|
||||||
|
{
|
||||||
|
/// Creates a new FlexibleArrayWrapper for the given flexible array struct type and flexible
|
||||||
|
/// array type. The flexible array length is set to `array_len`. vec_with_array_field is used
|
||||||
|
/// to make sure the resultant wrapper is appropriately sized.
|
||||||
|
pub fn from_array_len(array_len: usize) -> FlexibleArrayWrapper<T, S> {
|
||||||
|
let mut entries = vec_with_array_field::<T, S>(array_len);
|
||||||
|
entries[0].set_len(array_len);
|
||||||
|
|
||||||
|
FlexibleArrayWrapper {
|
||||||
|
entries,
|
||||||
|
phantom: PhantomData,
|
||||||
|
allocated_len: array_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new FlexibleArrayWrapper for the given flexible array struct type and flexible
|
||||||
|
/// array type. The flexible array length is inferred from `total_size`.
|
||||||
|
pub fn from_total_size(total_size: usize) -> FlexibleArrayWrapper<T, S> {
|
||||||
|
let array_size = total_size - size_of::<T>();
|
||||||
|
let num_elements = array_size.div_ceil(size_of::<S>());
|
||||||
|
|
||||||
|
FlexibleArrayWrapper::<T, S>::from_array_len(num_elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mapping the unsized array to a slice is unsafe because the length isn't known. Using
|
||||||
|
/// the length we originally allocated with eliminates the possibility of overflow.
|
||||||
|
fn get_valid_len(&self) -> usize {
|
||||||
|
if self.entries[0].get_len() > self.allocated_len {
|
||||||
|
self.allocated_len
|
||||||
|
} else {
|
||||||
|
self.entries[0].get_len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice of the flexible array member, for inspecting. To modify, use
|
||||||
|
/// mut_entries_slice instead.
|
||||||
|
pub fn entries_slice(&self) -> &[S] {
|
||||||
|
let valid_length = self.get_valid_len();
|
||||||
|
// SAFETY:
|
||||||
|
// Safe because the length has been validated.
|
||||||
|
unsafe { self.entries[0].get_slice(valid_length) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable pointer so it can be passed to the kernel. Callers must not access the
|
||||||
|
/// flexible array member. Using this pointer is unsafe.
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||||
|
&mut self.entries[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
488
src/magma/sys/linux/i915.rs
Normal file
488
src/magma/sys/linux/i915.rs
Normal file
|
|
@ -0,0 +1,488 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use mesa3d_util::log_status;
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
|
||||||
|
use crate::flexible_array_impl;
|
||||||
|
use crate::ioctl_readwrite;
|
||||||
|
use crate::ioctl_write_ptr;
|
||||||
|
use crate::sys::linux::flexible_array::FlexibleArray;
|
||||||
|
use crate::sys::linux::flexible_array::FlexibleArrayWrapper;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_CPU_VISIBLE_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||||
|
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_COMMAND_BASE;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_IOCTL_BASE;
|
||||||
|
use crate::sys::linux::bindings::i915_bindings::*;
|
||||||
|
use crate::sys::linux::PlatformDevice;
|
||||||
|
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericBuffer;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_i915_getparam,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_GETPARAM,
|
||||||
|
drm_i915_getparam
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_i915_query,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_QUERY,
|
||||||
|
drm_i915_query
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_i915_gem_create,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_GEM_CREATE,
|
||||||
|
drm_i915_gem_create
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_i915_gem_mmap_offset,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT,
|
||||||
|
drm_i915_gem_mmap_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_i915_gem_context_create_ext,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE,
|
||||||
|
drm_i915_gem_context_create_ext
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
drm_ioctl_i915_gem_context_destroy,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY,
|
||||||
|
drm_i915_gem_context_destroy
|
||||||
|
);
|
||||||
|
|
||||||
|
flexible_array_impl!(
|
||||||
|
drm_i915_query_memory_regions,
|
||||||
|
drm_i915_memory_region_info,
|
||||||
|
num_regions,
|
||||||
|
regions
|
||||||
|
);
|
||||||
|
|
||||||
|
fn i915_query<T, S>(
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
query_id: u64,
|
||||||
|
) -> MesaResult<FlexibleArrayWrapper<T, S>>
|
||||||
|
where
|
||||||
|
T: FlexibleArray<S> + Default,
|
||||||
|
{
|
||||||
|
let mut item = drm_i915_query_item {
|
||||||
|
query_id,
|
||||||
|
length: 0,
|
||||||
|
flags: 0,
|
||||||
|
data_ptr: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut query = drm_i915_query {
|
||||||
|
num_items: 1,
|
||||||
|
flags: 0,
|
||||||
|
items_ptr: &mut item as *mut _ as u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: First call to get the size
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_i915_query(physical_device.as_fd().unwrap(), &mut query)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.length < 0 {
|
||||||
|
return Err(MesaError::from(std::io::Error::from_raw_os_error(
|
||||||
|
-item.length,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_size = item.length as usize;
|
||||||
|
if total_size == 0 {
|
||||||
|
return Ok(FlexibleArrayWrapper::<T, S>::from_total_size(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut wrapper = FlexibleArrayWrapper::<T, S>::from_total_size(total_size);
|
||||||
|
item.data_ptr = wrapper.as_mut_ptr() as u64;
|
||||||
|
|
||||||
|
// SAFETY: Second call to get the data
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_i915_query(physical_device.as_fd().unwrap(), &mut query)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct I915MemoryInfo {
|
||||||
|
sysmem_total: u64,
|
||||||
|
sysmem_free: u64,
|
||||||
|
vram_mappable_total: u64,
|
||||||
|
vram_mappable_free: u64,
|
||||||
|
vram_unmappable_total: u64,
|
||||||
|
vram_unmappable_free: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn i915_query_memory_regions(
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
) -> MesaResult<I915MemoryInfo> {
|
||||||
|
let query_mem_regions = i915_query::<drm_i915_query_memory_regions, drm_i915_memory_region_info>(
|
||||||
|
physical_device,
|
||||||
|
DRM_I915_QUERY_MEMORY_REGIONS as u64,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let regions = query_mem_regions.entries_slice();
|
||||||
|
let mut info = I915MemoryInfo::default();
|
||||||
|
|
||||||
|
for region in regions {
|
||||||
|
// SAFETY: Accessing a C union's fields is unsafe in Rust.
|
||||||
|
let (probed_cpu_visible_size, unallocated_cpu_visible_size) = unsafe {
|
||||||
|
(
|
||||||
|
region
|
||||||
|
.__bindgen_anon_1
|
||||||
|
.__bindgen_anon_1
|
||||||
|
.probed_cpu_visible_size,
|
||||||
|
region
|
||||||
|
.__bindgen_anon_1
|
||||||
|
.__bindgen_anon_1
|
||||||
|
.unallocated_cpu_visible_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match region.region.memory_class as u32 {
|
||||||
|
I915_MEMORY_CLASS_SYSTEM => {
|
||||||
|
info.sysmem_total = region.probed_size;
|
||||||
|
info.sysmem_free = region.unallocated_size;
|
||||||
|
}
|
||||||
|
I915_MEMORY_CLASS_DEVICE => {
|
||||||
|
if probed_cpu_visible_size > 0 {
|
||||||
|
info.vram_mappable_total = probed_cpu_visible_size;
|
||||||
|
info.vram_unmappable_total = region.probed_size - probed_cpu_visible_size;
|
||||||
|
if region.unallocated_size != u64::MAX {
|
||||||
|
info.vram_mappable_free = unallocated_cpu_visible_size;
|
||||||
|
info.vram_unmappable_free =
|
||||||
|
region.unallocated_size - unallocated_cpu_visible_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info.vram_mappable_total = region.probed_size;
|
||||||
|
info.vram_unmappable_total = 0;
|
||||||
|
if region.unallocated_size != u64::MAX {
|
||||||
|
info.vram_mappable_free = region.unallocated_size;
|
||||||
|
info.vram_unmappable_free = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct I915 {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
mem_props: MagmaMemoryProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct I915Context {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
context_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct I915Buffer {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I915 {
|
||||||
|
pub fn new(physical_device: Arc<dyn PhysicalDevice>) -> MesaResult<I915> {
|
||||||
|
let mut val: i32 = 0;
|
||||||
|
let mut getparam = drm_i915_getparam {
|
||||||
|
param: I915_PARAM_HAS_ALIASING_PPGTT as i32,
|
||||||
|
value: &mut val as *mut _,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_i915_getparam struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_i915_getparam(physical_device.as_fd().unwrap(), &mut getparam)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mem_info = i915_query_memory_regions(&physical_device).unwrap_or_default();
|
||||||
|
let mut mem_props: MagmaMemoryProperties = Default::default();
|
||||||
|
|
||||||
|
if mem_info.sysmem_total > 0 {
|
||||||
|
mem_props.add_heap(mem_info.sysmem_total, MAGMA_HEAP_CPU_VISIBLE_BIT);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if mem_info.vram_mappable_total > 0 {
|
||||||
|
mem_props.add_heap(
|
||||||
|
mem_info.vram_mappable_total,
|
||||||
|
MAGMA_HEAP_CPU_VISIBLE_BIT | MAGMA_HEAP_DEVICE_LOCAL_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if mem_info.vram_unmappable_total > 0 {
|
||||||
|
mem_props.add_heap(mem_info.vram_unmappable_total, MAGMA_HEAP_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.add_memory_type(MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if mem_props.memory_heap_count == 0 {
|
||||||
|
// Fallback for older kernels
|
||||||
|
mem_props.add_heap(4 * 1024 * 1024 * 1024, MAGMA_HEAP_CPU_VISIBLE_BIT);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(I915 {
|
||||||
|
physical_device,
|
||||||
|
mem_props,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for I915 {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Ok(self.mem_props.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
if heap_idx >= self.mem_props.memory_heap_count {
|
||||||
|
return Err(MesaError::WithContext("Heap Index out of bounds"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mem_info = i915_query_memory_regions(&self.physical_device)?;
|
||||||
|
let heap = &self.mem_props.memory_heaps[heap_idx as usize];
|
||||||
|
|
||||||
|
let (budget, free) = if heap.is_cpu_visible() && !heap.is_device_local() {
|
||||||
|
(mem_info.sysmem_total, mem_info.sysmem_free)
|
||||||
|
} else if heap.is_cpu_visible() && heap.is_device_local() {
|
||||||
|
(mem_info.vram_mappable_total, mem_info.vram_mappable_free)
|
||||||
|
} else if !heap.is_cpu_visible() && heap.is_device_local() {
|
||||||
|
(
|
||||||
|
mem_info.vram_unmappable_total,
|
||||||
|
mem_info.vram_unmappable_free,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return Err(MesaError::Unsupported);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(MagmaHeapBudget {
|
||||||
|
budget,
|
||||||
|
usage: budget - free,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, _device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
let ctx = I915Context::new(self.physical_device.clone())?;
|
||||||
|
Ok(Arc::new(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let buf = I915Buffer::new(self.physical_device.clone(), create_info)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let gem_handle = self.physical_device.import(info.handle)?;
|
||||||
|
let buf = I915Buffer::from_existing(
|
||||||
|
self.physical_device.clone(),
|
||||||
|
gem_handle,
|
||||||
|
info.size.try_into()?,
|
||||||
|
)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for I915 {}
|
||||||
|
impl PlatformDevice for I915 {}
|
||||||
|
|
||||||
|
impl I915Context {
|
||||||
|
fn new(physical_device: Arc<dyn PhysicalDevice>) -> MesaResult<I915Context> {
|
||||||
|
let mut ctx_create = drm_i915_gem_context_create_ext::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_i915_gem_context_create_ext struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_i915_gem_context_create_ext(
|
||||||
|
physical_device.as_fd().unwrap(),
|
||||||
|
&mut ctx_create,
|
||||||
|
)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(I915Context {
|
||||||
|
physical_device,
|
||||||
|
context_id: ctx_create.ctx_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for I915Context {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let ctx_destroy = drm_i915_gem_context_destroy {
|
||||||
|
ctx_id: self.context_id,
|
||||||
|
pad: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_i915_gem_context_destroy struct
|
||||||
|
let result = unsafe {
|
||||||
|
drm_ioctl_i915_gem_context_destroy(self.physical_device.as_fd().unwrap(), &ctx_destroy)
|
||||||
|
};
|
||||||
|
log_status!(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for I915Context {}
|
||||||
|
|
||||||
|
impl I915Buffer {
|
||||||
|
fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<I915Buffer> {
|
||||||
|
let mut gem_create = drm_i915_gem_create {
|
||||||
|
size: create_info.size,
|
||||||
|
handle: 0,
|
||||||
|
pad: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_i915_gem_create struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_i915_gem_create(physical_device.as_fd().unwrap(), &mut gem_create)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(I915Buffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle: gem_create.handle,
|
||||||
|
size: create_info.size.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_existing(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
) -> MesaResult<I915Buffer> {
|
||||||
|
Ok(I915Buffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericBuffer for I915Buffer {
|
||||||
|
fn map(&self, _buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let mut gem_mmap = drm_i915_gem_mmap_offset {
|
||||||
|
handle: self.gem_handle,
|
||||||
|
pad: 0,
|
||||||
|
offset: 0,
|
||||||
|
flags: I915_MMAP_OFFSET_WC as u64,
|
||||||
|
extensions: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_i915_gem_mmap_offset struct
|
||||||
|
let offset = unsafe {
|
||||||
|
drm_ioctl_i915_gem_mmap_offset(self.physical_device.as_fd().unwrap(), &mut gem_mmap)?;
|
||||||
|
gem_mmap.offset
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapping = self.physical_device.cpu_map(offset, self.size)?;
|
||||||
|
Ok(Arc::new(mapping))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle> {
|
||||||
|
self.physical_device.export(self.gem_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate(
|
||||||
|
&self,
|
||||||
|
_sync_flags: u64,
|
||||||
|
_ranges: &[crate::magma_defines::MagmaMappedMemoryRange],
|
||||||
|
) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(
|
||||||
|
&self,
|
||||||
|
_sync_flags: u64,
|
||||||
|
_ranges: &[crate::magma_defines::MagmaMappedMemoryRange],
|
||||||
|
) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for I915Buffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.physical_device.close(self.gem_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer for I915Buffer {}
|
||||||
|
|
||||||
|
unsafe impl Send for I915 {}
|
||||||
|
unsafe impl Sync for I915 {}
|
||||||
|
|
||||||
|
unsafe impl Send for I915Context {}
|
||||||
|
unsafe impl Sync for I915Context {}
|
||||||
|
|
||||||
|
unsafe impl Send for I915Buffer {}
|
||||||
|
unsafe impl Sync for I915Buffer {}
|
||||||
30
src/magma/sys/linux/macros.rs
Normal file
30
src/magma/sys/linux/macros.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ioctl_write_ptr {
|
||||||
|
($name:ident, $ioty:expr, $nr:expr, $ty:ty) => {
|
||||||
|
pub unsafe fn $name(fd: std::os::fd::BorrowedFd, data: &$ty) -> std::io::Result<()> {
|
||||||
|
const OPCODE: rustix::ioctl::Opcode =
|
||||||
|
rustix::ioctl::opcode::write::<$ty>($ioty as u8, $nr as u8);
|
||||||
|
Ok(rustix::ioctl::ioctl(
|
||||||
|
fd,
|
||||||
|
rustix::ioctl::Setter::<OPCODE, $ty>::new(*data),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ioctl_readwrite {
|
||||||
|
($name:ident, $ioty:expr, $nr:expr, $ty:ty) => {
|
||||||
|
pub unsafe fn $name(fd: std::os::fd::BorrowedFd, data: &mut $ty) -> std::io::Result<()> {
|
||||||
|
const OPCODE: rustix::ioctl::Opcode =
|
||||||
|
rustix::ioctl::opcode::read_write::<$ty>($ioty as u8, $nr as u8);
|
||||||
|
Ok(rustix::ioctl::ioctl(
|
||||||
|
fd,
|
||||||
|
rustix::ioctl::Updater::<OPCODE, $ty>::new(data),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
21
src/magma/sys/linux/mod.rs
Normal file
21
src/magma/sys/linux/mod.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
mod amdgpu;
|
||||||
|
mod bindings;
|
||||||
|
mod common;
|
||||||
|
mod drm;
|
||||||
|
pub mod flexible_array;
|
||||||
|
mod i915;
|
||||||
|
mod macros;
|
||||||
|
mod msm;
|
||||||
|
mod xe;
|
||||||
|
|
||||||
|
pub use amdgpu::AmdGpu;
|
||||||
|
pub use common::enumerate_devices;
|
||||||
|
pub use common::PlatformDevice;
|
||||||
|
pub use common::PlatformPhysicalDevice;
|
||||||
|
pub use drm::*;
|
||||||
|
pub use i915::I915;
|
||||||
|
pub use msm::Msm;
|
||||||
|
pub use xe::Xe;
|
||||||
269
src/magma/sys/linux/msm.rs
Normal file
269
src/magma/sys/linux/msm.rs
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::ioctl_readwrite;
|
||||||
|
use crate::ioctl_write_ptr;
|
||||||
|
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericBuffer;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_COMMAND_BASE;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_IOCTL_BASE;
|
||||||
|
use crate::sys::linux::bindings::msm_bindings::*;
|
||||||
|
use crate::sys::linux::PlatformDevice;
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_msm_gem_new,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_GEM_NEW,
|
||||||
|
drm_msm_gem_new
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_msm_gem_info,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_GEM_INFO,
|
||||||
|
drm_msm_gem_info
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
msm_gem_cpu_prep,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP,
|
||||||
|
drm_msm_gem_cpu_prep
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
msm_gem_cpu_fini,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI,
|
||||||
|
drm_msm_gem_cpu_fini
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
msm_submitqueue_new,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW,
|
||||||
|
drm_msm_submitqueue
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
msm_submitqueue_close,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE,
|
||||||
|
__u32
|
||||||
|
);
|
||||||
|
|
||||||
|
struct MsmContext {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
submit_queue_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MsmContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: This is a valid file descriptor and a valid submitqueue id.
|
||||||
|
unsafe {
|
||||||
|
let _ =
|
||||||
|
msm_submitqueue_close(self.physical_device.as_fd().unwrap(), &self.submit_queue_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for MsmContext {}
|
||||||
|
|
||||||
|
pub struct Msm {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
mem_props: MagmaMemoryProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MsmBuffer {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Msm {
|
||||||
|
pub fn new(physical_device: Arc<dyn PhysicalDevice>) -> Msm {
|
||||||
|
Msm {
|
||||||
|
physical_device,
|
||||||
|
mem_props: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for Msm {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, _heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, _device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
let mut new_submit_queue = drm_msm_submitqueue {
|
||||||
|
flags: 0,
|
||||||
|
prio: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: This is a valid file descriptor.
|
||||||
|
unsafe {
|
||||||
|
msm_submitqueue_new(self.physical_device.as_fd().unwrap(), &mut new_submit_queue)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Arc::new(MsmContext {
|
||||||
|
physical_device: self.physical_device.clone(),
|
||||||
|
submit_queue_id: new_submit_queue.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let buf = MsmBuffer::new(self.physical_device.clone(), create_info, &self.mem_props)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let gem_handle = self.physical_device.import(info.handle)?;
|
||||||
|
let buf = MsmBuffer::from_existing(
|
||||||
|
self.physical_device.clone(),
|
||||||
|
gem_handle,
|
||||||
|
info.size.try_into()?,
|
||||||
|
)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformDevice for Msm {}
|
||||||
|
impl Device for Msm {}
|
||||||
|
|
||||||
|
impl MsmBuffer {
|
||||||
|
fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
_mem_props: &MagmaMemoryProperties,
|
||||||
|
) -> MesaResult<MsmBuffer> {
|
||||||
|
let mut gem_new = drm_msm_gem_new {
|
||||||
|
size: create_info.size,
|
||||||
|
flags: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: This is a well-formed ioctl conforming the driver specificiation.
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_msm_gem_new(physical_device.as_fd().unwrap(), &mut gem_new)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MsmBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle: gem_new.handle,
|
||||||
|
size: create_info.size.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_existing(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
) -> MesaResult<MsmBuffer> {
|
||||||
|
Ok(MsmBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericBuffer for MsmBuffer {
|
||||||
|
fn map(&self, _buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let mut gem_info: drm_msm_gem_info = drm_msm_gem_info {
|
||||||
|
handle: self.gem_handle,
|
||||||
|
info: MSM_INFO_GET_OFFSET,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_msm_gem_info
|
||||||
|
let offset = unsafe {
|
||||||
|
drm_ioctl_msm_gem_info(self.physical_device.as_fd().unwrap(), &mut gem_info)?;
|
||||||
|
gem_info.value
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapping = self.physical_device.cpu_map(offset, self.size)?;
|
||||||
|
Ok(Arc::new(mapping))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle> {
|
||||||
|
self.physical_device.export(self.gem_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
let prep = drm_msm_gem_cpu_prep {
|
||||||
|
handle: self.gem_handle,
|
||||||
|
op: MSM_PREP_READ | MSM_PREP_WRITE,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: This is a valid file descriptor and a valid gem handle.
|
||||||
|
unsafe {
|
||||||
|
msm_gem_cpu_prep(self.physical_device.as_fd().unwrap(), &prep)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
let fini = drm_msm_gem_cpu_fini {
|
||||||
|
handle: self.gem_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: This is a valid file descriptor and a valid gem handle.
|
||||||
|
unsafe {
|
||||||
|
msm_gem_cpu_fini(self.physical_device.as_fd().unwrap(), &fini)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MsmBuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// GEM close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer for MsmBuffer {}
|
||||||
|
|
||||||
|
unsafe impl Send for Msm {}
|
||||||
|
unsafe impl Sync for Msm {}
|
||||||
|
|
||||||
|
unsafe impl Send for MsmContext {}
|
||||||
|
unsafe impl Sync for MsmContext {}
|
||||||
|
|
||||||
|
unsafe impl Send for MsmBuffer {}
|
||||||
|
unsafe impl Sync for MsmBuffer {}
|
||||||
539
src/magma/sys/linux/xe.rs
Normal file
539
src/magma/sys/linux/xe.rs
Normal file
|
|
@ -0,0 +1,539 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use mesa3d_util::log_status;
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
|
||||||
|
use crate::ioctl_readwrite;
|
||||||
|
use crate::ioctl_write_ptr;
|
||||||
|
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericBuffer;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_CPU_VISIBLE_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||||
|
|
||||||
|
use crate::flexible_array_impl;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_COMMAND_BASE;
|
||||||
|
use crate::sys::linux::bindings::drm_bindings::DRM_IOCTL_BASE;
|
||||||
|
use crate::sys::linux::bindings::xe_bindings::*;
|
||||||
|
use crate::sys::linux::flexible_array::FlexibleArray;
|
||||||
|
use crate::sys::linux::flexible_array::FlexibleArrayWrapper;
|
||||||
|
use crate::sys::linux::PlatformDevice;
|
||||||
|
|
||||||
|
// This information is also useful to the system side of a driver. Should be separated
|
||||||
|
// into it's own crate or module.
|
||||||
|
const GEN12_IDS: [u16; 50] = [
|
||||||
|
0x4c8a, 0x4c8b, 0x4c8c, 0x4c90, 0x4c9a, 0x4680, 0x4681, 0x4682, 0x4683, 0x4688, 0x4689, 0x4690,
|
||||||
|
0x4691, 0x4692, 0x4693, 0x4698, 0x4699, 0x4626, 0x4628, 0x462a, 0x46a0, 0x46a1, 0x46a2, 0x46a3,
|
||||||
|
0x46a6, 0x46a8, 0x46aa, 0x46b0, 0x46b1, 0x46b2, 0x46b3, 0x46c0, 0x46c1, 0x46c2, 0x46c3, 0x9A40,
|
||||||
|
0x9A49, 0x9A59, 0x9A60, 0x9A68, 0x9A70, 0x9A78, 0x9AC0, 0x9AC9, 0x9AD9, 0x9AF8, 0x4905, 0x4906,
|
||||||
|
0x4907, 0x4908,
|
||||||
|
];
|
||||||
|
|
||||||
|
const ADLP_IDS: [u16; 23] = [
|
||||||
|
0x46A0, 0x46A1, 0x46A2, 0x46A3, 0x46A6, 0x46A8, 0x46AA, 0x462A, 0x4626, 0x4628, 0x46B0, 0x46B1,
|
||||||
|
0x46B2, 0x46B3, 0x46C0, 0x46C1, 0x46C2, 0x46C3, 0x46D0, 0x46D1, 0x46D2, 0x46D3, 0x46D4,
|
||||||
|
];
|
||||||
|
|
||||||
|
const RPLP_IDS: [u16; 10] = [
|
||||||
|
0xA720, 0xA721, 0xA7A0, 0xA7A1, 0xA7A8, 0xA7A9, 0xA7AA, 0xA7AB, 0xA7AC, 0xA7AD,
|
||||||
|
];
|
||||||
|
|
||||||
|
const MTL_IDS: [u16; 5] = [0x7D40, 0x7D60, 0x7D45, 0x7D55, 0x7DD5];
|
||||||
|
|
||||||
|
const LNL_IDS: [u16; 3] = [0x6420, 0x64A0, 0x64B0];
|
||||||
|
|
||||||
|
const PTL_IDS: [u16; 8] = [
|
||||||
|
0xB080, 0xB081, 0xB082, 0xB083, 0xB08F, 0xB090, 0xB0A0, 0xB0B0,
|
||||||
|
];
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_xe_device_query,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_XE_DEVICE_QUERY,
|
||||||
|
drm_xe_device_query
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_xe_gem_create,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_XE_GEM_CREATE,
|
||||||
|
drm_xe_gem_create
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_xe_gem_mmap_offset,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_XE_GEM_MMAP_OFFSET,
|
||||||
|
drm_xe_gem_mmap_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_readwrite!(
|
||||||
|
drm_ioctl_xe_vm_create,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_XE_VM_CREATE,
|
||||||
|
drm_xe_vm_create
|
||||||
|
);
|
||||||
|
|
||||||
|
ioctl_write_ptr!(
|
||||||
|
drm_ioctl_xe_vm_destroy,
|
||||||
|
DRM_IOCTL_BASE,
|
||||||
|
DRM_COMMAND_BASE + DRM_XE_VM_DESTROY,
|
||||||
|
drm_xe_vm_destroy
|
||||||
|
);
|
||||||
|
|
||||||
|
flexible_array_impl!(drm_xe_query_config, __u64, num_params, info);
|
||||||
|
flexible_array_impl!(
|
||||||
|
drm_xe_query_mem_regions,
|
||||||
|
drm_xe_mem_region,
|
||||||
|
num_mem_regions,
|
||||||
|
mem_regions
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct Xe {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
_gtt_size: u64,
|
||||||
|
_mem_alignment: u64,
|
||||||
|
mem_props: MagmaMemoryProperties,
|
||||||
|
sysmem_instance: u16,
|
||||||
|
vram_instance: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XeBuffer {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XeContext {
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
vm_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xe_device_query<T, S>(
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
query_id: u32,
|
||||||
|
) -> MesaResult<FlexibleArrayWrapper<T, S>>
|
||||||
|
where
|
||||||
|
T: FlexibleArray<S> + Default,
|
||||||
|
{
|
||||||
|
let mut device_query: drm_xe_device_query = drm_xe_device_query {
|
||||||
|
query: query_id,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_xe_device_query
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_xe_device_query(physical_device.as_fd().unwrap(), &mut device_query)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
let total_size = device_query.size;
|
||||||
|
let mut wrapper = FlexibleArrayWrapper::<T, S>::from_total_size(total_size as usize);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_xe_device_query
|
||||||
|
// - drm_xe_device_query.data: we trust the FlexibleArrayWrapper to hold enough space
|
||||||
|
unsafe {
|
||||||
|
device_query.data = wrapper.as_mut_ptr() as __u64;
|
||||||
|
drm_ioctl_xe_device_query(physical_device.as_fd().unwrap(), &mut device_query)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines and sets the graphics version of the Intel device based on its ID.
|
||||||
|
fn determine_graphics_version(pci_device_id: u16) -> MesaResult<u32> {
|
||||||
|
let mut graphics_version = 0;
|
||||||
|
if ADLP_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if RPLP_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if MTL_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if LNL_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if PTL_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if GEN12_IDS.contains(&pci_device_id) {
|
||||||
|
graphics_version = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if graphics_version != 0 {
|
||||||
|
Ok(graphics_version)
|
||||||
|
} else {
|
||||||
|
Err(MesaError::WithContext("missing intel pci-id"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct XeMemoryInfo {
|
||||||
|
vram_size: u64,
|
||||||
|
vram_used: u64,
|
||||||
|
sysmem_size: u64,
|
||||||
|
sysmem_used: u64,
|
||||||
|
vram_cpu_visible_size: u64,
|
||||||
|
vram_cpu_visible_used: u64,
|
||||||
|
sysmem_instance: u16,
|
||||||
|
vram_instance: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xe_query_memory_regions(physical_device: &Arc<dyn PhysicalDevice>) -> MesaResult<XeMemoryInfo> {
|
||||||
|
let mut memory_info: XeMemoryInfo = Default::default();
|
||||||
|
let query_mem_regions = xe_device_query::<drm_xe_query_mem_regions, drm_xe_mem_region>(
|
||||||
|
physical_device,
|
||||||
|
DRM_XE_DEVICE_QUERY_MEM_REGIONS,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mem_regions = query_mem_regions.entries_slice();
|
||||||
|
for region in mem_regions {
|
||||||
|
match region.mem_class as u32 {
|
||||||
|
DRM_XE_MEM_REGION_CLASS_SYSMEM => {
|
||||||
|
if memory_info.sysmem_size != 0 {
|
||||||
|
return Err(MesaError::WithContext("sysmem_size should not be set"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should really use sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE) for the
|
||||||
|
// host-visible heap. rustix has get_page_size(), but not get_num_pages..
|
||||||
|
memory_info.sysmem_size = region.total_size;
|
||||||
|
memory_info.sysmem_used = region.used;
|
||||||
|
memory_info.sysmem_instance = region.instance;
|
||||||
|
}
|
||||||
|
DRM_XE_MEM_REGION_CLASS_VRAM => {
|
||||||
|
if memory_info.vram_size != 0 || memory_info.vram_cpu_visible_size != 0 {
|
||||||
|
return Err(MesaError::WithContext("one vram value should be zero"));
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_info.vram_cpu_visible_size = region.cpu_visible_size;
|
||||||
|
memory_info.vram_size = region.total_size - region.cpu_visible_size;
|
||||||
|
memory_info.vram_cpu_visible_used = region.cpu_visible_used;
|
||||||
|
memory_info.vram_used = region.used - region.cpu_visible_used;
|
||||||
|
memory_info.vram_instance = region.instance;
|
||||||
|
}
|
||||||
|
_ => return Err(MesaError::Unsupported),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(memory_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Xe {
|
||||||
|
pub fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: &MagmaPciInfo,
|
||||||
|
) -> MesaResult<Xe> {
|
||||||
|
let _graphics_version = determine_graphics_version(pci_info.device_id)?;
|
||||||
|
let mut mem_props: MagmaMemoryProperties = Default::default();
|
||||||
|
|
||||||
|
let query_config = xe_device_query::<drm_xe_query_config, __u64>(
|
||||||
|
&physical_device,
|
||||||
|
DRM_XE_DEVICE_QUERY_CONFIG,
|
||||||
|
)?;
|
||||||
|
let config = query_config.entries_slice();
|
||||||
|
let _config_len = config.len();
|
||||||
|
|
||||||
|
let gtt_size = 1u64 << config[DRM_XE_QUERY_CONFIG_VA_BITS as usize];
|
||||||
|
let mem_alignment = config[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT as usize];
|
||||||
|
|
||||||
|
let memory_info = xe_query_memory_regions(&physical_device)?;
|
||||||
|
if memory_info.sysmem_size != 0 {
|
||||||
|
// Non-LLC case ignored.
|
||||||
|
mem_props.add_heap(memory_info.sysmem_size, MAGMA_HEAP_CPU_VISIBLE_BIT);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_info.vram_cpu_visible_size != 0 {
|
||||||
|
mem_props.add_heap(
|
||||||
|
memory_info.vram_cpu_visible_size,
|
||||||
|
MAGMA_HEAP_CPU_VISIBLE_BIT | MAGMA_HEAP_DEVICE_LOCAL_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_info.vram_size != 0 {
|
||||||
|
mem_props.add_heap(memory_info.vram_size, MAGMA_HEAP_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.add_memory_type(MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Xe {
|
||||||
|
physical_device,
|
||||||
|
_gtt_size: gtt_size,
|
||||||
|
_mem_alignment: mem_alignment,
|
||||||
|
mem_props,
|
||||||
|
sysmem_instance: memory_info.sysmem_instance,
|
||||||
|
vram_instance: memory_info.vram_instance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for Xe {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Ok(self.mem_props.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
if heap_idx >= self.mem_props.memory_heap_count {
|
||||||
|
return Err(MesaError::WithContext("Heap Index out of bounds"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory_info = xe_query_memory_regions(&self.physical_device)?;
|
||||||
|
let heap = &self.mem_props.memory_heaps[heap_idx as usize];
|
||||||
|
|
||||||
|
let (budget, usage) = if heap.is_device_local() && heap.is_cpu_visible() {
|
||||||
|
(
|
||||||
|
memory_info.vram_cpu_visible_size,
|
||||||
|
memory_info.vram_cpu_visible_used,
|
||||||
|
)
|
||||||
|
} else if heap.is_device_local() {
|
||||||
|
(memory_info.vram_size, memory_info.vram_used)
|
||||||
|
} else if heap.is_cpu_visible() {
|
||||||
|
(memory_info.sysmem_size, memory_info.sysmem_used)
|
||||||
|
} else {
|
||||||
|
return Err(MesaError::Unsupported);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(MagmaHeapBudget { budget, usage })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, _device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
let ctx = XeContext::new(self.physical_device.clone(), 0)?;
|
||||||
|
Ok(Arc::new(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let buf = XeBuffer::new(
|
||||||
|
self.physical_device.clone(),
|
||||||
|
create_info,
|
||||||
|
&self.mem_props,
|
||||||
|
self.sysmem_instance,
|
||||||
|
self.vram_instance,
|
||||||
|
)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let gem_handle = self.physical_device.import(info.handle)?;
|
||||||
|
let buf = XeBuffer::from_existing(
|
||||||
|
self.physical_device.clone(),
|
||||||
|
gem_handle,
|
||||||
|
info.size.try_into()?,
|
||||||
|
)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformDevice for Xe {}
|
||||||
|
impl Device for Xe {}
|
||||||
|
|
||||||
|
impl XeContext {
|
||||||
|
fn new(physical_device: Arc<dyn PhysicalDevice>, _priority: i32) -> MesaResult<XeContext> {
|
||||||
|
let mut vm_create = drm_xe_vm_create {
|
||||||
|
flags: DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_xe_vm_create struct
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_xe_vm_create(physical_device.as_fd().unwrap(), &mut vm_create)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(XeContext {
|
||||||
|
physical_device,
|
||||||
|
vm_id: vm_create.vm_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for XeContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let destroy = drm_xe_vm_destroy {
|
||||||
|
vm_id: self.vm_id,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_xe_vm_destroy struct
|
||||||
|
let result =
|
||||||
|
unsafe { drm_ioctl_xe_vm_destroy(self.physical_device.as_fd().unwrap(), &destroy) };
|
||||||
|
log_status!(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for XeContext {}
|
||||||
|
|
||||||
|
impl XeBuffer {
|
||||||
|
fn new(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
mem_props: &MagmaMemoryProperties,
|
||||||
|
sysmem_instance: u16,
|
||||||
|
vram_instance: u16,
|
||||||
|
) -> MesaResult<XeBuffer> {
|
||||||
|
let mut gem_create: drm_xe_gem_create = Default::default();
|
||||||
|
let mut pxp_ext: drm_xe_ext_set_property = Default::default();
|
||||||
|
|
||||||
|
gem_create.size = create_info.size;
|
||||||
|
let memory_type = mem_props.get_memory_type(create_info.memory_type_idx);
|
||||||
|
let memory_heap = mem_props.get_memory_heap(memory_type.heap_idx);
|
||||||
|
|
||||||
|
if memory_type.is_cached() {
|
||||||
|
gem_create.cpu_caching = DRM_XE_GEM_CPU_CACHING_WB as u16;
|
||||||
|
} else {
|
||||||
|
gem_create.cpu_caching = DRM_XE_GEM_CPU_CACHING_WC as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_heap.is_cpu_visible() && memory_heap.is_device_local() {
|
||||||
|
gem_create.flags |= DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM;
|
||||||
|
gem_create.placement |= 1 << sysmem_instance;
|
||||||
|
gem_create.placement |= 1 << vram_instance;
|
||||||
|
} else if memory_heap.is_device_local() {
|
||||||
|
gem_create.placement |= 1 << vram_instance;
|
||||||
|
} else if memory_heap.is_cpu_visible() {
|
||||||
|
gem_create.placement |= 1 << sysmem_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory_type.is_protected() {
|
||||||
|
pxp_ext.base.name = DRM_XE_GEM_CREATE_EXTENSION_SET_PROPERTY;
|
||||||
|
pxp_ext.property = DRM_XE_GEM_CREATE_SET_PROPERTY_PXP_TYPE;
|
||||||
|
pxp_ext.value = DRM_XE_PXP_TYPE_HWDRM as u64;
|
||||||
|
gem_create.extensions = &pxp_ext as *const drm_xe_ext_set_property as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_amdgpu_gem_create_args
|
||||||
|
unsafe {
|
||||||
|
drm_ioctl_xe_gem_create(physical_device.as_fd().unwrap(), &mut gem_create)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(XeBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle: gem_create.handle,
|
||||||
|
size: create_info.size.try_into()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_existing(
|
||||||
|
physical_device: Arc<dyn PhysicalDevice>,
|
||||||
|
gem_handle: u32,
|
||||||
|
size: usize,
|
||||||
|
) -> MesaResult<XeBuffer> {
|
||||||
|
Ok(XeBuffer {
|
||||||
|
physical_device,
|
||||||
|
gem_handle,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericBuffer for XeBuffer {
|
||||||
|
fn map(&self, _buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let mut xe_offset: drm_xe_gem_mmap_offset = Default::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// Valid arguments are supplied for the following arguments:
|
||||||
|
// - Underlying descriptor
|
||||||
|
// - drm_xe_gem_mmap_offset
|
||||||
|
let offset = unsafe {
|
||||||
|
xe_offset.handle = self.gem_handle;
|
||||||
|
drm_ioctl_xe_gem_mmap_offset(self.physical_device.as_fd().unwrap(), &mut xe_offset)?;
|
||||||
|
xe_offset.offset
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapping = self.physical_device.cpu_map(offset, self.size)?;
|
||||||
|
Ok(Arc::new(mapping))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle> {
|
||||||
|
self.physical_device.export(self.gem_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for XeBuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.physical_device.close(self.gem_handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer for XeBuffer {}
|
||||||
|
|
||||||
|
unsafe impl Send for Xe {}
|
||||||
|
unsafe impl Sync for Xe {}
|
||||||
|
|
||||||
|
unsafe impl Send for XeContext {}
|
||||||
|
unsafe impl Sync for XeContext {}
|
||||||
|
|
||||||
|
unsafe impl Send for XeBuffer {}
|
||||||
|
unsafe impl Sync for XeBuffer {}
|
||||||
18
src/magma/sys/mod.rs
Normal file
18
src/magma/sys/mod.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||||
|
pub mod linux;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub mod windows;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(any(target_os = "android", target_os = "linux"))] {
|
||||||
|
pub use linux as platform;
|
||||||
|
} else if #[cfg(windows)] {
|
||||||
|
pub use windows as platform;
|
||||||
|
} else {
|
||||||
|
compile_error!("Unsupported platform");
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/magma/sys/windows/amd.rs
Normal file
106
src/magma/sys/windows/amd.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Taken from gfxstrand@'s WDDM branch, which seems reversed engineered.
|
||||||
|
//
|
||||||
|
// https://gitlab.freedesktop.org/gfxstrand/mesa/-/tree/radv/wddm2?ref_type=heads
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::sys::windows::VendorPrivateData;
|
||||||
|
|
||||||
|
static AMD_CREATE_ALLOC_PDATA: [u32; 15] = [
|
||||||
|
0x00000000, 0x00000000, 0x00000080, 0x00000420, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
];
|
||||||
|
|
||||||
|
static AMD_ALLOC_PDATA: [u32; 206] = [
|
||||||
|
0x000002f8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000036f, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
||||||
|
0x000002f8, 0x0000000d, 0x00000080, 0x00000000, 0xa0002008, 0x00270000, 0x00010000, 0x00001805,
|
||||||
|
0x00000004, 0x00000004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x01080000, 0x00270000, 0x00000000, 0x00000000, 0x00000000, 0x00270000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00400000, 0x00000001, 0x00000001, 0x00000001,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000001e4, 0x00000000, 0x00000000, 0x00000008,
|
||||||
|
0x00000000, 0x00000120, 0x00020000, 0x00000020, 0x00000001, 0x00000000, 0x00270000, 0x00000001,
|
||||||
|
0x00270000, 0x00000001, 0x00000001, 0x00270000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00270000, 0x00000001, 0x00000001, 0x00270000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000114, 0x00000000, 0x00000000, 0x00000000, 0x00270000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct Amd(pub ());
|
||||||
|
|
||||||
|
impl VendorPrivateData for Amd {
|
||||||
|
fn createallocation_pdata(&self) -> Vec<u32> {
|
||||||
|
Vec::from(AMD_CREATE_ALLOC_PDATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocationinfo2_pdata(
|
||||||
|
&self,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
mem_props: &MagmaMemoryProperties,
|
||||||
|
) -> Vec<u32> {
|
||||||
|
let mut alloc_pdata = AMD_ALLOC_PDATA;
|
||||||
|
let memory_type = mem_props.get_memory_type(create_info.memory_type_idx);
|
||||||
|
|
||||||
|
// FIXME: gpu_info.pte_fragment_size, alignment
|
||||||
|
// Need GPU topology crate
|
||||||
|
let size: u32 = 0;
|
||||||
|
let phys_size: u32 = 0;
|
||||||
|
let phys_alignment: u32 = 0;
|
||||||
|
|
||||||
|
alloc_pdata[21] = phys_size;
|
||||||
|
alloc_pdata[22] = phys_alignment;
|
||||||
|
alloc_pdata[42] = phys_size;
|
||||||
|
alloc_pdata[46] = phys_size;
|
||||||
|
alloc_pdata[70] = size;
|
||||||
|
alloc_pdata[72] = phys_size;
|
||||||
|
alloc_pdata[75] = phys_size;
|
||||||
|
alloc_pdata[92] = size;
|
||||||
|
alloc_pdata[95] = phys_size;
|
||||||
|
alloc_pdata[141] = phys_size;
|
||||||
|
|
||||||
|
// Some sort of flags field
|
||||||
|
alloc_pdata[20] = 0x00002000;
|
||||||
|
|
||||||
|
if memory_type.is_coherent() {
|
||||||
|
alloc_pdata[20] |= 0xa0000000;
|
||||||
|
alloc_pdata[41] |= 0x00080000;
|
||||||
|
} else {
|
||||||
|
alloc_pdata[20] |= 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write-back cached
|
||||||
|
if memory_type.is_device_local() {
|
||||||
|
alloc_pdata[20] |= 0x00000005;
|
||||||
|
alloc_pdata[24] = 0x00030101;
|
||||||
|
alloc_pdata[25] = 0x00000001;
|
||||||
|
} else if memory_type.is_coherent() && !memory_type.is_cached() {
|
||||||
|
// RADEON_FLAG_GTT_WC
|
||||||
|
alloc_pdata[20] |= 0x00000004;
|
||||||
|
alloc_pdata[24] = 3;
|
||||||
|
alloc_pdata[25] = 3;
|
||||||
|
} else {
|
||||||
|
alloc_pdata[20] |= 0x00000008;
|
||||||
|
alloc_pdata[24] = 4;
|
||||||
|
alloc_pdata[25] = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec::from(alloc_pdata)
|
||||||
|
}
|
||||||
|
}
|
||||||
673
src/magma/sys/windows/d3dkmt_common.rs
Normal file
673
src/magma/sys/windows/d3dkmt_common.rs
Normal file
|
|
@ -0,0 +1,673 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::slice::from_raw_parts;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libc::wcslen;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use mesa3d_util::IntoRawDescriptor;
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaError;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaMapping;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
|
||||||
|
use crate::check_ntstatus;
|
||||||
|
use crate::log_ntstatus;
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MagmaPciBusInfo;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::magma_defines::MAGMA_HEAP_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||||
|
use crate::magma_defines::MAGMA_SYNC_RANGES;
|
||||||
|
use crate::magma_defines::MAGMA_SYNC_WHOLE_RANGE;
|
||||||
|
use crate::magma_defines::MAGMA_VENDOR_ID_AMD;
|
||||||
|
|
||||||
|
use crate::sys::windows::Amd;
|
||||||
|
use crate::sys::windows::VendorPrivateData;
|
||||||
|
|
||||||
|
use crate::traits::AsVirtGpu;
|
||||||
|
use crate::traits::Buffer;
|
||||||
|
use crate::traits::Context;
|
||||||
|
use crate::traits::Device;
|
||||||
|
use crate::traits::GenericBuffer;
|
||||||
|
use crate::traits::GenericDevice;
|
||||||
|
use crate::traits::GenericPhysicalDevice;
|
||||||
|
use crate::traits::PhysicalDevice;
|
||||||
|
|
||||||
|
use windows_sys::Wdk::Graphics::Direct3D::*;
|
||||||
|
use windows_sys::Win32::Foundation::LUID;
|
||||||
|
|
||||||
|
type D3dkmtHandle = u32;
|
||||||
|
|
||||||
|
pub struct WddmAdapter {
|
||||||
|
handle: D3dkmtHandle,
|
||||||
|
_luid: LUID,
|
||||||
|
segment_group_size: D3DKMT_SEGMENTGROUPSIZEINFO,
|
||||||
|
_hw_sch_enabled: bool,
|
||||||
|
_hw_sch_supported: bool,
|
||||||
|
adapter_name: String,
|
||||||
|
chip_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WddmDevice {
|
||||||
|
handle: D3dkmtHandle,
|
||||||
|
adapter: Arc<dyn PhysicalDevice>,
|
||||||
|
vendor_private_data: Box<dyn VendorPrivateData>,
|
||||||
|
mem_props: MagmaMemoryProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WddmBuffer {
|
||||||
|
handle: D3dkmtHandle,
|
||||||
|
device: Arc<dyn Device>,
|
||||||
|
size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WddmContext {
|
||||||
|
handle: D3dkmtHandle,
|
||||||
|
_device: Arc<dyn Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WddmMapping {
|
||||||
|
_buffer: Arc<dyn Buffer>,
|
||||||
|
pdata: *mut c_void,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WindowsDevice {
|
||||||
|
fn as_wddm_handle(&self) -> D3dkmtHandle {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vendor_private_data(&self) -> Option<&dyn VendorPrivateData> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WindowsPhysicalDevice {
|
||||||
|
fn as_wddm_handle(&self) -> D3dkmtHandle {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn segment_group_size(&self) -> D3DKMT_SEGMENTGROUPSIZEINFO {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WddmAdapter {
|
||||||
|
pub fn new(handle: D3dkmtHandle, luid: LUID) -> WddmAdapter {
|
||||||
|
WddmAdapter {
|
||||||
|
handle,
|
||||||
|
_luid: luid,
|
||||||
|
segment_group_size: Default::default(),
|
||||||
|
_hw_sch_enabled: Default::default(),
|
||||||
|
_hw_sch_supported: Default::default(),
|
||||||
|
adapter_name: Default::default(),
|
||||||
|
chip_type: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize(&mut self) -> MesaResult<(MagmaPciInfo, MagmaPciBusInfo)> {
|
||||||
|
let mut pci_info: MagmaPciInfo = Default::default();
|
||||||
|
let mut pci_bus_info: MagmaPciBusInfo = Default::default();
|
||||||
|
|
||||||
|
let mut query_device_ids: D3DKMT_QUERY_DEVICE_IDS = Default::default();
|
||||||
|
let mut adapter_address: D3DKMT_ADAPTERADDRESS = Default::default();
|
||||||
|
|
||||||
|
let mut adapter_info = D3DKMT_QUERYADAPTERINFO {
|
||||||
|
hAdapter: self.handle,
|
||||||
|
Type: KMTQAITYPE_PHYSICALADAPTERDEVICEIDS,
|
||||||
|
pPrivateDriverData: &mut query_device_ids as *mut D3DKMT_QUERY_DEVICE_IDS
|
||||||
|
as *mut c_void,
|
||||||
|
PrivateDriverDataSize: std::mem::size_of::<D3DKMT_QUERY_DEVICE_IDS>() as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `adapter_info` is stack-allocated and properly typed.
|
||||||
|
// - `pPrivateDriverData` and `PrivateDriverDataSize` are both correct for the
|
||||||
|
// KMTQAITYPE_PHYSICALADAPTERDEVICEIDS operation
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryAdapterInfo(&mut adapter_info as *mut D3DKMT_QUERYADAPTERINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
adapter_info.Type = KMTQAITYPE_ADAPTERADDRESS;
|
||||||
|
adapter_info.pPrivateDriverData =
|
||||||
|
&mut adapter_address as *mut D3DKMT_ADAPTERADDRESS as *mut c_void;
|
||||||
|
adapter_info.PrivateDriverDataSize = std::mem::size_of::<D3DKMT_ADAPTERADDRESS>() as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `adapter_info` is stack-allocated and properly typed.
|
||||||
|
// - `pPrivateDriverData` and `PrivateDriverDataSize` are both correct for the
|
||||||
|
// KMTQAITYPE_ADAPTERADDRESS operation
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryAdapterInfo(&mut adapter_info as *mut D3DKMT_QUERYADAPTERINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut wddm_caps: D3DKMT_WDDM_2_7_CAPS = Default::default();
|
||||||
|
adapter_info.Type = KMTQAITYPE_WDDM_2_7_CAPS;
|
||||||
|
adapter_info.pPrivateDriverData =
|
||||||
|
&mut wddm_caps as *mut D3DKMT_WDDM_2_7_CAPS as *mut c_void;
|
||||||
|
adapter_info.PrivateDriverDataSize = std::mem::size_of::<D3DKMT_WDDM_2_7_CAPS>() as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `adapter_info` is stack-allocated and properly typed.
|
||||||
|
// - `pPrivateDriverData` and `PrivateDriverDataSize` are both correct for the
|
||||||
|
// KMTQAITYPE_WDDM_2_7_CAPS operation
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryAdapterInfo(&mut adapter_info as *mut D3DKMT_QUERYADAPTERINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
adapter_info.Type = KMTQAITYPE_GETSEGMENTGROUPSIZE;
|
||||||
|
adapter_info.pPrivateDriverData =
|
||||||
|
&mut self.segment_group_size as *mut D3DKMT_SEGMENTGROUPSIZEINFO as *mut c_void;
|
||||||
|
adapter_info.PrivateDriverDataSize =
|
||||||
|
std::mem::size_of::<D3DKMT_SEGMENTGROUPSIZEINFO>() as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `adapter_info` is stack-allocated and properly typed.
|
||||||
|
// - `pPrivateDriverData` and `PrivateDriverDataSize` are both correct for the
|
||||||
|
// KMTQAITYPE_GETSEGMENTGROUPSIZE operation
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryAdapterInfo(&mut adapter_info as *mut D3DKMT_QUERYADAPTERINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut registry_info: D3DKMT_ADAPTERREGISTRYINFO = Default::default();
|
||||||
|
adapter_info.Type = KMTQAITYPE_ADAPTERREGISTRYINFO_RENDER;
|
||||||
|
adapter_info.pPrivateDriverData =
|
||||||
|
&mut registry_info as *mut D3DKMT_ADAPTERREGISTRYINFO as *mut c_void;
|
||||||
|
adapter_info.PrivateDriverDataSize =
|
||||||
|
std::mem::size_of::<D3DKMT_ADAPTERREGISTRYINFO>() as u32;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `adapter_info` is stack-allocated and properly typed.
|
||||||
|
// - `pPrivateDriverData` and `PrivateDriverDataSize` are both correct for the
|
||||||
|
// KMTQAITYPE_ADAPTERREGISTERYINFO operation
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryAdapterInfo(&mut adapter_info as *mut D3DKMT_QUERYADAPTERINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `registry_info` has been successfully retrieved and contains well-formed UTF-16 data.
|
||||||
|
// - WCHAR/wchar_t are 16-bits on Windows.
|
||||||
|
let adapter_name_len = unsafe { wcslen(®istry_info.AdapterString[0] as *const u16) };
|
||||||
|
let chip_type_len = unsafe { wcslen(®istry_info.ChipType[0] as *const u16) };
|
||||||
|
let adapter_name_slice: &[u16] = unsafe {
|
||||||
|
from_raw_parts(
|
||||||
|
®istry_info.AdapterString[0] as *const _,
|
||||||
|
adapter_name_len,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let chip_type_slice: &[u16] =
|
||||||
|
unsafe { from_raw_parts(®istry_info.ChipType[0] as *const _, chip_type_len) };
|
||||||
|
|
||||||
|
self.adapter_name = String::from_utf16(adapter_name_slice)
|
||||||
|
.map_err(|_| MesaError::WithContext("invalid utf-16 data"))?;
|
||||||
|
self.chip_type = String::from_utf16(chip_type_slice)
|
||||||
|
.map_err(|_| MesaError::WithContext("invalid utf-16 data"))?;
|
||||||
|
|
||||||
|
let device_ids = query_device_ids.DeviceIds;
|
||||||
|
pci_info.revision_id = device_ids.RevisionID.try_into()?;
|
||||||
|
pci_info.vendor_id = device_ids.VendorID.try_into()?;
|
||||||
|
pci_info.device_id = device_ids.DeviceID.try_into()?;
|
||||||
|
pci_info.subvendor_id = device_ids.SubVendorID.try_into()?;
|
||||||
|
pci_info.subdevice_id = device_ids.SubSystemID.try_into()?;
|
||||||
|
|
||||||
|
pci_bus_info.domain = 0;
|
||||||
|
pci_bus_info.bus = adapter_address.BusNumber.try_into()?;
|
||||||
|
pci_bus_info.device = adapter_address.DeviceNumber.try_into()?;
|
||||||
|
pci_bus_info.function = adapter_address.FunctionNumber.try_into()?;
|
||||||
|
|
||||||
|
Ok((pci_info, pci_bus_info))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericPhysicalDevice for WddmAdapter {
|
||||||
|
fn create_device(
|
||||||
|
&self,
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: &MagmaPciInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Device>> {
|
||||||
|
let vendor_private_data = match pci_info.vendor_id {
|
||||||
|
MAGMA_VENDOR_ID_AMD => Box::new(Amd(())),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let device = WddmDevice::new(physical_device.clone(), vendor_private_data)?;
|
||||||
|
Ok(Arc::new(device))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsPhysicalDevice for WddmAdapter {
|
||||||
|
fn as_wddm_handle(&self) -> D3dkmtHandle {
|
||||||
|
self.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
fn segment_group_size(&self) -> D3DKMT_SEGMENTGROUPSIZEINFO {
|
||||||
|
self.segment_group_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsVirtGpu for WddmAdapter {}
|
||||||
|
impl PhysicalDevice for WddmAdapter {}
|
||||||
|
|
||||||
|
impl Drop for WddmAdapter {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut close = D3DKMT_CLOSEADAPTER {
|
||||||
|
hAdapter: self.handle,
|
||||||
|
};
|
||||||
|
// SAFETY: Safe since we own the adapter handle
|
||||||
|
log_ntstatus!(unsafe { D3DKMTCloseAdapter(&mut close as *mut D3DKMT_CLOSEADAPTER) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_adapters() -> MesaResult<Vec<(WddmAdapter, MagmaPciInfo, MagmaPciBusInfo)>> {
|
||||||
|
let mut enum_adapters = D3DKMT_ENUMADAPTERS2::default();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `enum_adapters` is stack-allocated and properly typed.
|
||||||
|
// - D3DKMTEnumAdapters2 does not modify any other memory.
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTEnumAdapters2(&mut enum_adapters as *mut D3DKMT_ENUMADAPTERS2)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// First call gets enum_adapters.NumAdapters, second call gets the actual data.
|
||||||
|
let mut adapter_slice = vec![D3DKMT_ADAPTERINFO::default(); enum_adapters.NumAdapters as usize];
|
||||||
|
enum_adapters.pAdapters = adapter_slice.as_mut_ptr();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `enum_adapters` is stack-allocated and properly typed.
|
||||||
|
// - D3DKMTEnumAdapters2 does not modify any other memory.
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTEnumAdapters2(&mut enum_adapters as *mut D3DKMT_ENUMADAPTERS2)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Should not return a larger value of NumAdapters than it returned on the first call.
|
||||||
|
assert!((enum_adapters.NumAdapters as usize) <= adapter_slice.len());
|
||||||
|
let mut adapters = Vec::with_capacity(enum_adapters.NumAdapters as usize);
|
||||||
|
|
||||||
|
for adapter in &mut adapter_slice[..(enum_adapters.NumAdapters as usize)] {
|
||||||
|
let mut adapter = WddmAdapter::new(adapter.hAdapter, adapter.AdapterLuid);
|
||||||
|
let (pci_info, pci_bus_info) = adapter.initialize()?;
|
||||||
|
adapters.push((adapter, pci_info, pci_bus_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(adapters)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WddmDevice {
|
||||||
|
pub fn new(
|
||||||
|
adapter: Arc<dyn PhysicalDevice>,
|
||||||
|
vendor_private_data: Box<dyn VendorPrivateData>,
|
||||||
|
) -> MesaResult<WddmDevice> {
|
||||||
|
let mut mem_props: MagmaMemoryProperties = Default::default();
|
||||||
|
|
||||||
|
let mut arg = D3DKMT_CREATEDEVICE {
|
||||||
|
Flags: Default::default(),
|
||||||
|
Anonymous: D3DKMT_CREATEDEVICE_0 {
|
||||||
|
hAdapter: adapter.as_wddm_handle(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Safe because mutable arg is allocated locally on the stack and we trust the D3DKMT API
|
||||||
|
// not to modify any other memory.
|
||||||
|
check_ntstatus!(unsafe { D3DKMTCreateDevice(&mut arg as *mut D3DKMT_CREATEDEVICE) })?;
|
||||||
|
|
||||||
|
let segment_group_size = adapter.segment_group_size();
|
||||||
|
if segment_group_size.NonLocalMemory > 0 {
|
||||||
|
mem_props.add_heap(segment_group_size.NonLocalMemory, 0);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT | MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||||
|
);
|
||||||
|
mem_props.add_memory_type(
|
||||||
|
MAGMA_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| MAGMA_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
||||||
|
);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if segment_group_size.LocalMemory > 0 {
|
||||||
|
mem_props.add_heap(segment_group_size.LocalMemory, MAGMA_HEAP_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.add_memory_type(MAGMA_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
mem_props.increment_heap_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(WddmDevice {
|
||||||
|
handle: arg.hDevice,
|
||||||
|
adapter,
|
||||||
|
vendor_private_data,
|
||||||
|
mem_props,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericDevice for WddmDevice {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties> {
|
||||||
|
Ok(self.mem_props.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, heap_idx: u32) -> MesaResult<MagmaHeapBudget> {
|
||||||
|
if heap_idx >= self.mem_props.memory_heap_count {
|
||||||
|
return Err(MesaError::WithContext("Heap Index out of bounds"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut segment_group = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
|
||||||
|
if self.mem_props.get_memory_heap(heap_idx).is_device_local() {
|
||||||
|
segment_group = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut arg = D3DKMT_QUERYVIDEOMEMORYINFO {
|
||||||
|
hProcess: std::ptr::null_mut::<c_void>(),
|
||||||
|
hAdapter: self.adapter.as_wddm_handle(),
|
||||||
|
MemorySegmentGroup: segment_group,
|
||||||
|
Budget: 0, // output
|
||||||
|
CurrentUsage: 0, // output
|
||||||
|
CurrentReservation: 0, // output
|
||||||
|
AvailableForReservation: 0, // output
|
||||||
|
PhysicalAdapterIndex: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTQueryVideoMemoryInfo(&mut arg as *mut D3DKMT_QUERYVIDEOMEMORYINFO)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(MagmaHeapBudget {
|
||||||
|
budget: arg.Budget,
|
||||||
|
usage: arg.CurrentUsage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context(&self, device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>> {
|
||||||
|
let ctx = WddmContext::new(device.clone())?;
|
||||||
|
Ok(Arc::new(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let buf = WddmBuffer::new(device.clone(), create_info, &self.mem_props)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
device: &Arc<dyn Device>,
|
||||||
|
info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>> {
|
||||||
|
let mut open_alloc_info: D3DDDI_OPENALLOCATIONINFO2 = Default::default();
|
||||||
|
|
||||||
|
let mut arg = D3DKMT_OPENRESOURCEFROMNTHANDLE {
|
||||||
|
hDevice: self.handle,
|
||||||
|
hNtHandle: info.handle.os_handle.into_raw_descriptor(),
|
||||||
|
NumAllocations: 1,
|
||||||
|
pOpenAllocationInfo2: &mut open_alloc_info as *mut _,
|
||||||
|
PrivateRuntimeDataSize: 0,
|
||||||
|
pPrivateRuntimeData: std::ptr::null_mut(),
|
||||||
|
hResource: 0, // output
|
||||||
|
KeyedMutexPrivateRuntimeDataSize: 0,
|
||||||
|
pKeyedMutexPrivateRuntimeData: std::ptr::null_mut(),
|
||||||
|
ResourcePrivateDriverDataSize: 0,
|
||||||
|
pResourcePrivateDriverData: std::ptr::null_mut(),
|
||||||
|
TotalPrivateDriverDataBufferSize: 0,
|
||||||
|
pTotalPrivateDriverDataBuffer: std::ptr::null_mut(),
|
||||||
|
hKeyedMutex: 0,
|
||||||
|
hSyncObject: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
check_ntstatus!(unsafe { D3DKMTOpenResourceFromNtHandle(&mut arg) })?;
|
||||||
|
|
||||||
|
let buf =
|
||||||
|
WddmBuffer::from_existing(device.clone(), open_alloc_info.hAllocation, info.size)?;
|
||||||
|
Ok(Arc::new(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WddmDevice {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let arg = D3DKMT_DESTROYDEVICE {
|
||||||
|
hDevice: self.handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Safe because const arg is allocated locally on the stack and we trust the D3DKMT API
|
||||||
|
// not to modify any other memory.
|
||||||
|
log_ntstatus!(unsafe { D3DKMTDestroyDevice(&arg as *const D3DKMT_DESTROYDEVICE) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsDevice for WddmDevice {
|
||||||
|
fn as_wddm_handle(&self) -> D3dkmtHandle {
|
||||||
|
self.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vendor_private_data(&self) -> Option<&dyn VendorPrivateData> {
|
||||||
|
Some(&*self.vendor_private_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for WddmDevice {}
|
||||||
|
|
||||||
|
impl WddmContext {
|
||||||
|
pub fn new(device: Arc<dyn Device>) -> MesaResult<WddmContext> {
|
||||||
|
// TODO: Fill in NodeOrdinal, EngineAffinity, pPrivateDriverData
|
||||||
|
let mut arg = D3DKMT_CREATECONTEXTVIRTUAL {
|
||||||
|
hDevice: device.as_wddm_handle(),
|
||||||
|
NodeOrdinal: Default::default(),
|
||||||
|
EngineAffinity: Default::default(),
|
||||||
|
Flags: D3DDDI_CREATECONTEXTFLAGS {
|
||||||
|
Anonymous: D3DDDI_CREATECONTEXTFLAGS_0 {
|
||||||
|
Value: Default::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pPrivateDriverData: std::ptr::null_mut::<c_void>(),
|
||||||
|
PrivateDriverDataSize: Default::default(),
|
||||||
|
ClientHint: D3DKMT_CLIENTHINT_VULKAN,
|
||||||
|
hContext: 0, // return value
|
||||||
|
};
|
||||||
|
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTCreateContextVirtual(&mut arg as *mut D3DKMT_CREATECONTEXTVIRTUAL)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(WddmContext {
|
||||||
|
handle: arg.hContext,
|
||||||
|
_device: device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WddmContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Safe because const arg is allocated locally on the stack and we trust the D3DKMT API
|
||||||
|
// not to modify any other memory.
|
||||||
|
log_ntstatus!(unsafe {
|
||||||
|
D3DKMTDestroyContext(&D3DKMT_DESTROYCONTEXT {
|
||||||
|
hContext: self.handle,
|
||||||
|
} as *const D3DKMT_DESTROYCONTEXT)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for WddmContext {}
|
||||||
|
|
||||||
|
impl WddmBuffer {
|
||||||
|
pub fn new(
|
||||||
|
device: Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
mem_props: &MagmaMemoryProperties,
|
||||||
|
) -> MesaResult<WddmBuffer> {
|
||||||
|
let vendor_private_data = device.vendor_private_data().unwrap();
|
||||||
|
|
||||||
|
let flags: D3DKMT_CREATEALLOCATIONFLAGS = Default::default();
|
||||||
|
|
||||||
|
// flags.set_NonSecure(1);
|
||||||
|
// flags.set_CreateWriteCombined(1);
|
||||||
|
|
||||||
|
// type annotations important for following calculation
|
||||||
|
let mut create_allocation: Vec<u32> = vendor_private_data.createallocation_pdata();
|
||||||
|
let mut allocationinfo2: Vec<u32> =
|
||||||
|
vendor_private_data.allocationinfo2_pdata(create_info, mem_props);
|
||||||
|
|
||||||
|
let size_create_allocation: usize = create_allocation.len() * std::mem::size_of::<u32>();
|
||||||
|
let size_allocationinfo2: usize = allocationinfo2.len() * std::mem::size_of::<u32>();
|
||||||
|
|
||||||
|
let mut alloc_info: D3DDDI_ALLOCATIONINFO2 = D3DDDI_ALLOCATIONINFO2 {
|
||||||
|
pPrivateDriverData: allocationinfo2.as_mut_ptr() as *mut c_void,
|
||||||
|
PrivateDriverDataSize: size_allocationinfo2.try_into()?,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut arg = D3DKMT_CREATEALLOCATION {
|
||||||
|
hDevice: device.as_wddm_handle(),
|
||||||
|
hResource: Default::default(),
|
||||||
|
hGlobalShare: 0,
|
||||||
|
pPrivateRuntimeData: std::ptr::null_mut::<c_void>(),
|
||||||
|
PrivateRuntimeDataSize: 0,
|
||||||
|
PrivateDriverDataSize: size_create_allocation.try_into()?,
|
||||||
|
NumAllocations: 1,
|
||||||
|
Anonymous1: D3DKMT_CREATEALLOCATION_0 {
|
||||||
|
pPrivateDriverData: create_allocation.as_mut_ptr() as *mut c_void,
|
||||||
|
},
|
||||||
|
Anonymous2: D3DKMT_CREATEALLOCATION_1 {
|
||||||
|
pAllocationInfo2: &mut alloc_info as *mut D3DDDI_ALLOCATIONINFO2,
|
||||||
|
},
|
||||||
|
Flags: flags,
|
||||||
|
hPrivateRuntimeResourceHandle: std::ptr::null_mut::<c_void>(), // output of D3DKMTCreateAllocation
|
||||||
|
};
|
||||||
|
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTCreateAllocation2(&mut arg as *mut D3DKMT_CREATEALLOCATION)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(WddmBuffer {
|
||||||
|
handle: alloc_info.hAllocation,
|
||||||
|
device,
|
||||||
|
size: create_info.size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn from_existing(
|
||||||
|
device: Arc<dyn Device>,
|
||||||
|
handle: D3dkmtHandle,
|
||||||
|
size: u64,
|
||||||
|
) -> MesaResult<WddmBuffer> {
|
||||||
|
Ok(WddmBuffer {
|
||||||
|
handle,
|
||||||
|
device,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for WddmMapping {}
|
||||||
|
unsafe impl Sync for WddmMapping {}
|
||||||
|
|
||||||
|
unsafe impl MappedRegion for WddmMapping {
|
||||||
|
fn as_ptr(&self) -> *mut u8 {
|
||||||
|
self.pdata as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_mesa_mapping(&self) -> MesaMapping {
|
||||||
|
MesaMapping {
|
||||||
|
ptr: self.pdata as u64,
|
||||||
|
size: self.size as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericBuffer for WddmBuffer {
|
||||||
|
fn map(&self, buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>> {
|
||||||
|
let mut arg = D3DKMT_LOCK2 {
|
||||||
|
hDevice: self.device.as_wddm_handle(),
|
||||||
|
hAllocation: self.handle,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
check_ntstatus!(unsafe { D3DKMTLock2(&mut arg as *mut D3DKMT_LOCK2) })?;
|
||||||
|
|
||||||
|
Ok(Arc::new(WddmMapping {
|
||||||
|
_buffer: buffer.clone(),
|
||||||
|
pdata: arg.pData,
|
||||||
|
size: self.size.try_into()?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle> {
|
||||||
|
Err(MesaError::Unsupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate(&self, sync_flags: u64, ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
let mut arg = D3DKMT_INVALIDATECACHE {
|
||||||
|
hDevice: self.device.as_wddm_handle(),
|
||||||
|
hAllocation: self.handle,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sync_flags & MAGMA_SYNC_WHOLE_RANGE) != 0 {
|
||||||
|
arg.Offset = 0;
|
||||||
|
arg.Length = self.size.try_into()?;
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTInvalidateCache(&mut arg as *mut D3DKMT_INVALIDATECACHE)
|
||||||
|
})?;
|
||||||
|
} else if (sync_flags & MAGMA_SYNC_RANGES) != 0 {
|
||||||
|
for r in ranges {
|
||||||
|
arg.Offset = r.offset.try_into()?;
|
||||||
|
arg.Length = r.size.try_into()?;
|
||||||
|
check_ntstatus!(unsafe {
|
||||||
|
D3DKMTInvalidateCache(&mut arg as *mut D3DKMT_INVALIDATECACHE)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self, _sync_flags: u64, _ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WddmBuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Safe because const arg is allocated locally on the stack and we trust the D3DKMT API
|
||||||
|
// not to modify any other memory.
|
||||||
|
let arg = D3DKMT_DESTROYALLOCATION2 {
|
||||||
|
hDevice: self.device.as_wddm_handle(),
|
||||||
|
hResource: Default::default(),
|
||||||
|
phAllocationList: &self.handle as *const D3dkmtHandle,
|
||||||
|
AllocationCount: 1,
|
||||||
|
Flags: D3DDDICB_DESTROYALLOCATION2FLAGS {
|
||||||
|
Anonymous: D3DDDICB_DESTROYALLOCATION2FLAGS_0 {
|
||||||
|
Value: Default::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
log_ntstatus!(unsafe { D3DKMTDestroyAllocation2(&arg as *const D3DKMT_DESTROYALLOCATION2) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer for WddmBuffer {}
|
||||||
|
|
||||||
|
unsafe impl Send for WddmDevice {}
|
||||||
|
unsafe impl Sync for WddmDevice {}
|
||||||
|
|
||||||
|
unsafe impl Send for WddmContext {}
|
||||||
|
unsafe impl Sync for WddmContext {}
|
||||||
|
|
||||||
|
unsafe impl Send for WddmBuffer {}
|
||||||
|
unsafe impl Sync for WddmBuffer {}
|
||||||
25
src/magma/sys/windows/macros.rs
Normal file
25
src/magma/sys/windows/macros.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! check_ntstatus {
|
||||||
|
($x: expr) => {{
|
||||||
|
match $x {
|
||||||
|
windows_sys::Win32::Foundation::STATUS_SUCCESS => Ok(()),
|
||||||
|
e => {
|
||||||
|
let error = rustix::io::Errno::from_raw_os_error(e);
|
||||||
|
Err(MesaError::RustixError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_ntstatus {
|
||||||
|
($x: expr) => {{
|
||||||
|
match $x {
|
||||||
|
windows_sys::Win32::Foundation::STATUS_SUCCESS => (),
|
||||||
|
e => error!("logging error status: {:#X}", e),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
13
src/magma/sys/windows/mod.rs
Normal file
13
src/magma/sys/windows/mod.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
mod amd;
|
||||||
|
mod d3dkmt_common;
|
||||||
|
mod macros;
|
||||||
|
mod wddm;
|
||||||
|
|
||||||
|
pub use amd::Amd;
|
||||||
|
pub use d3dkmt_common::WindowsDevice as PlatformDevice;
|
||||||
|
pub use d3dkmt_common::WindowsPhysicalDevice as PlatformPhysicalDevice;
|
||||||
|
pub use wddm::enumerate_devices;
|
||||||
|
pub use wddm::VendorPrivateData;
|
||||||
39
src/magma/sys/windows/wddm.rs
Normal file
39
src/magma/sys/windows/wddm.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::magma::MagmaPhysicalDevice;
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::sys::windows::d3dkmt_common;
|
||||||
|
|
||||||
|
pub trait VendorPrivateData {
|
||||||
|
fn createallocation_pdata(&self) -> Vec<u32> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocationinfo2_pdata(
|
||||||
|
&self,
|
||||||
|
_create_info: &MagmaCreateBufferInfo,
|
||||||
|
_mem_props: &MagmaMemoryProperties,
|
||||||
|
) -> Vec<u32> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_devices() -> MesaResult<Vec<MagmaPhysicalDevice>> {
|
||||||
|
let mut devices: Vec<MagmaPhysicalDevice> = Vec::new();
|
||||||
|
let adapters = d3dkmt_common::enumerate_adapters()?;
|
||||||
|
|
||||||
|
for (adapter, pci_info, pci_bus_info) in adapters {
|
||||||
|
devices.push(MagmaPhysicalDevice::new(
|
||||||
|
Arc::new(adapter),
|
||||||
|
pci_info,
|
||||||
|
pci_bus_info,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
67
src/magma/traits.rs
Normal file
67
src/magma/traits.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2025 Android Open Source Project
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mesa3d_util::MappedRegion;
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use mesa3d_util::MesaResult;
|
||||||
|
use virtgpu_kumquat::VirtGpuKumquat;
|
||||||
|
|
||||||
|
use crate::magma_defines::MagmaCreateBufferInfo;
|
||||||
|
use crate::magma_defines::MagmaHeapBudget;
|
||||||
|
use crate::magma_defines::MagmaImportHandleInfo;
|
||||||
|
use crate::magma_defines::MagmaMappedMemoryRange;
|
||||||
|
use crate::magma_defines::MagmaMemoryProperties;
|
||||||
|
use crate::magma_defines::MagmaPciInfo;
|
||||||
|
use crate::sys::platform::PlatformDevice;
|
||||||
|
use crate::sys::platform::PlatformPhysicalDevice;
|
||||||
|
|
||||||
|
pub trait AsVirtGpu {
|
||||||
|
fn as_virtgpu(&self) -> Option<&VirtGpuKumquat> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GenericPhysicalDevice {
|
||||||
|
fn create_device(
|
||||||
|
&self,
|
||||||
|
physical_device: &Arc<dyn PhysicalDevice>,
|
||||||
|
pci_info: &MagmaPciInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Device>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GenericDevice {
|
||||||
|
fn get_memory_properties(&self) -> MesaResult<MagmaMemoryProperties>;
|
||||||
|
|
||||||
|
fn get_memory_budget(&self, _heap_idx: u32) -> MesaResult<MagmaHeapBudget>;
|
||||||
|
|
||||||
|
fn create_context(&self, device: &Arc<dyn Device>) -> MesaResult<Arc<dyn Context>>;
|
||||||
|
|
||||||
|
fn create_buffer(
|
||||||
|
&self,
|
||||||
|
device: &Arc<dyn Device>,
|
||||||
|
create_info: &MagmaCreateBufferInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>>;
|
||||||
|
|
||||||
|
fn import(
|
||||||
|
&self,
|
||||||
|
_device: &Arc<dyn Device>,
|
||||||
|
_info: MagmaImportHandleInfo,
|
||||||
|
) -> MesaResult<Arc<dyn Buffer>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GenericBuffer {
|
||||||
|
fn map(&self, buffer: &Arc<dyn Buffer>) -> MesaResult<Arc<dyn MappedRegion>>;
|
||||||
|
|
||||||
|
fn export(&self) -> MesaResult<MesaHandle>;
|
||||||
|
|
||||||
|
fn invalidate(&self, sync_flags: u64, ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()>;
|
||||||
|
|
||||||
|
fn flush(&self, sync_flags: u64, ranges: &[MagmaMappedMemoryRange]) -> MesaResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PhysicalDevice: PlatformPhysicalDevice + AsVirtGpu + GenericPhysicalDevice {}
|
||||||
|
pub trait Device: GenericDevice + PlatformDevice {}
|
||||||
|
pub trait Context {}
|
||||||
|
pub trait Buffer: GenericBuffer {}
|
||||||
|
|
@ -111,6 +111,13 @@ if with_gfxstream_vk
|
||||||
endif
|
endif
|
||||||
subdir('gfxstream')
|
subdir('gfxstream')
|
||||||
endif
|
endif
|
||||||
|
if with_magma
|
||||||
|
subdir('util/rust')
|
||||||
|
subdir('virtio/protocols')
|
||||||
|
subdir('virtio/virtgpu_kumquat')
|
||||||
|
subdir('magma')
|
||||||
|
subdir('magma/ffi')
|
||||||
|
endif
|
||||||
if with_gallium_asahi or with_asahi_vk or with_tools.contains('asahi')
|
if with_gallium_asahi or with_asahi_vk or with_tools.contains('asahi')
|
||||||
subdir('asahi')
|
subdir('asahi')
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use std::ffi::NulError;
|
use std::ffi::NulError;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::num::TryFromIntError;
|
use std::num::TryFromIntError;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
|
|
@ -23,6 +24,9 @@ pub enum MesaError {
|
||||||
/// Nul crate error.
|
/// Nul crate error.
|
||||||
#[error("Nul Error occurred {0}")]
|
#[error("Nul Error occurred {0}")]
|
||||||
NulError(NulError),
|
NulError(NulError),
|
||||||
|
/// An attempted integer parsing failed.
|
||||||
|
#[error("int parsing failed: {0}")]
|
||||||
|
ParseIntError(ParseIntError),
|
||||||
/// Rustix crate error.
|
/// Rustix crate error.
|
||||||
#[error("The errno is {0}")]
|
#[error("The errno is {0}")]
|
||||||
RustixError(RustixError),
|
RustixError(RustixError),
|
||||||
|
|
@ -58,6 +62,12 @@ impl From<IoError> for MesaError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ParseIntError> for MesaError {
|
||||||
|
fn from(e: ParseIntError) -> MesaError {
|
||||||
|
MesaError::ParseIntError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<TryFromIntError> for MesaError {
|
impl From<TryFromIntError> for MesaError {
|
||||||
fn from(e: TryFromIntError) -> MesaError {
|
fn from(e: TryFromIntError) -> MesaError {
|
||||||
MesaError::TryFromIntError(e)
|
MesaError::TryFromIntError(e)
|
||||||
|
|
|
||||||
213
src/virtio/protocols/protocols/magma_virtio_protocol.rs
Normal file
213
src/virtio/protocols/protocols/magma_virtio_protocol.rs
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright 2025 Google
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use mesa3d_util::MesaHandle;
|
||||||
|
use zerocopy::FromBytes;
|
||||||
|
use zerocopy::Immutable;
|
||||||
|
use zerocopy::IntoBytes;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DeviceId {
|
||||||
|
pub device_uuid: [u8; 16],
|
||||||
|
pub driver_uuid: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Memory index and physical device id of the associated VkDeviceMemory.
|
||||||
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Hash,
|
||||||
|
FromBytes,
|
||||||
|
IntoBytes,
|
||||||
|
Immutable,
|
||||||
|
)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct VulkanInfo {
|
||||||
|
pub memory_idx: u32,
|
||||||
|
pub device_id: DeviceId,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAGMA_VIRTIO_GET_CAPABILITIES: u32 = 0x100;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct magma_virtio_ctrl_hdr {
|
||||||
|
pub type_: u32,
|
||||||
|
pub payload: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D, KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_transfer_host_3d {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub box_: kumquat_gpu_protocol_box,
|
||||||
|
pub offset: u64,
|
||||||
|
pub level: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub layer_stride: u32,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_resource_create_3d {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub target: u32,
|
||||||
|
pub format: u32,
|
||||||
|
pub bind: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub depth: u32,
|
||||||
|
pub array_size: u32,
|
||||||
|
pub last_level: u32,
|
||||||
|
pub nr_samples: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub size: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Copy, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_ctx_create {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub nlen: u32,
|
||||||
|
pub context_init: u32,
|
||||||
|
pub debug_name: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for kumquat_gpu_protocol_ctx_create {
|
||||||
|
fn default() -> Self {
|
||||||
|
// SAFETY: All zero pattern is safe for this particular struct
|
||||||
|
unsafe { ::std::mem::zeroed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE, KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_ctx_resource {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
pub resource_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_SUBMIT_3D */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_cmd_submit {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
pub pad: u32,
|
||||||
|
pub size: u32,
|
||||||
|
|
||||||
|
// The in-fence IDs are prepended to the cmd_buf and memory layout
|
||||||
|
// of the KUMQUAT_GPU_PROTOCOL_SUBMIT_3D buffer looks like this:
|
||||||
|
// _________________
|
||||||
|
// | CMD_SUBMIT_3D |
|
||||||
|
// -----------------
|
||||||
|
// | header |
|
||||||
|
// | in-fence IDs |
|
||||||
|
// | cmd_buf |
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// This makes in-fence IDs naturally aligned to the sizeof(u64) inside
|
||||||
|
// of the virtio buffer.
|
||||||
|
pub num_in_fences: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub ring_idx: u8,
|
||||||
|
pub padding: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_resp_capset_info {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub capset_id: u32,
|
||||||
|
pub version: u32,
|
||||||
|
pub size: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KUMQUAT_GPU_PROTOCOL_GET_CAPSET */
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_get_capset {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub capset_id: u32,
|
||||||
|
pub capset_version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_resource_create_blob {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
pub blob_mem: u32,
|
||||||
|
pub blob_flags: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
pub blob_id: u64,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_resp_resource_create {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub handle_type: u32,
|
||||||
|
pub vulkan_info: VulkanInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, FromBytes, IntoBytes, Immutable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct kumquat_gpu_protocol_resp_cmd_submit_3d {
|
||||||
|
pub hdr: kumquat_gpu_protocol_ctrl_hdr,
|
||||||
|
pub fence_id: u64,
|
||||||
|
pub handle_type: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A virtio gpu command and associated metadata specific to each command.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum KumquatGpuProtocol {
|
||||||
|
OkNoData,
|
||||||
|
GetNumCapsets,
|
||||||
|
GetCapsetInfo(u32),
|
||||||
|
GetCapset(kumquat_gpu_protocol_get_capset),
|
||||||
|
CtxCreate(kumquat_gpu_protocol_ctx_create),
|
||||||
|
CtxDestroy(u32),
|
||||||
|
CtxAttachResource(kumquat_gpu_protocol_ctx_resource),
|
||||||
|
CtxDetachResource(kumquat_gpu_protocol_ctx_resource),
|
||||||
|
ResourceCreate3d(kumquat_gpu_protocol_resource_create_3d),
|
||||||
|
TransferToHost3d(kumquat_gpu_protocol_transfer_host_3d, MesaHandle),
|
||||||
|
TransferFromHost3d(kumquat_gpu_protocol_transfer_host_3d, MesaHandle),
|
||||||
|
CmdSubmit3d(kumquat_gpu_protocol_cmd_submit, Vec<u8>, Vec<u64>),
|
||||||
|
ResourceCreateBlob(kumquat_gpu_protocol_resource_create_blob),
|
||||||
|
SnapshotSave,
|
||||||
|
SnapshotRestore,
|
||||||
|
RespNumCapsets(u32),
|
||||||
|
RespCapsetInfo(kumquat_gpu_protocol_resp_capset_info),
|
||||||
|
RespCapset(Vec<u8>),
|
||||||
|
RespContextCreate(u32),
|
||||||
|
RespResourceCreate(kumquat_gpu_protocol_resp_resource_create, MesaHandle),
|
||||||
|
RespCmdSubmit3d(u64, MesaHandle),
|
||||||
|
RespOkSnapshot,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum KumquatGpuProtocolWrite<T: IntoBytes + FromBytes + Immutable> {
|
||||||
|
Cmd(T),
|
||||||
|
CmdWithHandle(T, MesaHandle),
|
||||||
|
CmdWithData(T, Vec<u8>),
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,9 @@ project(
|
||||||
|
|
||||||
windows_link = subproject('windows-link-0.2-rs').get_variable('lib')
|
windows_link = subproject('windows-link-0.2-rs').get_variable('lib')
|
||||||
windows_sys_args = [
|
windows_sys_args = [
|
||||||
|
'--cfg', 'feature="Wdk"',
|
||||||
|
'--cfg', 'feature="Wdk_Graphics"',
|
||||||
|
'--cfg', 'feature="Wdk_Graphics_Direct3D"',
|
||||||
'--cfg', 'feature="Win32"',
|
'--cfg', 'feature="Win32"',
|
||||||
'--cfg', 'feature="Win32_Networking"',
|
'--cfg', 'feature="Win32_Networking"',
|
||||||
'--cfg', 'feature="Win32_Networking_WinSock"',
|
'--cfg', 'feature="Win32_Networking_WinSock"',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue