Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/amd/amdgpu.c
- This file was added.
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/errno.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/param.h> | |||||
#include <sys/bitstring.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/ktr.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/mman.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/rangeset.h> | |||||
#include <sys/rwlock.h> | |||||
#include <sys/sbuf.h> | |||||
#include <sys/sx.h> | |||||
#include <sys/turnstile.h> | |||||
#include <sys/vmem.h> | |||||
#include <sys/vmmeter.h> | |||||
#include <sys/sched.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/smp.h> | |||||
#include <vm/vm.h> | |||||
#include <vm/vm_param.h> | |||||
#include <vm/vm_kern.h> | |||||
#include <vm/vm_page.h> | |||||
#include <vm/vm_map.h> | |||||
#include <vm/vm_object.h> | |||||
#include <vm/vm_extern.h> | |||||
#include <vm/vm_pageout.h> | |||||
#include <vm/vm_pager.h> | |||||
#include <vm/vm_phys.h> | |||||
#include <vm/vm_radix.h> | |||||
#include <vm/vm_reserv.h> | |||||
#include <vm/uma.h> | |||||
#include <machine/pmap.h> | |||||
#include <machine/vmm.h> | |||||
#include <machine/vmparam.h> | |||||
#include "amd/amdgpu.h" | |||||
#include "contrib/dev/acpica/include/acpi.h" | |||||
#include "contrib/dev/acpica/include/acpixf.h" | |||||
#define GFP_NATIVE_MASK (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_ZERO) | |||||
#define GFP_KERNEL M_WAITOK | |||||
//MALLOC_DECLARE(M_KMALLOC); | |||||
MALLOC_DECLARE(M_VMMDEV); | |||||
typedef unsigned gfp_t; | |||||
static inline gfp_t | |||||
linux_check_m_flags(gfp_t flags) | |||||
{ | |||||
const gfp_t m = M_NOWAIT | M_WAITOK; | |||||
/* make sure either M_NOWAIT or M_WAITOK is set */ | |||||
if ((flags & m) == 0) | |||||
flags |= M_NOWAIT; | |||||
else if ((flags & m) == m) | |||||
flags &= ~M_WAITOK; | |||||
/* mask away LinuxKPI specific flags */ | |||||
return (flags & GFP_NATIVE_MASK); | |||||
} | |||||
static inline void * | |||||
kmalloc(size_t size, gfp_t flags) | |||||
{ | |||||
return (malloc(size, M_VMMDEV, linux_check_m_flags(flags))); | |||||
} | |||||
static inline void | |||||
kfree(const void *ptr) | |||||
{ | |||||
free(__DECONST(void *, ptr), M_VMMDEV); | |||||
} | |||||
static inline void * | |||||
kmemdup(const void *src, size_t len, gfp_t gfp) | |||||
{ | |||||
void *dst; | |||||
dst = kmalloc(len, gfp); | |||||
if (dst != NULL) { | |||||
memcpy(dst, src, len); | |||||
} | |||||
return (dst); | |||||
} | |||||
#define AMD_VBIOS_SIGNATURE " 761295520" | |||||
#define AMD_VBIOS_SIGNATURE_OFFSET 0x30 | |||||
#define AMD_VBIOS_SIGNATURE_SIZE sizeof(AMD_VBIOS_SIGNATURE) | |||||
#define AMD_VBIOS_SIGNATURE_END (AMD_VBIOS_SIGNATURE_OFFSET + AMD_VBIOS_SIGNATURE_SIZE) | |||||
#define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA) | |||||
#define AMD_VBIOS_LENGTH(p) ((p)[2] << 9) | |||||
#define acpi_get_table AcpiGetTable | |||||
typedef struct { | |||||
uint32_t Signature; | |||||
uint32_t TableLength; //Length | |||||
uint8_t Revision; | |||||
uint8_t Checksum; | |||||
uint8_t OemId[6]; | |||||
uint8_t OemTableId[8]; //UINT64 OemTableId; | |||||
uint32_t OemRevision; | |||||
uint32_t CreatorId; | |||||
uint32_t CreatorRevision; | |||||
} AMD_ACPI_DESCRIPTION_HEADER; | |||||
typedef struct { | |||||
AMD_ACPI_DESCRIPTION_HEADER SHeader; | |||||
uint8_t TableUUID[16]; //0x24 | |||||
uint32_t VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. | |||||
uint32_t Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. | |||||
uint32_t Reserved[4]; //0x3C | |||||
}UEFI_ACPI_VFCT; | |||||
typedef struct { | |||||
uint32_t PCIBus; //0x4C | |||||
uint32_t PCIDevice; //0x50 | |||||
uint32_t PCIFunction; //0x54 | |||||
uint16_t VendorID; //0x58 | |||||
uint16_t DeviceID; //0x5A | |||||
uint16_t SSVID; //0x5C | |||||
uint16_t SSID; //0x5E | |||||
uint32_t Revision; //0x60 | |||||
uint32_t ImageLength; //0x64 | |||||
}VFCT_IMAGE_HEADER; | |||||
typedef struct { | |||||
VFCT_IMAGE_HEADER VbiosHeader; | |||||
uint8_t VbiosContent[1]; | |||||
}GOP_VBIOS_CONTENT; | |||||
/* Check if current bios is an ATOM BIOS. | |||||
* Return true if it is ATOM BIOS. Otherwise, return false. | |||||
*/ | |||||
static bool check_atom_bios(uint8_t *bios, size_t size) | |||||
{ | |||||
uint16_t tmp, bios_header_start; | |||||
if (!bios || size < 0x49) { | |||||
// vbios mem is null or mem size is wrong | |||||
return false; | |||||
} | |||||
if (!AMD_IS_VALID_VBIOS(bios)) { | |||||
// BIOS signature incorrect | |||||
return false; | |||||
} | |||||
bios_header_start = bios[0x48] | (bios[0x49] << 8); | |||||
if (!bios_header_start) { | |||||
// Can't locate bios header | |||||
return false; | |||||
} | |||||
tmp = bios_header_start + 4; | |||||
if (size < tmp) { | |||||
// BIOS header is broken | |||||
return false; | |||||
} | |||||
if (!memcmp(bios + tmp, "ATOM", 4) || | |||||
!memcmp(bios + tmp, "MOTA", 4)) { | |||||
// ATOMBIOS detected | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
static int | |||||
amdgpu_get_vbios_vfct(struct vm *vm, int bus, int slot, int func, | |||||
uint16_t vendor, uint16_t dev_id, void **bios, uint64_t *size) | |||||
{ | |||||
if (vm == NULL || bios == NULL || size == NULL) | |||||
return EINVAL; | |||||
struct acpi_table_header *hdr; | |||||
uint32_t tbl_size; | |||||
UEFI_ACPI_VFCT *vfct; | |||||
unsigned offset; | |||||
if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) | |||||
return ENOENT; | |||||
tbl_size = hdr->Length; | |||||
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) | |||||
return ENODEV; | |||||
vfct = (UEFI_ACPI_VFCT *)hdr; | |||||
offset = vfct->VBIOSImageOffset; | |||||
while (offset < tbl_size) { | |||||
GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset); | |||||
VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader; | |||||
offset += sizeof(VFCT_IMAGE_HEADER); | |||||
if (offset > tbl_size) | |||||
return ENODEV; | |||||
offset += vhdr->ImageLength; | |||||
if (offset > tbl_size) | |||||
return ENODEV; | |||||
if (vhdr->ImageLength && | |||||
vhdr->PCIBus == bus && | |||||
vhdr->PCIDevice == slot && | |||||
vhdr->PCIFunction == func && | |||||
vhdr->VendorID == vendor && | |||||
vhdr->DeviceID == dev_id) { | |||||
*bios = kmemdup(&vbios->VbiosContent, | |||||
vhdr->ImageLength, | |||||
GFP_KERNEL); | |||||
if (!check_atom_bios((uint8_t *)*bios, vhdr->ImageLength)) { | |||||
kfree(*bios); | |||||
return ENODEV; | |||||
} | |||||
*size = vhdr->ImageLength; | |||||
return 0; | |||||
} | |||||
} | |||||
return ENOENT; | |||||
} | |||||
int | |||||
vm_amdgpu_get_vbios(struct vm *vm, int bus, int slot, int func, | |||||
uint16_t vendor, uint16_t dev_id, void *bios, uint64_t *size) | |||||
{ | |||||
int error; | |||||
void *bios_base; | |||||
uint64_t bios_size; | |||||
error = 0; | |||||
if (amdgpu_get_vbios_vfct(vm, bus, slot, func, vendor, dev_id, &bios_base, &bios_size) == 0) | |||||
goto done; | |||||
return ENOENT; | |||||
done: | |||||
if (bios) { | |||||
*size = min(bios_size, *size); | |||||
error = copyout(bios_base, bios, *size); | |||||
} else { | |||||
*size = bios_size; | |||||
} | |||||
kfree(bios_base); | |||||
return (error); | |||||
} |