Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/amd/amdgpu_bios.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
/* | |||||
* This is a copy of <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c> | |||||
*/ | |||||
#define __FBSD__ | |||||
#ifdef __FBSD__ | |||||
/* build defines */ | |||||
#define CONFIG_ACPI | |||||
/* includes */ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/module.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/malloc.h> | |||||
#include <dev/pci/pcivar.h> | |||||
#include "amdgpu_bios.h" | |||||
#include "contrib/dev/acpica/include/acpi.h" | |||||
#include "contrib/dev/acpica/include/acpixf.h" | |||||
/* type definitons */ | |||||
#define __iomem | |||||
#define true 1 | |||||
#define false 0 | |||||
typedef uint8_t UCHAR; | |||||
typedef uint16_t USHORT; | |||||
typedef uint32_t ULONG; | |||||
typedef uint32_t acpi_size; | |||||
/* log definitions */ | |||||
#define DRM_DEBUG uprintf | |||||
#define DRM_INFO uprintf | |||||
#define DRM_ERROR uprintf | |||||
#define GFP_NATIVE_MASK (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_ZERO) | |||||
#define GFP_KERNEL M_WAITOK | |||||
#define __GFP_ZERO M_ZERO | |||||
#define kzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO) | |||||
#define memcpy_fromio(a, b, c) memcpy((a), (b), (c)) | |||||
#define acpi_get_table AcpiGetTable | |||||
#define PCI_DEVFN(bus, slot, func) ((((bus) & 0xff) << 8) | (((slot) & 0x1f) << 3) | ((func) & 0x07)) | |||||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) | |||||
#define PCI_FUNC(devfn) ((devfn) & 0x07) | |||||
#define PCI_BUS_NUM(devfn) (((devfn) >> 8) & 0xff) | |||||
//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); | |||||
} | |||||
/* structs */ | |||||
/* device copied from sys/compat/linuxkpi/common/include/linux/pci.h */ | |||||
struct device { | |||||
#ifndef __FBSD__ | |||||
struct device *parent; | |||||
struct list_head irqents; | |||||
#endif /*__FBSD__*/ | |||||
device_t bsddev; | |||||
#ifndef __FBSD__ | |||||
/* | |||||
* The following flag is used to determine if the LinuxKPI is | |||||
* responsible for detaching the BSD device or not. If the | |||||
* LinuxKPI got the BSD device using devclass_get_device(), it | |||||
* must not try to detach or delete it, because it's already | |||||
* done somewhere else. | |||||
*/ | |||||
bool bsddev_attached_here; | |||||
struct device_driver *driver; | |||||
struct device_type *type; | |||||
dev_t devt; | |||||
struct class *class; | |||||
void (*release)(struct device *dev); | |||||
struct kobject kobj; | |||||
uint64_t *dma_mask; | |||||
void *driver_data; | |||||
unsigned int irq; | |||||
#define LINUX_IRQ_INVALID 65535 | |||||
unsigned int irq_start; | |||||
unsigned int irq_end; | |||||
const struct attribute_group **groups; | |||||
struct fwnode_handle *fwnode; | |||||
spinlock_t devres_lock; | |||||
struct list_head devres_head; | |||||
#endif /*__FBSD__*/ | |||||
}; | |||||
/* pci_dev copied from sys/compat/linuxkpi/common/include/linux/pci.h */ | |||||
struct pci_dev { | |||||
struct device dev; | |||||
#ifndef __FBSD__ | |||||
struct list_head links; | |||||
struct pci_driver *pdrv; | |||||
struct pci_bus *bus; | |||||
uint64_t dma_mask; | |||||
#endif | |||||
uint16_t device; | |||||
uint16_t vendor; | |||||
#ifndef __FBSD__ | |||||
uint16_t subsystem_vendor; | |||||
uint16_t subsystem_device; | |||||
unsigned int irq; | |||||
#endif | |||||
unsigned int devfn; | |||||
#ifndef __FBSD__ | |||||
uint32_t class; | |||||
uint8_t revision; | |||||
bool msi_enabled; | |||||
#endif | |||||
}; | |||||
/* amdgpu_device copied from <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/amdgpu/amdgpu.h>*/ | |||||
struct amdgpu_device { | |||||
#ifndef __FBSD__ | |||||
struct device *dev; | |||||
struct drm_device *ddev; | |||||
#endif /*__FBSD__*/ | |||||
struct pci_dev *pdev; | |||||
#ifndef __FBSD__ | |||||
#ifdef CONFIG_DRM_AMD_ACP | |||||
struct amdgpu_acp acp; | |||||
#endif | |||||
/* ASIC */ | |||||
enum amd_asic_type asic_type; | |||||
uint32_t family; | |||||
uint32_t rev_id; | |||||
uint32_t external_rev_id; | |||||
unsigned long flags; | |||||
int usec_timeout; | |||||
const struct amdgpu_asic_funcs *asic_funcs; | |||||
bool shutdown; | |||||
bool need_swiotlb; | |||||
bool accel_working; | |||||
struct notifier_block acpi_nb; | |||||
struct amdgpu_i2c_chan *i2c_bus[AMDGPU_MAX_I2C_BUS]; | |||||
struct amdgpu_debugfs debugfs[AMDGPU_DEBUGFS_MAX_COMPONENTS]; | |||||
unsigned debugfs_count; | |||||
#if defined(CONFIG_DEBUG_FS) | |||||
struct dentry *debugfs_preempt; | |||||
struct dentry *debugfs_regs[AMDGPU_DEBUGFS_MAX_COMPONENTS]; | |||||
#endif | |||||
struct amdgpu_atif *atif; | |||||
struct amdgpu_atcs atcs; | |||||
struct mutex srbm_mutex; | |||||
/* GRBM index mutex. Protects concurrent access to GRBM index */ | |||||
struct mutex grbm_idx_mutex; | |||||
struct dev_pm_domain vga_pm_domain; | |||||
bool have_disp_power_ref; | |||||
bool have_atomics_support; | |||||
/* BIOS */ | |||||
bool is_atom_fw; | |||||
#endif /*__FBSD__*/ | |||||
uint8_t *bios; | |||||
uint32_t bios_size; | |||||
#ifndef __FBSD__ | |||||
struct amdgpu_bo *stolen_vga_memory; | |||||
uint32_t bios_scratch_reg_offset; | |||||
uint32_t bios_scratch[AMDGPU_BIOS_NUM_SCRATCH]; | |||||
/* Register/doorbell mmio */ | |||||
resource_size_t rmmio_base; | |||||
resource_size_t rmmio_size; | |||||
void __iomem *rmmio; | |||||
/* protects concurrent MM_INDEX/DATA based register access */ | |||||
spinlock_t mmio_idx_lock; | |||||
struct amdgpu_mmio_remap rmmio_remap; | |||||
/* protects concurrent SMC based register access */ | |||||
spinlock_t smc_idx_lock; | |||||
amdgpu_rreg_t smc_rreg; | |||||
amdgpu_wreg_t smc_wreg; | |||||
/* protects concurrent PCIE register access */ | |||||
spinlock_t pcie_idx_lock; | |||||
amdgpu_rreg_t pcie_rreg; | |||||
amdgpu_wreg_t pcie_wreg; | |||||
amdgpu_rreg_t pciep_rreg; | |||||
amdgpu_wreg_t pciep_wreg; | |||||
amdgpu_rreg64_t pcie_rreg64; | |||||
amdgpu_wreg64_t pcie_wreg64; | |||||
/* protects concurrent UVD register access */ | |||||
spinlock_t uvd_ctx_idx_lock; | |||||
amdgpu_rreg_t uvd_ctx_rreg; | |||||
amdgpu_wreg_t uvd_ctx_wreg; | |||||
/* protects concurrent DIDT register access */ | |||||
spinlock_t didt_idx_lock; | |||||
amdgpu_rreg_t didt_rreg; | |||||
amdgpu_wreg_t didt_wreg; | |||||
/* protects concurrent gc_cac register access */ | |||||
spinlock_t gc_cac_idx_lock; | |||||
amdgpu_rreg_t gc_cac_rreg; | |||||
amdgpu_wreg_t gc_cac_wreg; | |||||
/* protects concurrent se_cac register access */ | |||||
spinlock_t se_cac_idx_lock; | |||||
amdgpu_rreg_t se_cac_rreg; | |||||
amdgpu_wreg_t se_cac_wreg; | |||||
/* protects concurrent ENDPOINT (audio) register access */ | |||||
spinlock_t audio_endpt_idx_lock; | |||||
amdgpu_block_rreg_t audio_endpt_rreg; | |||||
amdgpu_block_wreg_t audio_endpt_wreg; | |||||
void __iomem *rio_mem; | |||||
resource_size_t rio_mem_size; | |||||
#ifdef __FreeBSD__ | |||||
int rio_rid; | |||||
int rio_type; | |||||
struct resource *rio_res; | |||||
#endif | |||||
struct amdgpu_doorbell doorbell; | |||||
/* clock/pll info */ | |||||
struct amdgpu_clock clock; | |||||
/* MC */ | |||||
struct amdgpu_gmc gmc; | |||||
struct amdgpu_gart gart; | |||||
dma_addr_t dummy_page_addr; | |||||
struct amdgpu_vm_manager vm_manager; | |||||
struct amdgpu_vmhub vmhub[AMDGPU_MAX_VMHUBS]; | |||||
unsigned num_vmhubs; | |||||
/* memory management */ | |||||
struct amdgpu_mman mman; | |||||
struct amdgpu_vram_scratch vram_scratch; | |||||
struct amdgpu_wb wb; | |||||
atomic64_t num_bytes_moved; | |||||
atomic64_t num_evictions; | |||||
atomic64_t num_vram_cpu_page_faults; | |||||
atomic_t gpu_reset_counter; | |||||
atomic_t vram_lost_counter; | |||||
/* data for buffer migration throttling */ | |||||
struct { | |||||
spinlock_t lock; | |||||
s64 last_update_us; | |||||
s64 accum_us; /* accumulated microseconds */ | |||||
s64 accum_us_vis; /* for visible VRAM */ | |||||
u32 log2_max_MBps; | |||||
} mm_stats; | |||||
/* display */ | |||||
bool enable_virtual_display; | |||||
struct amdgpu_mode_info mode_info; | |||||
/* For pre-DCE11. DCE11 and later are in "struct amdgpu_device->dm" */ | |||||
struct work_struct hotplug_work; | |||||
struct amdgpu_irq_src crtc_irq; | |||||
struct amdgpu_irq_src vupdate_irq; | |||||
struct amdgpu_irq_src pageflip_irq; | |||||
struct amdgpu_irq_src hpd_irq; | |||||
/* rings */ | |||||
u64 fence_context; | |||||
unsigned num_rings; | |||||
struct amdgpu_ring *rings[AMDGPU_MAX_RINGS]; | |||||
bool ib_pool_ready; | |||||
struct amdgpu_sa_manager ring_tmp_bo; | |||||
/* interrupts */ | |||||
struct amdgpu_irq irq; | |||||
/* powerplay */ | |||||
struct amd_powerplay powerplay; | |||||
bool pp_force_state_enabled; | |||||
/* smu */ | |||||
struct smu_context smu; | |||||
/* dpm */ | |||||
struct amdgpu_pm pm; | |||||
u32 cg_flags; | |||||
u32 pg_flags; | |||||
/* gfx */ | |||||
struct amdgpu_gfx gfx; | |||||
/* sdma */ | |||||
struct amdgpu_sdma sdma; | |||||
/* uvd */ | |||||
struct amdgpu_uvd uvd; | |||||
/* vce */ | |||||
struct amdgpu_vce vce; | |||||
/* vcn */ | |||||
struct amdgpu_vcn vcn; | |||||
/* firmwares */ | |||||
struct amdgpu_firmware firmware; | |||||
/* PSP */ | |||||
struct psp_context psp; | |||||
/* GDS */ | |||||
struct amdgpu_gds gds; | |||||
/* KFD */ | |||||
struct amdgpu_kfd_dev kfd; | |||||
/* UMC */ | |||||
struct amdgpu_umc umc; | |||||
/* display related functionality */ | |||||
struct amdgpu_display_manager dm; | |||||
/* discovery */ | |||||
uint8_t *discovery; | |||||
/* mes */ | |||||
bool enable_mes; | |||||
struct amdgpu_mes mes; | |||||
struct amdgpu_ip_block ip_blocks[AMDGPU_MAX_IP_NUM]; | |||||
int num_ip_blocks; | |||||
struct mutex mn_lock; | |||||
DECLARE_HASHTABLE(mn_hash, 7); | |||||
/* tracking pinned memory */ | |||||
atomic64_t vram_pin_size; | |||||
atomic64_t visible_pin_size; | |||||
atomic64_t gart_pin_size; | |||||
/* soc15 register offset based on ip, instance and segment */ | |||||
uint32_t *reg_offset[MAX_HWIP][HWIP_MAX_INSTANCE]; | |||||
const struct amdgpu_nbio_funcs *nbio_funcs; | |||||
const struct amdgpu_df_funcs *df_funcs; | |||||
const struct amdgpu_mmhub_funcs *mmhub_funcs; | |||||
/* delayed work_func for deferring clockgating during resume */ | |||||
struct delayed_work delayed_init_work; | |||||
struct amdgpu_virt virt; | |||||
/* firmware VRAM reservation */ | |||||
struct amdgpu_fw_vram_usage fw_vram_usage; | |||||
/* link all shadow bo */ | |||||
struct list_head shadow_list; | |||||
struct mutex shadow_list_lock; | |||||
/* keep an lru list of rings by HW IP */ | |||||
struct list_head ring_lru_list; | |||||
spinlock_t ring_lru_list_lock; | |||||
/* record hw reset is performed */ | |||||
bool has_hw_reset; | |||||
u8 reset_magic[AMDGPU_RESET_MAGIC_NUM]; | |||||
/* s3/s4 mask */ | |||||
bool in_suspend; | |||||
/* record last mm index being written through WREG32*/ | |||||
unsigned long last_mm_index; | |||||
bool in_gpu_reset; | |||||
enum pp_mp1_state mp1_state; | |||||
struct mutex lock_reset; | |||||
struct amdgpu_doorbell_index doorbell_index; | |||||
int asic_reset_res; | |||||
struct work_struct xgmi_reset_work; | |||||
bool in_baco_reset; | |||||
long gfx_timeout; | |||||
long sdma_timeout; | |||||
long video_timeout; | |||||
long compute_timeout; | |||||
uint64_t unique_id; | |||||
uint64_t df_perfmon_config_assign_mask[AMDGPU_MAX_DF_PERFMONS]; | |||||
#endif /*__FBSD__*/ | |||||
}; | |||||
/* AMD_ACPI_DESCRIPTION_HEADER copied from <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/include/atombios.h>*/ | |||||
typedef struct { | |||||
ULONG Signature; | |||||
ULONG TableLength; //Length | |||||
UCHAR Revision; | |||||
UCHAR Checksum; | |||||
UCHAR OemId[6]; | |||||
UCHAR OemTableId[8]; //UINT64 OemTableId; | |||||
ULONG OemRevision; | |||||
ULONG CreatorId; | |||||
ULONG CreatorRevision; | |||||
} AMD_ACPI_DESCRIPTION_HEADER; | |||||
/* UEFI_ACPI_VFCT copied from <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/include/atombios.h>*/ | |||||
typedef struct { | |||||
AMD_ACPI_DESCRIPTION_HEADER SHeader; | |||||
UCHAR TableUUID[16]; //0x24 | |||||
ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. | |||||
ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. | |||||
ULONG Reserved[4]; //0x3C | |||||
}UEFI_ACPI_VFCT; | |||||
/* VFCT_IMAGE_HEADER copied from <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/include/atombios.h>*/ | |||||
typedef struct { | |||||
ULONG PCIBus; //0x4C | |||||
ULONG PCIDevice; //0x50 | |||||
ULONG PCIFunction; //0x54 | |||||
USHORT VendorID; //0x58 | |||||
USHORT DeviceID; //0x5A | |||||
USHORT SSVID; //0x5C | |||||
USHORT SSID; //0x5E | |||||
ULONG Revision; //0x60 | |||||
ULONG ImageLength; //0x64 | |||||
}VFCT_IMAGE_HEADER; | |||||
/* GOP_VBIOS_CONTENT copied from <https://github.com/freebsd/drm-kmod/blob/750746f938420b1351a39fe864e7d41ae5ae7648/drivers/gpu/drm/amd/include/atombios.h>*/ | |||||
typedef struct { | |||||
VFCT_IMAGE_HEADER VbiosHeader; | |||||
UCHAR VbiosContent[1]; | |||||
}GOP_VBIOS_CONTENT; | |||||
#endif /*__FBSD__*/ | |||||
/* | |||||
* Copyright 2008 Advanced Micro Devices, Inc. | |||||
* Copyright 2008 Red Hat Inc. | |||||
* Copyright 2009 Jerome Glisse. | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a | |||||
* copy of this software and associated documentation files (the "Software"), | |||||
* to deal in the Software without restriction, including without limitation | |||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
* and/or sell copies of the Software, and to permit persons to whom the | |||||
* Software is furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice shall be included in | |||||
* all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||||
* OTHER DEALINGS IN THE SOFTWARE. | |||||
* | |||||
* Authors: Dave Airlie | |||||
* Alex Deucher | |||||
* Jerome Glisse | |||||
*/ | |||||
#ifndef __FBSD__ | |||||
#include "amdgpu.h" | |||||
#include "atom.h" | |||||
#include <linux/pci.h> | |||||
#include <linux/slab.h> | |||||
#include <linux/acpi.h> | |||||
#endif /*__FBSD__*/ | |||||
/* | |||||
* BIOS. | |||||
*/ | |||||
#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) | |||||
/* 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) { | |||||
DRM_INFO("vbios mem is null or mem size is wrong\n"); | |||||
return false; | |||||
} | |||||
if (!AMD_IS_VALID_VBIOS(bios)) { | |||||
DRM_INFO("BIOS signature incorrect %x %x\n", bios[0], bios[1]); | |||||
return false; | |||||
} | |||||
bios_header_start = bios[0x48] | (bios[0x49] << 8); | |||||
if (!bios_header_start) { | |||||
DRM_INFO("Can't locate bios header\n"); | |||||
return false; | |||||
} | |||||
tmp = bios_header_start + 4; | |||||
if (size < tmp) { | |||||
DRM_INFO("BIOS header is broken\n"); | |||||
return false; | |||||
} | |||||
if (!memcmp(bios + tmp, "ATOM", 4) || | |||||
!memcmp(bios + tmp, "MOTA", 4)) { | |||||
DRM_DEBUG("ATOMBIOS detected\n"); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
#ifndef __FBSD__ | |||||
/* If you boot an IGP board with a discrete card as the primary, | |||||
* the IGP rom is not accessible via the rom bar as the IGP rom is | |||||
* part of the system bios. On boot, the system bios puts a | |||||
* copy of the igp rom at the start of vram if a discrete card is | |||||
* present. | |||||
*/ | |||||
static bool igp_read_bios_from_vram(struct amdgpu_device *adev) | |||||
{ | |||||
uint8_t __iomem *bios; | |||||
resource_size_t vram_base; | |||||
resource_size_t size = 256 * 1024; /* ??? */ | |||||
if (!(adev->flags & AMD_IS_APU)) | |||||
if (amdgpu_device_need_post(adev)) | |||||
return false; | |||||
adev->bios = NULL; | |||||
vram_base = pci_resource_start(adev->pdev, 0); | |||||
bios = ioremap_wc(vram_base, size); | |||||
if (!bios) { | |||||
return false; | |||||
} | |||||
adev->bios = kmalloc(size, GFP_KERNEL); | |||||
if (!adev->bios) { | |||||
iounmap(bios); | |||||
return false; | |||||
} | |||||
adev->bios_size = size; | |||||
memcpy_fromio(adev->bios, bios, size); | |||||
iounmap(bios); | |||||
if (!check_atom_bios(adev->bios, size)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
#else /*__FBSD__*/ | |||||
static bool igp_read_bios_from_vram(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif /*__FBSD__*/ | |||||
#ifndef __FBSD__ | |||||
#ifdef __FreeBSD__ | |||||
#define pci_map_rom(pdev, sizep) \ | |||||
vga_pci_map_bios(device_get_parent(pdev->dev.bsddev), sizep) | |||||
#define pci_unmap_rom(pdev, bios) \ | |||||
vga_pci_unmap_bios(device_get_parent(pdev->dev.bsddev), bios) | |||||
#endif | |||||
#else /*__FBSD__*/ | |||||
#define pci_map_rom(pdev, sizep) \ | |||||
vga_pci_map_bios(pdev->dev.bsddev, sizep) | |||||
#define pci_unmap_rom(pdev, bios) \ | |||||
vga_pci_unmap_bios(pdev->dev.bsddev, bios) | |||||
#endif /*__FBSD__*/ | |||||
#ifdef __FBSD__ | |||||
static | |||||
#endif | |||||
bool amdgpu_read_bios(struct amdgpu_device *adev) | |||||
{ | |||||
uint8_t __iomem *bios; | |||||
size_t size; | |||||
adev->bios = NULL; | |||||
/* XXX: some cards may return 0 for rom size? ddx has a workaround */ | |||||
bios = pci_map_rom(adev->pdev, &size); | |||||
if (!bios) { | |||||
return false; | |||||
} | |||||
adev->bios = kzalloc(size, GFP_KERNEL); | |||||
if (adev->bios == NULL) { | |||||
pci_unmap_rom(adev->pdev, bios); | |||||
return false; | |||||
} | |||||
adev->bios_size = size; | |||||
memcpy_fromio(adev->bios, bios, size); | |||||
pci_unmap_rom(adev->pdev, bios); | |||||
if (!check_atom_bios(adev->bios, size)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
#ifndef __FBSD__ | |||||
static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) | |||||
{ | |||||
u8 header[AMD_VBIOS_SIGNATURE_END+1] = {0}; | |||||
int len; | |||||
if (!adev->asic_funcs->read_bios_from_rom) | |||||
return false; | |||||
/* validate VBIOS signature */ | |||||
if (amdgpu_asic_read_bios_from_rom(adev, &header[0], sizeof(header)) == false) | |||||
return false; | |||||
header[AMD_VBIOS_SIGNATURE_END] = 0; | |||||
if ((!AMD_IS_VALID_VBIOS(header)) || | |||||
0 != memcmp((char *)&header[AMD_VBIOS_SIGNATURE_OFFSET], | |||||
AMD_VBIOS_SIGNATURE, | |||||
strlen(AMD_VBIOS_SIGNATURE))) | |||||
return false; | |||||
/* valid vbios, go on */ | |||||
len = AMD_VBIOS_LENGTH(header); | |||||
len = ALIGN(len, 4); | |||||
adev->bios = kmalloc(len, GFP_KERNEL); | |||||
if (!adev->bios) { | |||||
DRM_ERROR("no memory to allocate for BIOS\n"); | |||||
return false; | |||||
} | |||||
adev->bios_size = len; | |||||
/* read complete BIOS */ | |||||
amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); | |||||
if (!check_atom_bios(adev->bios, len)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
#else /*__FBSD__*/ | |||||
static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif | |||||
#ifndef __FBSD__ | |||||
static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) | |||||
{ | |||||
uint8_t __iomem *bios; | |||||
size_t size; | |||||
adev->bios = NULL; | |||||
bios = pci_platform_rom(adev->pdev, &size); | |||||
if (!bios) { | |||||
return false; | |||||
} | |||||
adev->bios = kzalloc(size, GFP_KERNEL); | |||||
if (adev->bios == NULL) | |||||
return false; | |||||
memcpy_fromio(adev->bios, bios, size); | |||||
if (!check_atom_bios(adev->bios, size)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
adev->bios_size = size; | |||||
return true; | |||||
} | |||||
#else /*__FBSD__*/ | |||||
static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif /*__FBSD__*/ | |||||
#ifdef CONFIG_ACPI | |||||
#ifndef __FBSD__ | |||||
/* ATRM is used to get the BIOS on the discrete cards in | |||||
* dual-gpu systems. | |||||
*/ | |||||
/* retrieve the ROM in 4k blocks */ | |||||
#define ATRM_BIOS_PAGE 4096 | |||||
/** | |||||
* amdgpu_atrm_call - fetch a chunk of the vbios | |||||
* | |||||
* @atrm_handle: acpi ATRM handle | |||||
* @bios: vbios image pointer | |||||
* @offset: offset of vbios image data to fetch | |||||
* @len: length of vbios image data to fetch | |||||
* | |||||
* Executes ATRM to fetch a chunk of the discrete | |||||
* vbios image on PX systems (all asics). | |||||
* Returns the length of the buffer fetched. | |||||
*/ | |||||
static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, | |||||
int offset, int len) | |||||
{ | |||||
acpi_status status; | |||||
union acpi_object atrm_arg_elements[2], *obj; | |||||
struct acpi_object_list atrm_arg; | |||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; | |||||
atrm_arg.Count = 2; | |||||
atrm_arg.Pointer = &atrm_arg_elements[0]; | |||||
atrm_arg_elements[0].Type = ACPI_TYPE_INTEGER; | |||||
atrm_arg_elements[0].Integer.Value = offset; | |||||
atrm_arg_elements[1].Type = ACPI_TYPE_INTEGER; | |||||
atrm_arg_elements[1].Integer.Value = len; | |||||
status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); | |||||
if (ACPI_FAILURE(status)) { | |||||
printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); | |||||
return -ENODEV; | |||||
} | |||||
obj = (union acpi_object *)buffer.Pointer; | |||||
memcpy(bios+offset, obj->Buffer.Pointer, obj->Buffer.Length); | |||||
len = obj->Buffer.Length; | |||||
kfree(buffer.Pointer); | |||||
return len; | |||||
} | |||||
static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) | |||||
{ | |||||
int ret; | |||||
int size = 256 * 1024; | |||||
int i; | |||||
struct pci_dev *pdev = NULL; | |||||
acpi_handle dhandle, atrm_handle; | |||||
acpi_status status; | |||||
bool found = false; | |||||
/* ATRM is for the discrete card only */ | |||||
if (adev->flags & AMD_IS_APU) | |||||
return false; | |||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | |||||
dhandle = ACPI_HANDLE(&pdev->dev); | |||||
if (!dhandle) | |||||
continue; | |||||
status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); | |||||
if (!ACPI_FAILURE(status)) { | |||||
found = true; | |||||
break; | |||||
} | |||||
} | |||||
if (!found) { | |||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) { | |||||
dhandle = ACPI_HANDLE(&pdev->dev); | |||||
if (!dhandle) | |||||
continue; | |||||
status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); | |||||
if (!ACPI_FAILURE(status)) { | |||||
found = true; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (!found) | |||||
return false; | |||||
adev->bios = kmalloc(size, GFP_KERNEL); | |||||
if (!adev->bios) { | |||||
DRM_ERROR("Unable to allocate bios\n"); | |||||
return false; | |||||
} | |||||
for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { | |||||
ret = amdgpu_atrm_call(atrm_handle, | |||||
adev->bios, | |||||
(i * ATRM_BIOS_PAGE), | |||||
ATRM_BIOS_PAGE); | |||||
if (ret < ATRM_BIOS_PAGE) | |||||
break; | |||||
} | |||||
if (!check_atom_bios(adev->bios, size)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
adev->bios_size = size; | |||||
return true; | |||||
} | |||||
#else /*__FBSD__*/ | |||||
static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif /*__FBSD__*/ | |||||
#else | |||||
static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif | |||||
#ifndef __FBSD__ | |||||
static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) | |||||
{ | |||||
if (adev->flags & AMD_IS_APU) | |||||
return igp_read_bios_from_vram(adev); | |||||
else | |||||
return amdgpu_asic_read_disabled_bios(adev); | |||||
} | |||||
#else /*__FBSD__*/ | |||||
static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif /*__FBSD__*/ | |||||
#ifdef CONFIG_ACPI | |||||
static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) | |||||
{ | |||||
struct acpi_table_header *hdr; | |||||
acpi_size tbl_size; | |||||
UEFI_ACPI_VFCT *vfct; | |||||
unsigned offset; | |||||
if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) | |||||
return false; | |||||
#ifdef __FreeBSD__ | |||||
tbl_size = hdr->Length; | |||||
#else | |||||
tbl_size = hdr->length; | |||||
#endif | |||||
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { | |||||
DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); | |||||
return false; | |||||
} | |||||
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) { | |||||
DRM_ERROR("ACPI VFCT image header truncated\n"); | |||||
return false; | |||||
} | |||||
offset += vhdr->ImageLength; | |||||
if (offset > tbl_size) { | |||||
DRM_ERROR("ACPI VFCT image truncated\n"); | |||||
return false; | |||||
} | |||||
if (vhdr->ImageLength && | |||||
#ifndef __FBSD__ | |||||
vhdr->PCIBus == adev->pdev->bus->number && | |||||
#else | |||||
vhdr->PCIBus == PCI_BUS_NUM(adev->pdev->devfn) && | |||||
#endif | |||||
vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && | |||||
vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && | |||||
vhdr->VendorID == adev->pdev->vendor && | |||||
vhdr->DeviceID == adev->pdev->device) { | |||||
adev->bios = kmemdup(&vbios->VbiosContent, | |||||
vhdr->ImageLength, | |||||
GFP_KERNEL); | |||||
if (!check_atom_bios(adev->bios, vhdr->ImageLength)) { | |||||
kfree(adev->bios); | |||||
return false; | |||||
} | |||||
adev->bios_size = vhdr->ImageLength; | |||||
return true; | |||||
} | |||||
} | |||||
DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); | |||||
return false; | |||||
} | |||||
#else | |||||
static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) | |||||
{ | |||||
return false; | |||||
} | |||||
#endif | |||||
#ifdef __FBSD__ | |||||
static | |||||
#endif | |||||
bool amdgpu_get_bios(struct amdgpu_device *adev) | |||||
{ | |||||
if (amdgpu_atrm_get_bios(adev)) | |||||
goto success; | |||||
if (amdgpu_acpi_vfct_bios(adev)) | |||||
goto success; | |||||
if (igp_read_bios_from_vram(adev)) | |||||
goto success; | |||||
if (amdgpu_read_bios(adev)) | |||||
goto success; | |||||
if (amdgpu_read_bios_from_rom(adev)) | |||||
goto success; | |||||
if (amdgpu_read_disabled_bios(adev)) | |||||
goto success; | |||||
if (amdgpu_read_platform_bios(adev)) | |||||
goto success; | |||||
DRM_ERROR("Unable to locate a BIOS ROM\n"); | |||||
return false; | |||||
success: | |||||
#ifndef __FBSD__ | |||||
adev->is_atom_fw = (adev->asic_type >= CHIP_VEGA10) ? true : false; | |||||
#endif | |||||
return true; | |||||
} | |||||
#ifdef __FBSD__ | |||||
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; | |||||
error = 0; | |||||
struct pci_dev pdev; | |||||
struct amdgpu_device adev; | |||||
adev.pdev = &pdev; | |||||
pdev.dev.bsddev = pci_find_bsf(bus, slot, func); | |||||
pdev.devfn = PCI_DEVFN(bus, slot, func); | |||||
pdev.vendor = vendor; | |||||
pdev.device = dev_id; | |||||
if (!amdgpu_get_bios(&adev)) | |||||
return ENOENT; | |||||
if (bios) { | |||||
*size = min(adev.bios_size, *size); | |||||
error = copyout(adev.bios, bios, *size); | |||||
} else if (size) { | |||||
*size = adev.bios_size; | |||||
} | |||||
kfree(adev.bios); | |||||
return (error); | |||||
} | |||||
#endif /*__FBSD__*/ |