Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe78118c authored by Alex Deucher's avatar Alex Deucher
Browse files

drm/radeon: protect concurrent smc register access with a spinlock



smc registers are access indirectly via the main mmio aperture, so
there may be problems with concurrent access.  This adds a spinlock
to protect access to this register space.

Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 136de91e
Loading
Loading
Loading
Loading
+26 −13
Original line number Original line Diff line number Diff line
@@ -47,10 +47,11 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
			 u32 smc_start_address,
			 u32 smc_start_address,
			 const u8 *src, u32 byte_count, u32 limit)
			 const u8 *src, u32 byte_count, u32 limit)
{
{
	unsigned long flags;
	u32 data, original_data;
	u32 data, original_data;
	u32 addr;
	u32 addr;
	u32 extra_shift;
	u32 extra_shift;
	int ret;
	int ret = 0;


	if (smc_start_address & 3)
	if (smc_start_address & 3)
		return -EINVAL;
		return -EINVAL;
@@ -59,13 +60,14 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,


	addr = smc_start_address;
	addr = smc_start_address;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	while (byte_count >= 4) {
	while (byte_count >= 4) {
		/* SMC address space is BE */
		/* SMC address space is BE */
		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];


		ret = ci_set_smc_sram_address(rdev, addr, limit);
		ret = ci_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		WREG32(SMC_IND_DATA_0, data);
		WREG32(SMC_IND_DATA_0, data);


@@ -80,7 +82,7 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,


		ret = ci_set_smc_sram_address(rdev, addr, limit);
		ret = ci_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		original_data = RREG32(SMC_IND_DATA_0);
		original_data = RREG32(SMC_IND_DATA_0);


@@ -97,11 +99,15 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,


		ret = ci_set_smc_sram_address(rdev, addr, limit);
		ret = ci_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		WREG32(SMC_IND_DATA_0, data);
		WREG32(SMC_IND_DATA_0, data);
	}
	}
	return 0;

done:
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);

	return ret;
}
}


void ci_start_smc(struct radeon_device *rdev)
void ci_start_smc(struct radeon_device *rdev)
@@ -197,6 +203,7 @@ PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)


int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
{
{
	unsigned long flags;
	u32 ucode_start_address;
	u32 ucode_start_address;
	u32 ucode_size;
	u32 ucode_size;
	const u8 *src;
	const u8 *src;
@@ -219,6 +226,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
		return -EINVAL;
		return -EINVAL;


	src = (const u8 *)rdev->smc_fw->data;
	src = (const u8 *)rdev->smc_fw->data;
	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	WREG32(SMC_IND_INDEX_0, ucode_start_address);
	WREG32(SMC_IND_INDEX_0, ucode_start_address);
	WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
	WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
	while (ucode_size >= 4) {
	while (ucode_size >= 4) {
@@ -231,6 +239,7 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
		ucode_size -= 4;
		ucode_size -= 4;
	}
	}
	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);


	return 0;
	return 0;
}
}
@@ -238,25 +247,29 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
int ci_read_smc_sram_dword(struct radeon_device *rdev,
int ci_read_smc_sram_dword(struct radeon_device *rdev,
			   u32 smc_address, u32 *value, u32 limit)
			   u32 smc_address, u32 *value, u32 limit)
{
{
	unsigned long flags;
	int ret;
	int ret;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
	if (ret)
	if (ret == 0)
		return ret;

		*value = RREG32(SMC_IND_DATA_0);
		*value = RREG32(SMC_IND_DATA_0);
	return 0;
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);

	return ret;
}
}


int ci_write_smc_sram_dword(struct radeon_device *rdev,
int ci_write_smc_sram_dword(struct radeon_device *rdev,
			    u32 smc_address, u32 value, u32 limit)
			    u32 smc_address, u32 value, u32 limit)
{
{
	unsigned long flags;
	int ret;
	int ret;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
	if (ret)
	if (ret == 0)
		return ret;

		WREG32(SMC_IND_DATA_0, value);
		WREG32(SMC_IND_DATA_0, value);
	return 0;
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);

	return ret;
}
}
+9 −0
Original line number Original line Diff line number Diff line
@@ -2110,6 +2110,8 @@ struct radeon_device {
	resource_size_t			rmmio_size;
	resource_size_t			rmmio_size;
	/* protects concurrent MM_INDEX/DATA based register access */
	/* protects concurrent MM_INDEX/DATA based register access */
	spinlock_t mmio_idx_lock;
	spinlock_t mmio_idx_lock;
	/* protects concurrent SMC based register access */
	spinlock_t smc_idx_lock;
	void __iomem			*rmmio;
	void __iomem			*rmmio;
	radeon_rreg_t			mc_rreg;
	radeon_rreg_t			mc_rreg;
	radeon_wreg_t			mc_wreg;
	radeon_wreg_t			mc_wreg;
@@ -2292,17 +2294,24 @@ static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uin


static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg)
static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg)
{
{
	unsigned long flags;
	u32 r;
	u32 r;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	WREG32(TN_SMC_IND_INDEX_0, (reg));
	WREG32(TN_SMC_IND_INDEX_0, (reg));
	r = RREG32(TN_SMC_IND_DATA_0);
	r = RREG32(TN_SMC_IND_DATA_0);
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
	return r;
	return r;
}
}


static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v)
static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v)
{
{
	unsigned long flags;

	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	WREG32(TN_SMC_IND_INDEX_0, (reg));
	WREG32(TN_SMC_IND_INDEX_0, (reg));
	WREG32(TN_SMC_IND_DATA_0, (v));
	WREG32(TN_SMC_IND_DATA_0, (v));
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
}
}


static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg)
static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg)
+1 −0
Original line number Original line Diff line number Diff line
@@ -1249,6 +1249,7 @@ int radeon_device_init(struct radeon_device *rdev,
	/* Registers mapping */
	/* Registers mapping */
	/* TODO: block userspace mapping of io register */
	/* TODO: block userspace mapping of io register */
	spin_lock_init(&rdev->mmio_idx_lock);
	spin_lock_init(&rdev->mmio_idx_lock);
	spin_lock_init(&rdev->smc_idx_lock);
	if (rdev->family >= CHIP_BONAIRE) {
	if (rdev->family >= CHIP_BONAIRE) {
		rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
		rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
		rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
		rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
+27 −17
Original line number Original line Diff line number Diff line
@@ -274,7 +274,7 @@ static const u8 cayman_smc_int_vectors[] =
	0x08, 0x72, 0x08, 0x72
	0x08, 0x72, 0x08, 0x72
};
};


int rv770_set_smc_sram_address(struct radeon_device *rdev,
static int rv770_set_smc_sram_address(struct radeon_device *rdev,
				      u16 smc_address, u16 limit)
				      u16 smc_address, u16 limit)
{
{
	u32 addr;
	u32 addr;
@@ -296,9 +296,10 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
			    u16 smc_start_address, const u8 *src,
			    u16 smc_start_address, const u8 *src,
			    u16 byte_count, u16 limit)
			    u16 byte_count, u16 limit)
{
{
	unsigned long flags;
	u32 data, original_data, extra_shift;
	u32 data, original_data, extra_shift;
	u16 addr;
	u16 addr;
	int ret;
	int ret = 0;


	if (smc_start_address & 3)
	if (smc_start_address & 3)
		return -EINVAL;
		return -EINVAL;
@@ -307,13 +308,14 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,


	addr = smc_start_address;
	addr = smc_start_address;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	while (byte_count >= 4) {
	while (byte_count >= 4) {
		/* SMC address space is BE */
		/* SMC address space is BE */
		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];


		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		WREG32(SMC_SRAM_DATA, data);
		WREG32(SMC_SRAM_DATA, data);


@@ -328,7 +330,7 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,


		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		original_data = RREG32(SMC_SRAM_DATA);
		original_data = RREG32(SMC_SRAM_DATA);


@@ -346,12 +348,15 @@ int rv770_copy_bytes_to_smc(struct radeon_device *rdev,


		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		ret = rv770_set_smc_sram_address(rdev, addr, limit);
		if (ret)
		if (ret)
			return ret;
			goto done;


		WREG32(SMC_SRAM_DATA, data);
		WREG32(SMC_SRAM_DATA, data);
	}
	}


	return 0;
done:
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);

	return ret;
}
}


static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
@@ -461,12 +466,15 @@ PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)


static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
{
{
	unsigned long flags;
	u16 i;
	u16 i;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	for (i = 0;  i < limit; i += 4) {
	for (i = 0;  i < limit; i += 4) {
		rv770_set_smc_sram_address(rdev, i, limit);
		rv770_set_smc_sram_address(rdev, i, limit);
		WREG32(SMC_SRAM_DATA, 0);
		WREG32(SMC_SRAM_DATA, 0);
	}
	}
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
}
}


int rv770_load_smc_ucode(struct radeon_device *rdev,
int rv770_load_smc_ucode(struct radeon_device *rdev,
@@ -595,27 +603,29 @@ int rv770_load_smc_ucode(struct radeon_device *rdev,
int rv770_read_smc_sram_dword(struct radeon_device *rdev,
int rv770_read_smc_sram_dword(struct radeon_device *rdev,
			      u16 smc_address, u32 *value, u16 limit)
			      u16 smc_address, u32 *value, u16 limit)
{
{
	unsigned long flags;
	int ret;
	int ret;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
	if (ret)
	if (ret == 0)
		return ret;

		*value = RREG32(SMC_SRAM_DATA);
		*value = RREG32(SMC_SRAM_DATA);
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);


	return 0;
	return ret;
}
}


int rv770_write_smc_sram_dword(struct radeon_device *rdev,
int rv770_write_smc_sram_dword(struct radeon_device *rdev,
			       u16 smc_address, u32 value, u16 limit)
			       u16 smc_address, u32 value, u16 limit)
{
{
	unsigned long flags;
	int ret;
	int ret;


	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
	if (ret)
	if (ret == 0)
		return ret;

		WREG32(SMC_SRAM_DATA, value);
		WREG32(SMC_SRAM_DATA, value);
	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);


	return 0;
	return ret;
}
}
+0 −2
Original line number Original line Diff line number Diff line
@@ -187,8 +187,6 @@ typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE;
#define RV770_SMC_SOFT_REGISTER_uvd_enabled             0x9C
#define RV770_SMC_SOFT_REGISTER_uvd_enabled             0x9C
#define RV770_SMC_SOFT_REGISTER_is_asic_lombok          0xA0
#define RV770_SMC_SOFT_REGISTER_is_asic_lombok          0xA0


int rv770_set_smc_sram_address(struct radeon_device *rdev,
			       u16 smc_address, u16 limit);
int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
			    u16 smc_start_address, const u8 *src,
			    u16 smc_start_address, const u8 *src,
			    u16 byte_count, u16 limit);
			    u16 byte_count, u16 limit);
Loading